react 源码分析:babel 如何解析 jsx
同作为 MVVM 框架,React 相比于 Vue 来讲,上手更需要JavaScript
功底深厚一些,本系列将阅读React
相关源码,从jsx -> VDom -> RDOM
等一些列的过程,将会在本系列中一一讲解
工欲善其事,必先利其器
经过多年的发展,React 已经更新了大版本16、17、18
,本系列主要讲的是 version:17.0.2
,在讲这个版本之前,我们先看一看在babel
的编译下,每个大版本之下会有什么样的变化。
jsx
v16.x 及以前版本
v17 及之后版本
所以各位看到了,在v16
及以前我们babel
进行jsx
解析编译的是根据 @babel/babel-preset-react-app 解析成React.createElement
进行包裹的,而v17
以及之后的版本,官网早就说明,对jsx
的转换用react/jsx-runtime
,而不再依赖React.createElement
了,看到这里我想各位对不同版本的 babel 解析 jsx 已经有了眉目了,早已经迫不及待想去看看 jsx-runtime 和 createElement 到底是如何玩的,那么进入源码
在 babel 解析后的 v17 产物中我们可以看得到 var _jsxRuntime = require("react/jsx-runtime");
那么我们追本溯源可以找到在packages/react/src/jsx/ReactJSX.js
里面的jsxs
是怎么来的
在非dev
环境下我们继续去找jsProd
ReactElement
这上面便是v17
及之后版本的jsx-runtime
所做的事情。那么这里再去看一下v16
中的createElement
所做的事情吧。相关参考视频讲解:进入学习
React.createElement
由 React.createElement 源码得知,他做了如下事情
解析
config
参数中是否有合法的key
、ref
属性,并处理,并将其他的属性挂到props
上。解析函数的第三参数,并分情况将第三参数挂到
props.children
上。对默认 props 进行处理,如果存在该属性则直接挂载到 props 上,不存在则要添加上。
开发环境下将
_store、_self、_source
设置为不可枚举状态,为后期的 diff 比较作优化,提高比较性能。将
type、key、ref、props
等属性通过调用 ReactElement 函数创建虚拟 dom。
ReactElement
仔细瞧一瞧,这个其实跟jsxs
调用的ReactElement
实现的差不多的功能,但是为什么要写两遍?仔细看来,在两个版本的ReactElement
中,传入的参数不一致,在开发环境下,分别对其做劫持不可枚举状态,仅此而已?
React.Component
写惯了hooks
组件,但是Class
组件也别忘了哟,因为在React17
里面Class
组件也是没有被抹去的,所以既然是源码解析,那么我们也要来看一看这个Component
到底干了啥。
从源码上可以得知,React.Component
主要做了以下几件事情:
将
props, context, updater
挂载到this
上,props,context
一目了然,后面的updater
位触发器,上面挂了很多方法,我们后面再谈。在
Component
原型链上添加isReactComponent
对象,用于区分函数组件还是类组件。在
Component
原型链上添加setState
方法,触发更新。在
Component
原型链上添加forceUpdate
方法,强制更新。
总结
不管是类组件还是函数组件,最终我们写的jsx
都被babel
转化成了可识别的元素,其中我们也看了ReactElement
,createElement
,Component
等内部实现,了解到了作为ReactElement
他是怎么被创建的,但是远远没有完,因为我们知道我们在写 React 的时候,会在后面带上一个ReactDOM.render(<Element/>, 'root')
,没错我们下一章节就要去探索一下ReactDOM.render
方法了。
评论