We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
查看原文
JSX作为描述组件内容的数据结构,为JS赋予了更多视觉表现力。在React中我们大量使用他。在深入源码之前,有些疑问我们需要先解决:
JSX
React
Fiber节点
React Component
React Element
带着这些疑问,让我们开始这一节的学习。
相信作为React的使用者,你已经接触过JSX。如果你还不了解他,可以看下官网对其的描述 (opens new window)。
JSX在编译时会被Babel编译为React.createElement方法。
Babel
React.createElement
这也是为什么在每个使用JSX的JS文件中,你必须显式的声明
import React from 'react';
否则在运行时该模块内就会报未定义变量 React的错误。
未定义变量 React
注意
在React17中,已经不需要显式导入React了。详见介绍全新的 JSX 转换(opens new window)
JSX并不是只能被编译为React.createElement方法,你可以通过@babel/plugin-transform-react-jsx (opens new window)插件显式告诉Babel编译时需要将JSX编译为什么函数的调用(默认为React.createElement)。
比如在preact (opens new window)这个类React库中,JSX会被编译为一个名为h的函数调用。
h
// 编译前 <p>KaSong</p> // 编译后 h("p", null, "KaSong");
既然JSX会被编译为React.createElement,让我们看看他做了什么:
export function createElement(type, config, children) { let propName; const props = {}; let key = null; let ref = null; let self = null; let source = null; if (config != null) { // 将 config 处理后赋值给 props // ...省略 } const childrenLength = arguments.length - 2; // 处理 children,会被赋值给props.children // ...省略 // 处理 defaultProps // ...省略 return ReactElement( type, key, ref, self, source, ReactCurrentOwner.current, props, ); } const ReactElement = function(type, key, ref, self, source, owner, props) { const element = { // 标记这是个 React Element $$typeof: REACT_ELEMENT_TYPE, type: type, key: key, ref: ref, props: props, _owner: owner, }; return element; };
我们可以看到,React.createElement最终会调用ReactElement方法返回一个包含组件数据的对象,该对象有个参数$$typeof: REACT_ELEMENT_TYPE标记了该对象是个React Element。
ReactElement
$$typeof: REACT_ELEMENT_TYPE
所以调用React.createElement返回的对象就是React Element么?
React提供了验证合法React Element的全局API React.isValidElement (opens new window),我们看下他的实现:
export function isValidElement(object) { return ( typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE ); }
可以看到,$$typeof === REACT_ELEMENT_TYPE的非null对象就是一个合法的React Element。换言之,在React中,所有JSX在运行时的返回结果(即React.createElement()的返回值)都是React Element。
$$typeof === REACT_ELEMENT_TYPE
null
React.createElement()
那么JSX和React Component的关系呢?
在React中,我们常使用ClassComponent与FunctionComponent构建组件。
ClassComponent
FunctionComponent
class AppClass extends React.Component { render() { return <p>KaSong</p> } } console.log('这是ClassComponent:', AppClass); console.log('这是Element:', <AppClass/>); function AppFunc() { return <p>KaSong</p>; } console.log('这是FunctionComponent:', AppFunc); console.log('这是Element:', <AppFunc/>);
我们可以从Demo控制台打印的对象看出,ClassComponent对应的Element的type字段为AppClass自身。
Element
type
AppClass
FunctionComponent对应的Element的type字段为AppFunc自身,如下所示:
AppFunc
{ $$typeof: Symbol(react.element), key: null, props: {}, ref: null, type: ƒ AppFunc(), _owner: null, _store: {validated: false}, _self: null, _source: null }
值得注意的一点,由于
AppClass instanceof Function === true; AppFunc instanceof Function === true;
所以无法通过引用类型区分ClassComponent和FunctionComponent。React通过ClassComponent实例原型上的isReactComponent变量判断是否是ClassComponent。
isReactComponent
ClassComponent.prototype.isReactComponent = {};
从上面的内容我们可以发现,JSX是一种描述当前组件内容的数据结构,他不包含组件schedule、reconcile、render所需的相关信息。
比如如下信息就不包括在JSX中:
优先级
state
标记
这些内容都包含在Fiber节点中。
所以,在组件mount时,Reconciler根据JSX描述的组件内容生成组件对应的Fiber节点。
mount
Reconciler
在update时,Reconciler将JSX与Fiber节点保存的数据对比,生成组件对应的Fiber节点,并根据对比结果为Fiber节点打上标记。
update
div
The text was updated successfully, but these errors were encountered:
No branches or pull requests
查看原文
JSX
作为描述组件内容的数据结构,为JS赋予了更多视觉表现力。在React
中我们大量使用他。在深入源码之前,有些疑问我们需要先解决:JSX
和Fiber节点
是同一个东西么?React Component
、React Element
是同一个东西么,他们和JSX
有什么关系?带着这些疑问,让我们开始这一节的学习。
#JSX简介
相信作为
React
的使用者,你已经接触过JSX
。如果你还不了解他,可以看下官网对其的描述 (opens new window)。JSX
在编译时会被Babel
编译为React.createElement
方法。这也是为什么在每个使用
JSX
的JS文件中,你必须显式的声明否则在运行时该模块内就会报
未定义变量 React
的错误。注意
在React17中,已经不需要显式导入React了。详见介绍全新的 JSX 转换(opens new window)
JSX
并不是只能被编译为React.createElement
方法,你可以通过@babel/plugin-transform-react-jsx (opens new window)插件显式告诉Babel
编译时需要将JSX
编译为什么函数的调用(默认为React.createElement
)。比如在preact (opens new window)这个类
React
库中,JSX
会被编译为一个名为h
的函数调用。#React.createElement(opens new window)
既然
JSX
会被编译为React.createElement
,让我们看看他做了什么:我们可以看到,
React.createElement
最终会调用ReactElement
方法返回一个包含组件数据的对象,该对象有个参数$$typeof: REACT_ELEMENT_TYPE
标记了该对象是个React Element
。所以调用
React.createElement
返回的对象就是React Element
么?React
提供了验证合法React Element
的全局API React.isValidElement (opens new window),我们看下他的实现:可以看到,
$$typeof === REACT_ELEMENT_TYPE
的非null
对象就是一个合法的React Element
。换言之,在React
中,所有JSX
在运行时的返回结果(即React.createElement()
的返回值)都是React Element
。那么
JSX
和React Component
的关系呢?#React Component
在
React
中,我们常使用ClassComponent
与FunctionComponent
构建组件。我们可以从Demo控制台打印的对象看出,
ClassComponent
对应的Element
的type
字段为AppClass
自身。FunctionComponent
对应的Element
的type
字段为AppFunc
自身,如下所示:值得注意的一点,由于
所以无法通过引用类型区分
ClassComponent
和FunctionComponent
。React
通过ClassComponent
实例原型上的isReactComponent
变量判断是否是ClassComponent
。#JSX与Fiber节点
从上面的内容我们可以发现,
JSX
是一种描述当前组件内容的数据结构,他不包含组件schedule、reconcile、render所需的相关信息。比如如下信息就不包括在
JSX
中:优先级
state
标记
这些内容都包含在
Fiber节点
中。所以,在组件
mount
时,Reconciler
根据JSX
描述的组件内容生成组件对应的Fiber节点
。在
update
时,Reconciler
将JSX
与Fiber节点
保存的数据对比,生成组件对应的Fiber节点
,并根据对比结果为Fiber节点
打上标记
。#参考资料
React.createElement
达到消除页面所有div
元素的效果(opens new window)The text was updated successfully, but these errors were encountered: