• react系列一,react虚拟dom如何转成真实的dom


    react,想必作为前端开发一定不陌生,组件化以及虚拟dom使得react成为最受欢迎额前端框架之一。我们知道react是基于虚拟dom的,但是什么是虚拟dom呢,其实就是一组js对象,那么我们今天就来认识什么是虚拟dom,以及如何转成真实的dom结构,完整的 简易版react  在个人github,实现了diff算法,组件渲染,组件更新,钩子函数。

    一.认识虚拟dom

    首先我们看如下代码

    const title = <h1 className="title">Hello, world!</h1>;
    

    这并不是合法的js代码,它是一种被称为jsx的语法扩展,通过它我们就可以很方便的在js代码中书写html片段。

    本质上,jsx是语法糖,上面这段代码会被babel转换成如下代码

    我们下载插件 babel-plugin-transform-react-jsx,并且配置.babelrc文件

    {
        "presets": ["env"],
        "plugins": [
            ["transform-react-jsx", {
                "pragma": "React.createElement"//大部分框架喜欢改成h
            }]
        ]
    }

    于是页面中的jsx就会被babel转成如下的结构

    const title = React.createElement(
        'h1',
        { className: 'title' },
        'Hello, world!'
    );
    

    可以看出babel已经把一个dom元素分解成标签名称h1,属性集合对象,以及内部子节点(这里是hello world文本节点),我们首先修改这个方法,为了转成我们需要的结构

    function createElement( tag, attrs, ...children ) {
        return {
            tag,
            attrs,
            children
        }
    }
    // 将上文定义的createElement方法放到对象React中
    const React = {
        createElement,
    }
    

    函数的参数...children使用了ES6的rest参数,它的作用是将后面child1,child2等参数合并成一个数组children。

    现在我们来试试调用它,一下结构都是babel自动调用React.createElement给我们转成的,当然你也可以自己写方法将真实的dom转为js对象

    const element = (
        <div>
            hello<span>world!</span>
        </div>
    );
    console.log( element );
    

      

    二.将上列的虚拟dom结构转成真实的dom

    1.如果遇到文本节点则直接返回新建的文本节点

    //处理文本节点
        if( typeof vnode === 'string'){
            const textNode = document.createTextNode( vnode )
            return textNode;
        }
    

    2.处理普通的元素

    //普通的dom
        const dom = document.createElement( vnode.tag );
        if( vnode.attrs ){
            Object.keys( vnode.attrs ).forEach( key => {
                const value = vnode.attrs[ key ];
                setAttribute( dom, key, value );    // 设置属性
            } );
        }
        vnode.children.forEach( child => render( child, dom ) );    // 递归渲染子节点
        return dom ;    // 返回虚拟dom为真正的DOM
    

    3.遇到普通元素的属性,需要这是属性节点,但是分为两种,一种是普通的属性,比如className,另一种是方法绑定,比如是onClick

    function setAttribute( dom, name, value ) {
        // 如果属性名是className,则改回class
        if ( name === 'className' ) name = 'class';
    
        // 如果属性名是onXXX,则是一个事件监听方法
        if ( /onw+/.test( name ) ) {
            name = name.toLowerCase();
            dom[ name ] = value || '';
        // 如果属性名是style,则更新style对象
        } else if ( name === 'style' ) {
            if ( !value || typeof value === 'string' ) {
                dom.style.cssText = value || '';
            } else if ( value && typeof value === 'object' ) {
                for ( let name in value ) {
                    // 可以通过style={  20 }这种形式来设置样式,可以省略掉单位px
                    dom.style[ name ] = typeof value[ name ] === 'number' ? value[ name ] + 'px' : value[ name ];
                }
            }
        // 普通属性则直接更新属性
        } else {
            if ( name in dom ) {
                dom[ name ] = value || '';
            }
            if ( value ) {
                dom.setAttribute( name, value );
            } else {
                dom.removeAttribute( name, value );
            }
        }
    }

    三.查看完整的代码

    function render ( vnode, container ){
        return container.appendChild( _render( vnode ) );
    }
    function _render( vnode ){
        if ( typeof vnode === 'number' ) {
            vnode = String( vnode );
        }
        //处理文本节点
        if( typeof vnode === 'string'){
            const textNode = document.createTextNode( vnode )
            return textNode;
        }
        //处理组件
        if ( typeof vnode.tag === 'function' ) {
            const component = createComponent( vnode.tag, vnode.attrs );
            setComponentProps( component, vnode.attrs );
            return component.base;
        }
        //普通的dom
        const dom = document.createElement( vnode.tag );
        if( vnode.attrs ){
            Object.keys( vnode.attrs ).forEach( key => {
                const value = vnode.attrs[ key ];
                setAttribute( dom, key, value );    // 设置属性
            } );
        }
        vnode.children.forEach( child => render( child, dom ) );    // 递归渲染子节点
        return dom ;    // 返回虚拟dom为真正的DOM
    }
    //实现dom挂载到页面某个元素
    const ReactDOM = {
        render: ( vnode, container ) => {
            container.innerHTML = '';
            return render( vnode, container );
        }
    }

    现在我们已经实现将虚拟dom转为真实的dom,已经绑定属性,我们现在来像react一样调用这个方法

    const element = (
        <div>
            hello<span>world!</span>
        </div>
    );
    
    ReactDOM.render(
        element,
        document.getElementById( 'main' )
    );

    现在就实现往页面中元素id为main的元素上挂载了该element。

  • 相关阅读:
    Objective-C /iphone开发基础:分类(category,又称类别)
    Objective-C,复合类,Composition
    [置顶] Objective-C开发环境介绍以及Cocoa,以及第一个程序
    链表的创建与插入练习
    c++算法联系,冒泡排序,bubble sort,插入排序,insert sort,
    封装,capsulation,&&继承,Inheritance,&&多态,polymorphism
    inheritance,菱形继承, 虚继承,virtual
    c++中运算符重载,+,-,--,+=,-=,*,/,*=,/=,
    [置顶] 运算符重载,浅拷贝(logical copy) ,vs, 深拷贝(physical copy),三大件(bigthree problem)
    [置顶] c++类的继承(inheritance)
  • 原文地址:https://www.cnblogs.com/zhenfei-jiang/p/9682430.html
Copyright © 2020-2023  润新知