本文来自 React技术揭秘html
JSX
做为描述组件内容的数据结构,为JS赋予了更多视觉表现力。在React
中咱们大量使用他。在深刻源码以前,有些疑问咱们须要先解决:react
JSX
和虚拟DOM是同一个东西么?
React Component
、
React Element
是同一个东西么,他们和
JSX
有什么关系?
带着这些疑问,让咱们开始这一节的学习。git
相信做为React
的使用者,你已经接触过JSX
。若是你还不了解他,能够看下官网对其的描述。github
JSX
在编译时会被Babel
编译为React.createElement
方法。web
这也是为何在每一个使用JSX
的JS文件中,你必须显式的声明数据结构
import React from 'react';
复制代码
不然在运行时该模块内就会报未定义变量 React
的错误。编辑器
你能够经过@babel/plugin-transform-react-jsx插件告诉Babel
编译时须要将JSX
编译为何函数的调用(默认为React.createElement
)。函数
好比在preact这个类React
库中,JSX
会被编译为一个名为h
的函数调用。post
// 编译前
<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,咱们看下他的实现:
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
中,咱们常使用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
是一种描述当前组件内容的数据结构,他并不能描述组件schedule、reconcile、render相关的信息。好比以下信息就不包括在JSX
中:
state
这些内容都是包含在虚拟DOM中的。
因此,在组件mount
时,Reconciler
根据JSX
描述的组件内容生成组件对应的虚拟DOM。在update
时,Reconciler
将JSX
与虚拟DOM保存的数据对比,为对比后状态有变化的虚拟DOM打上标记。
经过这篇文章在运行时修改React.createElement
达到消除页面全部div
元素的效果