react基础4:jsx语法渲染流程

2021-01-19  本文已影响0人  转移到CSDN名字丹丹的小跟班

jsx语法的解析

ReactDOM.render(
<div className="box" style={{color: 'red'}}>dandan</div>,
document.getElementById('root')
)

1.首先基于babel的语法解析模块(babel-preset-react)把jsx语法编译为react.creacteElement(....)结构

<div className="box" style={{color: 'red'}}>dandan</div>
//编译成以下结构:
React.createElement("div", {
  id: "div",
  className: "box",
  style: {color: 'red'}
}, "dandan")

2.执行React.createElement()函数,创建了一个对象(虚拟DOM)

// 根据传进来的值修改这个对象
//    1. type直接赋值给type
//    2. props中大部分属性都直接赋值给新对象props。
//    3. 但有一些比较特殊,如果是ref,key等值,我们需要把在props里面的值提取出来,创建两个新的对象存储,对象键就为ref,key,并将props里面的值删除
//    4.把传递进来的children作为新创建对象props中的一个属性(children为标签里的元素(可能是一个标签,文本或者更多的嵌套的标签))
// 编写createElement方法
// children有可能不止一个(如果有多个子元素就不止一个),也有可能一个没有
{
  type: 'div'
  ref: null
  key: null
  props: {
    id: 'div',
    className: 'box',
    style: {color: 'red'}
  __proto__: Object.prototype
}
  1. ReactDom.render() 将jsx语法最后生成的对象插入容器, 基于render方法把生成的对象动态创建为DOM元素,插入到指定容器里)

自己创建的createElement()函数

// children可能不止一个(如果有多个子元素就不止一个),也有可能一个没有,所以用...接取
function createElement(type, props, ...childrens) {
    // props可能不会有值,当无值时为{}
    props = props || {}
    // 定义ref, key变量,用于赋值
    let ref, key
    if("key" in props) {
        key = props.key
    }else {
        props.key = undefined
    }
    if("ref" in props) {
        ref = props.ref
    }else {
        props.key = undefined
    }
    // 返回一个对象
    return {type, props: {...props, children: childrens.length <= 1 ? childrens[0] : childrens}}
}

编写render方法 (作用:将创建的对象生成dom元素最后插入到页面当中)

function render(obj, container, callback) {
    // obj已经从createElement里得到
    // {
    //   type: 'div',
    //   props: {
    //     id: 'div',
    //     className: 'box',
    //     style: { color: 'red' },
    //     children: 'dandan'
    //   },
    //   ref: null,
    //   key: null
    // }
    
    // 首先结构赋值取出type, props
    let {type, props} = obj || {}
        //创建一个新元素
    let newElement = document.createElement(type)
    // 遍历props对象
    for(let attr in props) {
        // 检测是否为私有属性,如果不是则跳过此次循环
        if(!props.hasOwnProperty(attr)) continue
        // 检测该属性是否有值,如果没有则跳过此次循环
        if(!props[attr]) continue 
        // 取出属性值,将值赋值给元素
        let value = props[attr]
        // 一些属性需要单独处理
        // class
        if(attr === 'className') {
            newElement.setAttribute('class', value)
            continue
        }
        // style
        if(attr === 'style' && Object.prototype.toString.call(value) === '[object Object]') {
            for(let styKey in value) {
                if(value.hasOwnProperty(styKey)) {
                    newElement.style[styKey] = value[styKey]
                }
            }
            continue
        }
        // children可能为一个值(字符串或者jsx对象),也可能为一个数组(数组每一项可能是字符串,也可能是字符串)
        if(attr === 'children') {
          //如果不是数组,表示为一个值,则让他变成数组便于处理
            if(Object.prototype.toString.call(value) !== '[object Array]') {
                value = [value]
            }
            value.forEach((item, index) => {
                // 验证item类型,如果为对象则继续执行createElement方法,把创建的元素放到最开始的大盒子里
                if(Object.prototype.toString.call(item) === '[object String]') {
                    let text = document.createTextNode(item)
                    newElement.appendChild(text)
                }else {
                    render(item, newElement)
                }
            })
            continue
        }
        newElement.setAttribute(attr, value)
    }
    container.appendChild(newElement)
    callback && callback()
}

调用方法

render(createElement("div", {
  id: "div",
  className: "box",
  style: {color: 'red'}
}, "dandan"), document.getElementById("root"), () => {
    console.log('ok')
})
上一篇下一篇

猜你喜欢

热点阅读