本文共 5462 字,大约阅读时间需要 18 分钟。
react以声明式、组件化、一次学习随处编写的理念深受广大开发者热爱,其重要程度不言而喻。因此值得我们深入学习,此专题主要分享些react中部分源码解析。
const ele =hello
;复制代码
看不明白没关系,下面我们一步步解释下
这段代码并不是合法的js代码,它是一种被称为jsx的语法扩展,通过它我们就可以很方便的在js代码中书写html片段。 本质上,jsx是语法糖,上面这段代码会被babel转换成如下代码
分析下,对于React.createElement(xxx)中的参数是如何转变出来的,肯定是用一些正则匹配出来的,我们在这里暂不做重点解释。我们重点关注createElement()方法的实现 和render()方法实现
我们可以把ele打印出来看看虚拟DOM的样子
let jsxObj = createElement( 'div', null, createElement( 'h1', {style: {fontSize: '20px'}, className: 'box'}, 'hello' ), createElement( 'h1', {style: {fontSize: '20px'}, className: 'box'}, 'world' ));复制代码
假设我们已经有上述的结构,我们来实现createElement()方法转化成虚拟DOM。
<div> <h1 style={ {fontSize:'20px'}} className='box'>hello</h1></div> ->> 正则匹配到createElement(xxx)中的各个参数的,这里不做重点介绍正则
function createElement(type, props, ...childrens) { return { type: type, props: { ...props, children: childrens.length <= 1 ? childrens[0] : childrens } };}复制代码
完美,其实createElement()转化为虚拟DOM还是比较简单的,下面来实现讲虚拟DOM转化为真实的DOM呢
我们需要这样调用render方法。 render(jsxObj,container) 。jsxObj-> 虚拟的DOM对象 、container->挂载到的节点
function render(jsxObj, container) { //解构types和props解构props中的children let {type, props} = jsxObj, {children} = props; let newElement = document.createElement(type);//创建type类型的DOM元素 for (let attr in props) { //循环props if (!props.hasOwnProperty(attr)) break; switch (attr) { case 'className': //attr为className,增加class="xxx"属性 newElement.setAttribute('class', props[attr]); break; case 'style': //attr为styel,js实现增加样式 let styleOBJ = props['style']; for (let key in styleOBJ) { if (styleOBJ.hasOwnProperty(key)) { newElement['style'][key] = styleOBJ[key]; } } break; case 'children': let childrenAry = props['children']; //childrenAry为数组-> childrenAry,为str的话 ->[str] ,为空的话-> [].统一转变数组便于循环 childrenAry = childrenAry instanceof Array ? childrenAry : (childrenAry ? [childrenAry] : []); childrenAry.forEach(item => { if (typeof item === 'string') { //文本节点,直接增加到元素中 newElement.appendChild(document.createTextNode(item)); } else { //新的JSX元素,递归调用RENDER,只不过此时的容器是当前新创建的newElement render(item, newElement); } }); default: newElement.setAttribute(attr, props[attr]); } } container.appendChild(newElement);}复制代码
<h1></h1>
写了这么多,来测试下吧
完美,到目前来看一切正常。附上源码。
function createElement(type, props, ...childrens) { return { type: type, props: { ...props, children: childrens.length <= 1 ? childrens[0] : childrens } };}let jsxObj = createElement( 'div', null, createElement( 'h1', {style: {fontSize: '20px'}, className: 'box'}, 'hello' ), createElement( 'ul', null, createElement( 'li', null, 'AA' ), createElement('li', null) ));//=>DOM的动态创建function render(jsxObj, container, callback) { let {type, props} = jsxObj, {children} = props; let newElement = document.createElement(type); //=>属性和子元素的处理 for (let attr in props) { if (!props.hasOwnProperty(attr)) break; switch (attr) { case 'className': newElement.setAttribute('class', props[attr]); break; case 'style': let styleOBJ = props['style']; for (let key in styleOBJ) { if (styleOBJ.hasOwnProperty(key)) { newElement['style'][key] = styleOBJ[key]; } } break; //=>CHILDREN case 'children': let childrenAry = props['children']; childrenAry = childrenAry instanceof Array ? childrenAry : (childrenAry ? [childrenAry] : []); childrenAry.forEach(item => { if (typeof item === 'string') { //=>字符串:文本节点,直接增加到元素中 newElement.appendChild(document.createTextNode(item)); } else { //=>字符串:新的JSX元素,递归调用RENDER,只不过此时的容器是当前新创建的newElement render(item, newElement); } }); default: newElement.setAttribute(attr, props[attr]); } } container.appendChild(newElement); callback && callback();}render(jsxObj, window.root, () => { console.log('哈哈');});作者:言sir 链接:https://juejin.im/post/5b4ee916f265da0f563dd184 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。