You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
if(isStaticChildren){if(isArray(children)){for(leti=0;i<children.length;i++){validateChildKeys(children[i],type);}if(Object.freeze){Object.freeze(children);}}else{console.error('React.jsx: Static children should always be an array. '+'You are likely explicitly calling React.jsxs or React.jsxDEV. '+'Use the Babel transform instead.',);}}else{validateChildKeys(children,type);}
这段代码意图很清晰:如果 children 是静态数组,对 children 中的每个元素进行 key 的校验;如果 children 不是数组,或者是动态数组,那么对整个 children 做校验。validateChildKeys 函数会判断传入的 children/child 是否是数组,如果是数组则会校验数组中的每个元素是否有合法的 key 属性。
开始
React v17 增加了 jsx-runtime 机制,摒弃了过去通过
React.CreateElement
创建 React element 的方式。以<h1>Poem</h1>
为例,在 React v16 及其之前,上面的 JSX 会被转译成:而在 React v17 之后,通过正确的 transpiler 配置(以 tsc 为例,
compilerOptions.jsx
的值需要是ReactJSX
),会被转译成:乍一看似乎只是换了个函数,但是有几点需要引起注意。
首先是两个函数的签名。
React.createElement
接收的参数为(type, props, ...children)
,_jsx
接收的参数为(type, config, maybeKey)
。新的_jsx
函数在创建 React element 的时候,把children
属性放进了props
(config
) 里,而把key
独立了出来。第二个变化是,原先
createElement
是 React 的一个方法,因此在写 JSX 的时候,我们通常还需要通过import React from 'react'
在当前模块中引入 React。而在 React v17 之后,配合正确的 transpiler 配置,不再需要显示引入 React,transpiler 会自动根据配置引入对应的jsx
函数。由
React.createElement
改成react/jsx-runtime
带来的好处主要有两个:_jsx
比CreateElement
更简短而且更可压缩替换,打包完成之后 JavaScript bundle 的体积将会稍微减少一些。_jsx 与 _jsxs
再来看一个在项目中非常常用的、动态创建 React element 的例子:
上面的代码会被转译成:
不仔细看的话可能会忽略
div.outer
元素是通过_jsxs
而不是_jsx
创建的。_jsxs
和_jsx
的区别在于,前者应该只对children
为“静态”数组的元素调用,后者则是对元素的children
属性为非数组或者只为“动态”数组时调用。这里所谓的静态与动态,指的是子元素是否会在每次渲染时,存在动态排序、增删的情况。很明显,
div.outer
具有两个在源码中就有固定顺序的子元素。而div.inner
的子元素则是通过表达式动态生成的,每次App
组件渲染时,div.inner
的子元素与上次相比可能交换过了顺序,或者删除、增加了某些子元素。校验 key
我们都知道 React 要求开发者为每个动态生成的子元素手动增加一个
key
属性,相当于给这些子元素赋予一个固定的 ID,以便能够让 React 能够在同级元素中检测到哪些元素被移位、删除和新增。之所以上面的代码里会需要区别静态与动态子元素,并使用两个不同的方法来生成元素,就是为了校验子元素的key
属性。下面简单分析下具体的代码细节。两个函数内部都只调用了
jsxWithValidation
函数,区别在于传入的参数。先看看jsxWithValidation
函数能接收的参数:(type, props, key, isStaticChildren, source, self)
。_jsxs
和_jsx
区别只在于传入的isStaticChildren
的值,前者是true
,后者是false
。jsxWithValidation
中只在一个地方使用了isStaticChildren
:这段代码意图很清晰:如果
children
是静态数组,对children
中的每个元素进行key
的校验;如果children
不是数组,或者是动态数组,那么对整个children
做校验。validateChildKeys
函数会判断传入的children
/child
是否是数组,如果是数组则会校验数组中的每个元素是否有合法的key
属性。React Element
_jsxs
和_jsx
函数其实更多的只是在开发环境中做一些校验,真正重要的是它的返回值。前面我提到过几次 React element,这个概念似乎有点抽象又有点跟其他概念混淆。简单来讲的话,React element 就是 React component 的调用返回值。以上面的代码为例,
App
是 React component,a
是 React element。_jsxs
和_jsx
除了校验一些参数之外,还调用了一个关键函数jsx
(开发环境的话是jsxDEV
。没错,jsxDEV
跟jsx
的主要区别也是会做更多的校验),而jsx
的返回值就是 React element。每个 React element 都只是一个带有几个特殊属性的字面量对象而已:
下面这张图里表示的是
<button onClick={addCount}>Add</button>
对应的 React element 对象:总结
_jsx
和_jsxs
的区别在于能够校验动态生成的children
的key
属性,在生产环境版本的 React 中,两者指向同一个函数。defaultProps
是在jsx
函数中 merge 到 props 中的:Quiz
判断下面 console.log 语句输出内容的顺序
Answer
顺序为: 1 3 2
其实只要能想象到 JSX 被转译成普通的 JavaScript 代码的样子,就能知道答案。
App
组件被转译成:这里
_jsx(Button, { onClick: addCount })
调用完之后只会返回一个 React element 对象,并没有执行Button
函数。内容不错或者比较美观的文章
React 17 introduces new JSX transform
JSX.Element vs ReactElement vs ReactNode
The text was updated successfully, but these errors were encountered: