最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 「React系列」之createElement及render源码分析

    正文概述 掘金(前端了了liaoliao)   2021-01-07   559

    React元素只读性

    let element = { name: '邓立' }
    Object.freeze(element)
    element.sex = '男'
    console.log(element);
    
    • Object.freeze()原理
    Object.defineProperties(Object, 'freeze', {//数据劫持
        value: function (obj) {
            var i;
            for (i in object) {//遍历属性和方法
                if (Object.hasOwnProperty(i)) {
                    Object.defineProperty(obj, i, {//数据劫持
                        writable: false//把所有属性改为不可修改 只读
                    })
                }
            }
            Object.seal(obj)//让此对象不能添加额外的属性
        }
    })
    
    • Object.defineProperties 方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。Object.defineProperties | MDN
    • hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。hasOwnProperty() | MDN
    • Object.seal() 方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要原来是可写的就可以改变。Object.seal() | MDN

    JSX转换

    • 源代码
    import React from 'react';
    function App() {
      return <p>Hello World</p>;
    }
    
    • React16 转换
    import React from 'react';
    
    function App() {
      return React.createElement('p', null, 'Hello world');
    }
    
    • React17 转换
    import {jsx as _jsx} from 'react/jsx-runtime';
    
    function App() {
      return _jsx('p', { children: 'Hello world' });
    }
    
    • 想使用之前的方式转换需要在package.json里设置
    "scripts": {
        "start": "set DISABLE_NEW_JSX_TRANSFORM=true&&react-scripts start",
        "build": "set DISABLE_NEW_JSX_TRANSFORM=true&&react-scripts build",
        "test": "set DISABLE_NEW_JSX_TRANSFORM=true&&react-scripts test",
        "eject": "set DISABLE_NEW_JSX_TRANSFORM=true&&react-scripts eject"
      },
    

    React源码解析

    页面结构

    import React from './react'
    import ReactDOM from 'react-dom'
    /**
     * React 自定义组件
     * 1.自定义的组件首字母大写;
     * 2.组件使用前先定义
     * 3.组件需要返回并且只能返回一个根元素 
     * @param {*} props 
     */
    function ReactTest(props) {
      return (<div className='title' style={{ background: 'pink', color: 'purple' }}>
        <span>{props.name}</span>
      </div>)
    }
    ReactDOM.render(
      <ReactTest name='前端了了liaoliao' />,
      document.getElementById('root')
    );
    

    效果展示

    「React系列」之createElement及render源码分析

    React之createElement源码分析

    /**
     * 
     * @param {*} type 元素类型
     * @param {*} config 元素配置
     * @param {*} children 子类信息
     */
    function createElement(type, config, children) {
        let props = { ...config }
        if (arguments.length > 3) {
        	//截取有效数据
            children = Array.prototype.slice.call(arguments, 2)
        }
        props.children = children
        return {
            type,
            props
        }
    }
    const React = { createElement }
    export default React 
    

    ReactDOM之render源码分析

    「React系列」之createElement及render源码分析

    /**
     * 1.把vdom(虚拟DOM)变成真实DOM dom
     * 2.把虚拟DOM上的属性更新(同步)到dom上
     * 3.把此虚拟DOM的儿子们也都变成真实DOM挂载到自己的dom上dom.appendChlid
     * 4.把自己挂载到容器上
     * @param {*} vdom 要渲染的虚拟DOM
     * @param {*} container 要把虚拟DOM转换真实DOM并插入到容器中去
     */
    function render(vdom, container) {
        const dom = createDOM(vdom)
        container.appendChild(dom)
    }
    /**
     * 把虚拟DOM变成真实DOM
     * @param {*} vdom 
     */
    function createDOM(vdom) {
        //如果是数字或者字符串,就直接返回文本节点
        if (typeof vdom === 'number' || typeof vdom === 'string') {
            return document.createTextNode(vdom)
        }
        //否则就是一个虚拟DOM对象,即React元素
        let { type, props } = vdom
        let dom = null
        if (typeof type === 'function') {//自定义函数组件
            return momentFunctionComponent(vdom)
        } else {
            if (type) {//原生
                dom = document.createElement(type)
            }
        }
        //使用虚拟DOM的属性更新刚创建出来的真实DOM的属性
        updateProps(dom, props)
        //在这里处理props.children属性
        if (props && props.children) {
            if (typeof props.children === 'string' || typeof props.children === 'number') {
                //如果只有一个子类,并且是数字或者字符串
                dom.textContent = props.children
            } else if (typeof props.children === 'object' && props.children.type) {
                //如果只有一个子类,并且是虚拟dom元素
                render(props.children, dom)
                //如果是数组
            } else if (Array.isArray(props.children)) {
                reconcileChildren(props.children, dom)
            } else {
                document.textContent = props.children ? props.children.toString() : ''
            }
        }
        // vdom.dom = dom
        return dom
    }
    /**
     * 把一个类型为自定义函数组件的虚拟DOM转换为真实DOM并返回
     * @param {*} vdom 类型为自定义函数组件的虚拟DOM
     */
    function momentFunctionComponent(vdom) {
        let { type: FunctionComponent, props } = vdom
        let renderVdom = FunctionComponent(props)
        return createDOM(renderVdom)
    }
    /**
     * 遍历数组
     * @param {*} childrenVdom 子类们的虚拟dom
     * @param {*} parentDOM 父类的真实DOM
     */
    function reconcileChildren(childrenVdom, parentDOM) {
        for (let i = 0; i < childrenVdom.length; i++) {
            let childVdom = createDOM(childrenVdom[i])
            render(childVdom, parentDOM)
        }
    }
    /**
     * 使用虚拟DOM的属性更新刚创建出来的真实DOM的属性
     * @param {*} dom 真实DOM
     * @param {*} newProps 新属性对象
     */
    function updateProps(dom, 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[attr]
                }
            } else {//js支持dom.title='设置'
                dom[key] = newProps[key]
            }
        }
    }
    const ReactDOM = { render }
    export default ReactDOM
    

    项目地址github


    起源地下载网 » 「React系列」之createElement及render源码分析

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元