导语 web前端技术中,有个叫做jsx的模板渲染语法,它是一个JavaScript 的语法扩展,目前逐渐被行业标准化(用的人多了...)。实际上jsx 是来源于一个前端框架 react。在react中除了我们了解的jsx,那么jsx在react的渲染过程是哪个环节生效,以及渲染过程经历了哪些步骤。本文会基于这些点进行概述。
介绍前的建议
1.本文附上了react.render树状图.xmind,此为作者查看/调试react的渲染源码时做的结构笔记。可以下载进行一些函数定位,一些函数代码位置较深且存在部分依赖,以此关联上下文是个不错的选择。
2.作者是基于17版本的react进行解读与调试 -- https://github.com/facebook/react/tree/v17.0.1
3.设置了几个大标题,提前介绍一下有关react的函数,二、三 目录可直接跳过。
React中用到的一些Object设置对象属性方法
React中自带的常用方法
React中的常用名词
jsx简述
比如如下代码:
ReactDOM.render(<div><h1>Hello, world!</h1><p>React to Render</p></div>,document.getElementById('root'));
这样就简单完成了页面的渲染。这其中经历了:
1.jsx经过babel打包转换成js语法 (@babel/helper-builder-react-jsx-experimental)
2.react执行render函数,进行节点的遍历渲染并绑定事件
如果拿刚才上述的代码进行babel转换,那么可以得到如下具体执行的jscode:
ReactDOM.render( /*#__PURE__*/ React.createElement("div", null, /*#__PURE__*/ React.createElement("h1", null, "Hello, world!"), /*#__PURE__*/ React.createElement("p", null, "React to Render")), document.getElementById('root') );
附上一个在线jsx转换地址: https://babeljs.io/repl/
如果同学们需要了解具体的转换语法,可以从 @babel/preset-react 中入口寻找依赖,具体的实现在 @babel/helper-builder-react-jsx-experimental、@babel/helper-plugin-utils
作者仅简单关联了上下文,这里稍作截图:
我们会在webpack打包后的代码中看到 "_source","jsxFileName" 关键字。
这里的@babel/helper-plugin-utils还有一个作用:dev环境时在打包过程中 输出文件代码块的位置,以便于调试定位,处理会返回输出的一个只读属性 "__source",其中包含 { fileName:path, lineNumber:number, columnNumber:number }
所以,jsx语法与渲染的执行代码没有存在关联,react通过babel转换成原生js进行处理执行。转换后的createElement即为创建节点。
创建节点
创建节点的入口代码在 "react/src/React.js" createElement函数
createElement会根据当前的环境,引用不同的创建函数
不过,开发版与产品版仅是一些有没有验证合法性,和是否输出错误内容的区别。最终都会创建同样的element对象 -- 虚拟节点
如下图所示:
图上面右侧的错误提示中,用到的
"filename,lineNumber"即在打包过程中 @babel/helper-plugin-utils 提前注入好对象属性
当执行 真正的创建节点时 "ReactElement.js/createElement",这里会生成一个带有一些标记属性的Node对象:
渲染
渲染可以按照功能,切分为三个点 准备、执行、提交。
render函数在 "/react-dom/src/client/ReactDOMLegacy.js"中,在进行一系列检查判断后最终会在legacyRenderSubtreeIntoContainer函数下执行渲染
legacyRenderSubtreeIntoContainer函数会执行以下三个步骤 :
代码大致截图:
我们按照3个步骤往下延伸:
1-准备阶段:
做两件事:
1.创建根节点的一些关联对象 ReactRoot、fiberRoot、(HostRoot)fiberNode
2.定义根节点的默认浏览器事件
如下图,此为根节点(root)的关联对象
关联对象具备如下一些属性及功能
如下图,定义的默认事件:
准备阶段总结如下:
2 - 执行阶段 :
代码入口在 "react-reconciler/src/ReactFiberReconciler.js" unbatchedUpdates函数中
此阶段给所有的节点生成好节点树,等待提交
如下图所示:
3 - 提交阶段:
提交阶段的代码也在 "react-reconciler/src/ReactFiberReconciler.js" unbatchedUpdates函数内执行
具体的函数执行在 "react-reconciler/src/ReactFiberWorkLoop.new.js" performSyncWorkOnRoot函数下执行 commitRoot
进行实际的执行Dom操作、部分周期函数
如下图所示:
最终在 commitBeforeMutationEffects 和 commitMutationEffects 执行真正的dom操作和事件提交
整体的渲染流程到提交阶段执行完之后页面就已经能够看到效果,剩下的动作做一些数据同步、重设标记时间等..