查看原文
JSX作为描述组件内容的数据结构,为JS赋予了更多视觉表现力。在React中我们大量使用他。在深入源码之前,有些疑问我们需要先解决:
JSX和Fiber节点是同一个东西么?
React Component、React Element是同一个东西么,他们和JSX有什么关系?
带着这些疑问,让我们开始这一节的学习。
#JSX简介
相信作为React的使用者,你已经接触过JSX。如果你还不了解他,可以看下官网对其的描述 (opens new window)。
JSX在编译时会被Babel编译为React.createElement方法。
这也是为什么在每个使用JSX的JS文件中,你必须显式的声明
import React from '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的函数调用。
// 编译前
<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。
所以调用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。
那么JSX和React Component的关系呢?
#React Component
在React中,我们常使用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自身。
FunctionComponent对应的Element的type字段为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。
ClassComponent.prototype.isReactComponent = {};
#JSX与Fiber节点
从上面的内容我们可以发现,JSX是一种描述当前组件内容的数据结构,他不包含组件schedule、reconcile、render所需的相关信息。
比如如下信息就不包括在JSX中:
- 组件在更新中的
优先级
- 组件的
state
- 组件被打上的用于Renderer的
标记
这些内容都包含在Fiber节点中。
所以,在组件mount时,Reconciler根据JSX描述的组件内容生成组件对应的Fiber节点。
在update时,Reconciler将JSX与Fiber节点保存的数据对比,生成组件对应的Fiber节点,并根据对比结果为Fiber节点打上标记。
#参考资料
查看原文
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)