美文网首页
virtual-dom的理解与实现

virtual-dom的理解与实现

作者: 吴晗君 | 来源:发表于2019-06-28 00:34 被阅读0次

    首先,webpack会接住react相关的loader对源代码进行编译,将jsx语法转成js。比如下面的例子。不过我们不会写这些编译后的对象,太不直观了。

    const element = <h1 id='h1' className='h1'><span>哈哈哈</span></h1>
    

    会被转成

    const element = React.createElement(
    "h1",
     {
          id: "h1",
          className: "h1"
        },
     React.createElement("span", null, "\u54C8\u54C8\u54C8")
    ); 
    ReactDOM.render(element, document.getElementById('root'));
    

    这是函数组件

    import React from './VirtualDom/React';
    import ReactDOM from './VirtualDom/ReactDom';
    
    function Element2 () {
      return React.createElement("h1", {
        id: "h1",
        style: {
          color: 'red'
        },
        lassName: "h1"
      }, React.createElement("span", null, "\u54C8\u54C8\u54C8"), "我是函数组件,返回一个用来渲染的对象", React.createElement("div", null, React.createElement("ul", null, React.createElement("li", null, "1"), React.createElement("li", null, "2"), React.createElement("li", null, "3"), React.createElement("li", null, "4"))));
    }
    const Element22 = React.createElement(Element2, {name:'我', age: 100})
    
    ReactDOM.render(Element33, document.getElementById('root'));
    

    这是类组件

    import React from './VirtualDom/React';
    import ReactDOM from './VirtualDom/ReactDom';
    
    class Element3 extends React.Component {
      render () {
        console.log(this.props)
        return React.createElement("h1", {
          id: "h1",
          style: {
            color: 'red'
          },
          lassName: "h1"
        }, React.createElement("span", null, "\u54C8\u54C8\u54C8"), "我是类组件,通过render返回一个用来渲染节点的对象", React.createElement("div", null, React.createElement("ul", null, React.createElement("li", null, "1"), React.createElement("li", null, "2"), React.createElement("li", null, "3"), React.createElement("li", null, "4"))));
      }
    }
    const Element33 = React.createElement(Element3, {name:'我', age: 100})
    
    ReactDOM.render(Element33, document.getElementById('root'));
    

    其实可以看到,基础的就以下几个方法
    react.js

    export default {
      Component,
      createElement
    }
    

    react-dom.js

    // React.js
    export default {
      render
    }
    

    我们将React.createElement方法执行生成的虚拟dom节点打印出来可以看到,就是一个对象,有几个重要的属性

    {
    props: {},
    type:
    }
    

    props会保存我们传入的数据,还有children属性作为它的子节点,children属性的值分为三种:基础类型(比如数字、字符串、null)、react节点(就是个对象)、数组。
    我们来看下实现。

    react.js

    
    const createElement = function (type, options, child) {
      let props = {}
      if (options && typeof options === 'object') {
        // for (let key in options) { // for in 会遍历到可枚举的属性,Object.keys不会。
        //   props[key] = options[key]
        // }
        // 这个存在的问题就是没有深拷贝,会影响原来的属性。
        Object.keys(options).forEach(key => {
          props[key] = options[key]
        })
      }
    
      if (child && arguments.length === 3) {
        props.children = child
      }
    
      if (arguments.length > 3) {
        props.children = Array.from(arguments).slice(2)
      }
      
      return {
        props,
        type
      }
    }
    
    
    class Component {
      static isReactComponent = true
      constructor (props) {
        this.props = props
      }
    }
    
    export default {
      createElement,
      Component
    }
    

    react-dom.js

    const render = (virtualDom, parentNode) => {
      let childNode
      const props = virtualDom.props
      const type = virtualDom.type
      if (type && type.isReactComponent) {
        return render(new type(props).render(), parentNode)
      }
    
      if (typeof type === 'function') {
        return render(type(props), parentNode)
      }
    
      if (typeof virtualDom === 'string' || typeof virtualDom === 'number') {
        childNode = document.createTextNode(virtualDom)
      }
    
      if (typeof virtualDom.type === 'string') {
        childNode = document.createElement(virtualDom.type)
      }
     
      if (virtualDom && virtualDom.props && typeof virtualDom.props === 'object') {
        const props = virtualDom.props
        Object.keys(props).forEach((key) => {
          if (key === 'style') {
            const styleObj = props[key]
            const cssText = Object.keys(styleObj).map(styleName => {
              return `${styleName.replace(/[A-Z]/g, (childStr) => {
                return '-' + childStr.toLowerCase()
              })}:${styleObj[styleName]}`
            }).join(';');
            childNode.setAttribute(key, cssText) // 或者childNode.style.cssText = cssText
          } else if (key === 'children') {
            const children = Array.isArray(props.children) ? props.children : [props.children]
            children.forEach(child => {
              render(child, childNode)
            })
          } else if (key === 'className') {
            childNode.className = props.className
          } else {
            childNode.setAttribute(key, props[key])
          }
        })
      }
      parentNode.appendChild(childNode)
    }
    
    export default {
      render
    }
    

    做的事情其实就是将数据渲染到节点上面。符合react的介绍:构建用户界面的 JavaScript 库。

    注意点

    1. 处理style属性的时候记得将驼峰转换成横杠,str.replace的用法
    2. 为什么会将其他属性挂载在dom节点上呢?感觉没什么意义。真实项目中貌似没有看到,区别在哪里?

    相关文章

      网友评论

          本文标题:virtual-dom的理解与实现

          本文链接:https://www.haomeiwen.com/subject/exljcctx.html