Open SyMind opened 3 years ago
相关的 RFC:https://github.com/reactjs/rfcs/blob/createlement-rfc/text/0000-create-element-changes.md#detailed-design
React.createElement(...) 有以下几个问题
.defaultprops
React.lazy
.defaultProps
React.createElement
key
ref
<div {...props} />
React
除了性能,这也是为了简化学习使用 React 时不得不掌握的大量概念。特别是,forwardRef 和 defaultProps 将不再是什么特别的东西。
forwardRef
defaultProps
理想情况下,您不需要编写任何导入来使用 JSX:
function Foo() { return <div />; }
然后,它将被编译来包含这个依赖项。
import {jsx} from "react"; function Foo() { return jsx('div', ...); }
jsx('div', props, key)
在 createElement 中,children 以可变参数进行传递。在新的转换中,我们总是将它们添加到 props 对象中。
createElement
将它们以可变参数进行传递的原因是为了在 DEV 中区分静态子变量和动态子变量。我们可以传递一个布尔值或者使用两个不同的函数来区分它们。我的建议是将 <div>{a}{b}</div> 编译为 jsxs('div', {children: [a, b]}) ,将 <div>{a}</div> 编译为 jsx('div', {children:a})。 jsxs 函数指明顶部数组由 React 创建。此策略的优点是,即使你没有针对 PROD 和 DEV 进行单独构建,我们仍然可以发出关键警告,并且在 PROD 下也不会产生任何开销。
<div>{a}{b}</div>
jsxs('div', {children: [a, b]})
<div>{a}</div>
jsx('div', {children:a})
jsxs
const Foo = (props) => { return { onClick() { //... } render() { return <div onClick={this.onClick.bind(this)} />; } } };
它的存在会导致一些实现的复杂性。
这是很直接的升级。这是一种非常不常用的模式,大多数人都不知道。关键是你的类构造函数需要具有 Component.prototype.isReactComponent 属性并可以被 new 调用(即不是箭头函数)。即使你碰巧使用了模块模式,在原型中添加 isReactComponent 属性,并使用函数表达式而不是箭头函数。
Component.prototype.isReactComponent
isReactComponent
function Foo(props) { return { onClick() { //... } render() { return <div onClick={this.onClick.bind(this)} />; } } }; Foo.prototype = {isReactComponent: true};
这里的重要目的是,如果我们要在类和函数组件之间引入不同的语义,我们需要在调用它们之前就要知道我们要使用哪个语义。
defaultProps 在类中非常有用,因为 props 对象被传递给许多不同的方法,如生命周期、回调等。每一个都有自己的作用域。这导致使用 JS 默认参数变得困难,因为您必须在每个函数中反复确定相同的默认值。
class Foo { static defaultProps = {foo: 1}; componentDidMount() { let foo = this.props.foo; console.log(foo); } componentDidUpdate() { let foo = this.props.foo; console.log(foo); } componentWillUnmount() { let foo = this.props.foo; console.log(foo); } handleClick = () => { let foo = this.props.foo; console.log(foo); } render() { let foo = this.props.foo; console.log(foo); return <div onClick={this.handleClick} />; } }
但是,在函数组件中,实际上不需要这种模式,因为您可以只使用 JS 默认参数,并且通常这些值的所有使用的位置都在同一作用域内。
function Foo({foo = 1}) { useEffect(() => { console.log(foo); return () => { console.log(foo); }; }); let handleClick = () => { console.log(foo); }; console.log(foo); return <div onClick={handleClick} />; }
当在没有 .prototype.isReactComponent 的组件上使用 defaultProps 时,createElement 将发出警告。这包括那些特殊的组件,如 forwardRef 和 memo。
.prototype.isReactComponent
memo
如果 props 整体进行传递,那么升级将变得困难,不过你总是可以在需要时对它进行重构。
function Foo({foo = 1, bar = "hello"}) { let props = {foo, bar}; //... }
动机
React.createElement(...) 有以下几个问题
.defaultprops
,我们需要对它进行动态检测。这不能被很好的优化,因为被调用的函数是高度 megamorphic 的(应该是 V8 中的术语,待查找)。React.lazy
中,构造元素时.defaultProps
不起作用。因此在这种情况下,我们必须在渲染阶段检查 defaultProps 是否被解析,这意味着无论如何都无法保持语义的一致。React.createElement
,它是动态的属性查找,而不是一个模块作用域内的常量。这有些不足,有些运行成本。key
和ref
从 JSX 提供的 props 中获取,所以即使我们不克隆,我们也必须将其移除,这将导致该对象变成 map-like。key
和ref
可以通过扩展运算符动态传递,因此除非进行昂贵的分析,否则我们无法得知此类模式是否会包含它们<div {...props} />
。React
在 JSX 的作用域内。也就是说,你必须进行默认导入。这很不幸,因为像 hooks 这样的东西通常以命名参数的形式导入。理想情况下,您应该不需要导入任何东西就可以使用 JSX。除了性能,这也是为了简化学习使用 React 时不得不掌握的大量概念。特别是,
forwardRef
和defaultProps
将不再是什么特别的东西。设计细节
JSX 转换变更
Auto-import
理想情况下,您不需要编写任何导入来使用 JSX:
然后,它将被编译来包含这个依赖项。
将 key 从 props 中分开传递
总是将 children 作为属性传递
在
createElement
中,children 以可变参数进行传递。在新的转换中,我们总是将它们添加到 props 对象中。将它们以可变参数进行传递的原因是为了在 DEV 中区分静态子变量和动态子变量。我们可以传递一个布尔值或者使用两个不同的函数来区分它们。我的建议是将
<div>{a}{b}</div>
编译为jsxs('div', {children: [a, b]})
,将<div>{a}</div>
编译为jsx('div', {children:a})
。jsxs
函数指明顶部数组由 React 创建。此策略的优点是,即使你没有针对 PROD 和 DEV 进行单独构建,我们仍然可以发出关键警告,并且在 PROD 下也不会产生任何开销。废除『模块模式(module pattern)』组件
它的存在会导致一些实现的复杂性。
这是很直接的升级。这是一种非常不常用的模式,大多数人都不知道。关键是你的类构造函数需要具有
Component.prototype.isReactComponent
属性并可以被 new 调用(即不是箭头函数)。即使你碰巧使用了模块模式,在原型中添加isReactComponent
属性,并使用函数表达式而不是箭头函数。这里的重要目的是,如果我们要在类和函数组件之间引入不同的语义,我们需要在调用它们之前就要知道我们要使用哪个语义。
废除函数组件的
defaultProps
defaultProps 在类中非常有用,因为 props 对象被传递给许多不同的方法,如生命周期、回调等。每一个都有自己的作用域。这导致使用 JS 默认参数变得困难,因为您必须在每个函数中反复确定相同的默认值。
但是,在函数组件中,实际上不需要这种模式,因为您可以只使用 JS 默认参数,并且通常这些值的所有使用的位置都在同一作用域内。
当在没有
.prototype.isReactComponent
的组件上使用defaultProps
时,createElement 将发出警告。这包括那些特殊的组件,如forwardRef
和memo
。如果 props 整体进行传递,那么升级将变得困难,不过你总是可以在需要时对它进行重构。