最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • react源码(一):实现createElement和render函数

    正文概述 掘金(哇哦哦)   2021-01-20   954

    因为react依赖JSX语法,所以最开始需要对JSX的执行过程做大致介绍。至于JSX的相关基础知识,请参照JSX官网。

    JSX的执行过程

    • 1.写出代码
    <h1>Hello</h1>
    
    • 2.用打包工具如webpack,调用babel-loader把JSX语法转换成JavaScript函数,createElement
    • 3.运行代码时,浏览器执行createElement,得到虚拟DOM,也就是react元素。其实react元素就是一个纯粹的JavaScript对象,描述了会在页面上显示的DOM的属性。
    • 4.把虚拟DOM(react元素)给render函数,render就会把虚拟DOM转换成真实的DOM,并插入到页面中去。

    react中的虚拟DOM

    上面说道react元素就是虚拟DOM,接着来看一下react元素有哪些属性

    // 首先是调用React.createElement生成react元素,并在控制台输出
    let element = React.createElement("h1", {
        className: 'title',
        style: {
            color: 'red'
        }
    }, React.createElement('span', null, 'hello'), ' world');
    console.log(JSON.stringify(element,null,2));
    
    ReactDOM.render(
        element, 
        document.getElementById('root')
    );
    
    // 下面展示的属性并不是完全的react元素的属性,为了简化,把一些当前阶段用不到的属性进行删除
    
    /**
    {
         "type": "h1",
         "props": {
             "className": "title",
             "style": {
                 "color": "red"
             },
             "children": [
             	{
                     "type": "span",
                     "props": {
                         "children": "hello"
                     }
                 },
                 " world"
             ]
         }s
     }
     */
    

    主要思路

    • 1.createElement函数的主要任务就是创建一个JavaScript对象
    • 2.render函数的主要任务就是创建文本节点和各种元素,将其插入到页面中去

    代码实现

    • 1.createElement函数
    /**
     * 
     * @param {*} type 元素类型
     * @param {*} config 配置对象,一般来说就是属性对象
     * @param {*} children 第一个儿子
     */
    function createElement(type,config,children){
        if(config){
            delete config._owner;
            delete config._store;
        }
        let props = {...config};
        if(arguments.length > 3){
            children = Array.prototype.slice.call(arguments,2);
        }
        // children可能是数组(多于一个儿子),也可能是一个字符串或数字、也可能是一个null 或 react元素
        props.children = children;
        return {
            type,props
        }
    }
    
    • 2.render函数
    /**
     * 虚拟DOM转换成真实DOM 并插入到容器
     * @param {*} vdom 虚拟DOM
     * @param {*} container 插入到哪个容器
     */
    function render(vdom,container){
        const dom = createDOM(vdom);
        container.appendChild(dom);
    }
    
    /**
     * 把虚拟DOM变成真实DOM
     * @param {} vdom null 数字 字符串 React元素 数组 暂时不支持数组
     */
    function createDOM(vdom){
        // null就什么都不做
        // 如果vdom是一个字符串或者数字的话,创建一个文本的DOM节点返回
        if(typeof vdom  === 'string' || typeof  vdom ===  'number'){
            return document.createTextNode(vdom);
        }
        // 如果不存在返回空串 undefined null
        if(!vdom){
            return '';
        }
        // 否则就是一个react元素
        let {type,props} = vdom;
        let dom = document.createElement(type); //span div
        updateProps(dom,props); // 把虚拟DOM上的属性设置到真实DOM上
        // 处理子节点 如果子节点就是一个单节点 并且是字符串或数字
        if (typeof props.children === 'string' || typeof props.children === 'number') {
            dom.textContent = props.children; // dom.textContent = 'hello';
        
        // 说明是一个单React元素节点
        } else if (typeof props.children === 'object' && props.children.type) {
            render(props.children,dom);
    
        // 如果儿子是一个数组,说明有多个子节点
        }else if(Array.isArray(props.children)){
            reconcileChildren(props.children,dom);
        }else{ 
            // 如果出现了其他的意外情况
            dom.textContent = props.children ? props.children.toString():'';
        }
        return dom;
    }
    
    /**
     * 把子节点从虚拟DOM全部转换成真实DOM 并插入到父节点中去
     * @param {*} childrenVdom 子节点的虚拟DOM数组
     * @param {*} parentDOM 父节点真实DOM
     */
    function reconcileChildren(childrenVdom,parentDOM){
        childrenVdom.forEach(childVdom => {
            render(childVdom,parentDOM);
        });
    }
    
    /**
     * 把属性对象中的属性设置到DOM元素上
     * @param {*} dom 
     * @param {*} props 
     */
    function updateProps(dom,props){
        for(let key in props){
            if(key === 'children') continue;
            if(key === 'style'){
                let styleObj = props[key];
                for(let key in styleObj){
                    dom.style[key] = styleObj[key]; // dom.style.color = 'red';
                }
            }else{
                dom[key] = props[key]; // dom.className = 'title';
            }
        }
    }
    

    代码仓库


    起源地下载网 » react源码(一):实现createElement和render函数

    常见问题FAQ

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

    发表评论

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

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

    联系作者

    请选择支付方式

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