一、元素渲染
1.1 React元素
元素是组成React应用的最小单位,与浏览器的DOM元素不同,React元素是通过解析JSX生成的普通对象(virtual dom),被用来模拟真实DOM。而React可以以极低的开销通过元素来创建真实的DOM,其中ReactDOM就是用来负责渲染和更新DOM的。
1.2 元素的渲染原理
元素的渲染原理就是将
JSX
转换成vdom
,再经过一系列的操作生成真实DOM
,再将真实DOM插入容器中。
值得注意的是
React元素是不可变对象
,也就代表着一旦创建就无法更改元素。像动画的单独一帧,只能代表一个特定的时间点。(React17之前这只是官方约定此对象不可变,在React17之后,源码中在开发环境中直接将整个React元素给Object.freeze()
了)
二、源码实现
2.1 createElement
createElement
是用来将JSX
转换成vdom
的方法
children
的处理
- 传入多个儿子,将
props.children
处理成数组 - 传入一个儿子,将
props.children
处理成对象 - 没有传入儿子,将
props.children
就是undefined
function createElement(type,config,children){
let props = {...config};
if(argument.length > 3){
children = Array.prototype.slice.call(arguments,2).map(warpToVdom)
}else{
if(typeof children !== 'undefined'){
props.children = warpToVdom(children);
}
}
return {
type,
props
}
}
// React的源码对于文本和数字有做特殊,这里方便理解简单,统一将它包装成vdom(源码没有这一步,只是方便写伪代码)
export const REACT_TEXT = Symbol('REACT_TEXT');
function warpToVdom(element){
if(typeof element === 'string' || typeof element === 'number'){
return {
type: REACT_TEXT,
props:{
content:element
}
}
}else{
return element;
}
}
复制代码
2.2 ReactDOM
vdom渲染的方法统一放在
ReactDOM
中
2.2.1 render
render
函数很好理解,就是将vdom
创建成真实DOM
,再将真实DOM
插入容器中
function render(vdom,container){
let newDOM = createDOM(vdom);
container.appendChild(newDOM);
}
复制代码
2.2.2 createDOM 创建真实DOM
- 根据
vdom
的 type 创建真实元素
- 再根据
props
更新真实元素
的属性 - 最后通过
props.children
来判断是否递归执行render
function createDOM(vdom){
let {type,props} = vdom;
let dom;
// 1. 创建DOM
if(type === REACT_TEXT){
dom = document.createTextNode(props.content);
}else{
dom = document.createElement(props.content);
}
if(props){
// 2. 更新属性
updateProps(dom,{},props);
// 3. 递归渲染
if(typeof props.children == 'object' && props.children.type){
render(props.children,dom)
}else if(Array.isArray(props.children)){
reconcileChildren(props.children,dom)
}
}
vdom.dom = dom
return dom
}
复制代码
2.2.3 updateProps 更新属性
function updateProps(dom,oldProps,newProps){
for(let key in newProps){
if(key === 'children'){
continue;
}
if(key === 'style'){
let styleObj = newProps[style];
for(let attr in styleObj){
dom.style[attr] = styleObj[sttr]
}
}else{
dom[key] = newProps[key]
}
}
}
复制代码
2.2.4 reconcileChildren
当
props.children
是对象时直接render,是数组时通过reconcileChildren
遍历出 child 再render
function reconcileChildren(childrenVdom,parentDOM){
for(let i=0;i<childrenVdom.length;i++){
let childVdom = childrenVdom[i];
render(childVdom,parentDOM)
}
}
复制代码
2.2.5 导出
最后将render方法导出即可
const ReactDOM = {
render
}
export default ReactDOM;
复制代码
三、结语
这套思路是借鉴了
React15
的源码来实现的,和现在的最新版的代码有一些出入,是由于16中新增的Fiber
会使virtual DOM
进行增量式渲染,直接手写的话会劝退大部分萌新玩家,之所以选择React15
是由于这个渲染的过程更易于理解,当然后续也是会更新Fiber
相关的内容的。对Fiber
感兴趣的话可以看看这个–react-fiber-architecture。最后点个赞再走吧~~
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END