乐趣区

关于react.js:React初始化渲染

初始化渲染的过程,咱们能够间接对应到 React 程序中 ReactDOM.render 函数调用后的后果。

1. 环境筹备

初始化我的项目:npx create-react-app simple-react
删除一些代码,最要害的内容就是:
src/index.js
public/index.html
package.json 中的 dependencies 和 scripts:

2.JSX 介绍

JSX 是 JavaScript 的一种语法扩大。
JSX 到一般 Javascript 的代码的转化是通过 babel 实现的。

3.React.createElement 编写

DISABLE_NEW_JSX_TRANSFORM=true 禁用掉新的 jsxdev 转换,应用 React.createElement

这个迷你的包 (cross-env) 可能提供一个设置环境变量的 scripts,让你可能以 unix 形式设置环境变量,而后在 windows 上也能兼容运行。
npm i cross-env -D
package.json
“start”: “cross-env DISABLE_NEW_JSX_TRANSFORM=true react-scripts start”,

react17 之前通过 Babel 把 jsx 转换为虚构 DOM
<div> Hello Simple React </div>
React.createElement(“div”, null, “Hello Simple React”)

react.js

import {REACT_ELEMENT} from "./utils"
function createElement(type, properties, children) {
    // debugger
    let ref = properties.ref || null
    let key = properties.key || null

        // 属性能够是可读的、可写的或可删除的。// 属性能够是可枚举的(能够通过 for…in 循环枚举)或不可枚举的。// 能够通过 Object.defineProperty()等办法定义属性的个性。;['key', 'ref', '__self', '__source'].forEach(key => {delete properties[key]
        })
    let props = {...properties}
    // 传参大于 3
    if (arguments.length > 3) {console.log(arguments)
        props.children = Array.prototype.slice.call(arguments, 2)
    } else {props.children = children}
    return {
        //$$-- 唯一性,jsx 转换成为的类型
        $$typeof: REACT_ELEMENT,
        type,
        ref,// 操作 DOM
        key,//diff 比拟
        props
    }
}


const React = {createElement}

export default React

4.ReactDOM.render 函数编写

初始化渲染第一步:实现函数 React.createElement
初始化渲染第二步:实现函数 ReactDOM.render

react-dom.js

import {REACT_ELEMENT} from './utils'
// 初始化渲染(不止挂载操作)function render(VNode, containerDOM) {
    // 将虚构 DOM 转化成实在 DOM
    // 将失去的实在 DOM 挂载到 contanerDOM 中
    mount(VNode, containerDOM)
}
function mount(VNode, containerDOM) {let newDOM = createDOM(VNode)
    newDOM && containerDOM.appendChild(newDOM)
}
function createDOM(VNode) {
    // 1. 依据类型创立元素 2. 解决子元素,VNode 是树形构造 3. 解决属性值
    const {type, props} = VNode
    let dom;
    //typeof === REACT_ELEMENT 代表虚构 DOM VNode
    if (type && VNode.$$typeof === REACT_ELEMENT) {dom = document.createElement(type)
    }

    if (props) {
        // 子节点存在
        if (typeof props.children === 'object' && props.children.type) {
            // 递归,创立子节点
            mount(props.children, dom)
        } else if (Array.isArray(props.children)) {
            // 多个子节点
            mountArray(props.children, dom)
        } else if (typeof props.children === 'string') {
            // 文本节点
            dom.appendChild(document.createTextNode(props.children))
        }
    }
    // TODO:解决属性值
    //setPropsForDOM(dom, props)
    return dom
}


// 多个子节点解决
function mountArray(children, parent) {console.log('parent', parent, children)
    if (!Array.isArray(children)) return
    for (let i = 0; i < children.length; i++) {if (typeof children[i] === 'string') {
            // 创立文本节点
            parent.appendChild(document.createTextNode(children[i]))
        } else {mount(children[i], parent)
        }
    }
}

const ReactDOM = {render}

export default ReactDOM

5. 实现函数 setPropsForDOM 进行属性更新

//【这里须要留神 jsx 属性名称的写法】

ReactDOM.render(<div className='test-class' style={{color: 'red'}}>Simple React App<span>xx1</span><span>xx2</span></div>, document.getElementById('root'))
function setPropsForDOM(dom, VNodeProps = {}) {if (!dom) return
    for (let key in VNodeProps) {if (key === 'children') continue
        //^ 结尾,on 结尾,A- Z 之间一个,. 匹配单个字符,//* 号匹配一个字母或者数字呈现一次或者屡次 onclick
        if (/^on[A-Z].*/.test(key)) {// TODO: 事件处理} else if (key === 'style') {Object.keys(VNodeProps[key]).forEach(styleName => {dom.style[styleName] = VNodeProps[key][styleName]
            })
        } else {dom[key] = VNodeProps[key]
        }
    }
}

最初总结:从 JSX 源代码(<div>Hello JSX</div>)到显示到界面上,须要经验哪些关键环节。
JSX -> 转译成函数调用 -> 执行函数调用返回虚构 DOM -> 将虚构 DOM 转化成 DOM -> 将生成的 DOM 挂载到 DOM 上

退出移动版