react中jsx转真实DOM的基本流程

回顾

​ 今天刷《JS每日一题》的时候,我看了react jsx渲染成真实DOM的面试题,我看了一遍,我感觉我自己可以很清晰的跟着作者的脚步走。虽然我感觉很清晰逻辑步骤,但是作为菜鸟,多敲敲代码总是好的,也能加深对该知识点的印象。

​ 经过前面几次的学习和思考,我知道了 react中的jsx语法会通过babel转化为 js代码,以React.createElement函数形式存在,createElement函数返回一个ReactElement函数,ReactElement函数返回一个的虚拟节点,虚拟节点中嵌套虚拟节点,就形成了虚拟DOM,最后通过ReactDOM.render方法转化为真实DOM

简单的代码示例:

<div>
  <img src="avatar.png" className="profile" />
  <Hello />
</div>

//转化后
React.createElement(
  "div",
  null,
  React.createElement("img", {
    src: "avatar.png",
    className: "profile"
  }),
  React.createElement(Hello, null)   //组件的名称
);

这里有个疑点?babel转化的时候,是怎么区分是原生标签名?还是组件名呢?

我们写代码的过程中,我们都知道react的组件名称,首字母必须大写,不大写就会相当于原生标签, 也许就是为这里进行解释的吧。

解答:

babel在转化jsx过程中,会判断首字母的大小写

  • 首字母为小写的时候,会被认为是原生DOM标签, 那么createElement中的第一个参数就是一个字符串,表示标签的名称
  • 首字母为大写的时候,会被认为是组件,那么createElement中的第一个参数就是组件的名称,如上面的Hello

createElement会根据传入的type进行判断:

  • 如果是原生标签节点,type是字符串,如 span div
  • 如果是文本节点,type就没有
  • 如果是函数组件,type就是函数名
  • 如果是类组件,type就是类组件名

虚拟DOM会通过ReactDOM.render()进行渲染成真实DOM

ReactDOM.render(element, container[, callback])

首次渲染的时候,容器中的所有DOM节点都会被替换掉,以后的跟新就会使用react中的diff算来,来进行比较。

如果传递了callback回调函数,就会在渲染完成的时候,再进行回调。

那么render函数实现呢?

跟着《JS每日一题》手动敲了一遍,还是挺有收货的,对React.createElement函数有着更近一步的理解咯。

render()函数的实现

function render(vnode, container) {
    //createNode函数根据虚拟DOM,返回一个真实DOM
    const node = createNode(vnode, container)
    //通过appendChild原生方法添加再挂载的容器中
    container.appendChild(node)
}
createNode函数的实现(思路)
function createNode(vnode, parentNode) {
    let node = null    //创建一个变量,来保存真实节点
    const { type, props, children } = vnode
    if(type === TEXT) {   //如果是一个空节点
        node = document.createTextNode('')
    } else if(typeof type === 'string') {  //字符串,说明是一个原生标签
        node = document.createElement(type)
    } else if(typeof type === 'function') { //组件
        //isReactComponent是类组件原型上的一个对象,专门用来区分是函数组件,还是类组件
        node = type.isReactComponent ? renderClass(vnode, parentNode) : renderFunction(vnode, parentNode)
    }

    //用来处理子节点(虚拟DOM) 挂载到当前的node(真实DOM)
    renderChild(children, node)
    //处理props,绑定在节点上面
    handleProps(node, props)
    return node
}

在上面的带有有isReactComponent这个属性,该属性是类组件的原型上,是react内部用来区分该组件到底是类组件还是函数组件。

renderChild函数的实现(思路)

处理子节点虚拟DOM转化为真实DOM(递归

 //循环遍历 子节点, 然后依次调用render函数
function renderChild(childArr, parentNode) {
    for(let i = 0; i < childArr.length; i++) {
        let child = childArr[i]
        if(Array.isArray(child)) {
            for(let k = 0; k < child.length; k++) {
                render(child[k], parentNode)
            }
        } else {
            render(child, parentNode)
        }
    }
}
handleProps函数的实现(思路)
//在createElement函数中,已经收集了(数组的形式)
function handleProps(node, props) {
    Object.keys(props).forEach(item => {
        //处理事件(onclick)等
        if(item.slice(0,2) === 'on') {
            let eventName = item.slice(2).toLocaleLowerCase()
            node.addEventListener(eventName, props[item])
        } else {
            node[item] = props[item]
        }
    })
}
渲染函数组件的方法实现
function renderFunction(vnode, parentNode) {
    const { type, props } = vnode
    //type是个函数名,执行函数,传入props
    const node = type(props)
    //调用真实节点函数,生成一个真实DOM,并返回
    return createNode(node, parentNode)
}
渲染类组件的方法实现
function renderClass(vnode, parentNode) {
    const { type, props } = vnode
    //type是一个类(构造函数), 通过new创建一个实例
    const node = new type(props)
    //创建实例后,调用render方法
    const renderNode = node.render()
    //调用真实节点函数,生成一个真实DOM,并返回
    return createNode(renderNode, parentNode)
}

你可能感兴趣的:(react)