react-2

作者: 成熟稳重的李先生 | 来源:发表于2020-09-06 12:22 被阅读0次

    用jsx写的代码,babel会将其转化为react的虚拟dom,如下图:

    image.png
    可以看到,转化后,是用React.createElement方法来生成dom的,它大约有3(更多)个参数: type,props, childrenprops是这个元素的详情,children可以有多个,代表这个元素的子元素, 那么它的执行结果又是什么呢?
    image.png

    上图打印出的对象就是react元素,他是一个类似真实dom的对象,type是标签名,props是其属性,像attribute,class,id等都存在于此,props中还有一个比较特殊的属性children,它是子元素集合(只有一个子元素(即文本元素)时,它的类型为string)。
    接下来,我们就来写一个简易版的react+react-dom

    // 首先,完成react
    function createElement(type, config, ...children) {
      // 将children使用rest参数的方法合并起来
      let props = { ...config, children };
      return { type, props };  // 此时,就生成虚拟元素
    }
    
    export default {
      createElement
    };
    

    接下来是重头戏,react-dom

    function render(element, parent) {
      if (typeof element === "string") {
        //文本元素,直接生成,随后插入父元素
        element = document.createTextNode(element);
      }
      let { type, props } = element; //element是一个虚拟dom对象
      if (typeof type === "string") {
        element = document.createElement(type);
        for (let propName in props) {
          if (propName === "style") {  // 添加样式
            Object.entries(props.style).forEach(([attr, value]) => {
              element.style[attr] = value;
            });
          } else if (propName === "className") {  // 添加class
            element.className = props.className;
          } else if (propName === "children") { 
            props.children.forEach(child => {  // 生成child并且插入父级
              return render(child, element);
            });
          }
        }
      }
      parent.appendChild(element);
    }
    
    export default {
      render
    };
    

    业务代码:

    //index.js
    ...
    let elm = React.createElement(
      "h1",
      { className: "title", style: { backgroundColor: "green" } },
      "hello",
      React.createElement("span", null, "world")
    );
    ReactDOM.render(elm, document.getElementById("root"));
    
    image.png
    正常输出了!
    以上,只是演示了普通的html元素,接下来,再兼容函数组件类组件
    //index.js
    function Welcome(props) { //函数式组件
      return React.createElement(
        "h1",
        { className: "title", style: { backgroundColor: "green" } },
        props.name,
        React.createElement("span", null, props.age)
      );
    }
    // class Welcome extends React.Component {   //类组件
    //   render() {
    //     return React.createElement(
    //       "h1",
    //       { className: "title", style: { backgroundColor: "green" } },
    //       this.props.name,
    //       React.createElement("span", null, this.props.age)
    //     );
    //   }
    // }
    let element = React.createElement(Welcome, { name: "lc", age: 18 });
    ReactDOM.render(element, document.getElementById("root"));
    

    修改react/index.js,以支持类组件

    function createElement(type, config, ...children) {
      let props = { ...config, children };
      let element = { type, props };
      return element;
    }
    
    class Component {
      static isReactComponent = true;
      constructor(props) {
        this.props = props;
      }
    }
    
    export default {
      createElement,
      Component,
    };
    

    再来修改react-dom,以支持解析函数式和类组件

    //  react-dom/index.js
    function render(element, parent) {
      let { type, props } = element; //element是一个虚拟dom对象
      if (typeof element === "string" || typeof element === "number") { //文本元素
        //文本元素,直接生成,随后插入父元素
        element = document.createTextNode(element);
      } else if (type.isReactComponent) { 
        //类组件
        element = new type(props).render();
        type = element.type;
        props = element.props;
      } else if (typeof type === "function") {  // 函数组件
        element = type(props);
        type = element.type;
        props = element.props;
      }
      if (typeof type === "string") {  // 
        element = document.createElement(type);
        for (let propName in props) {
          if (propName === "style") {
            Object.entries(props.style).forEach(([attr, value]) => {
              element.style[attr] = value;
            });
          } else if (propName === "className") {
            element.className = props.className;
          } else if (propName === "children") {
            props.children.forEach(child => {
              return render(child, element);
            });
          }else{
              dom.setAttribute(propName, props[propName])
          }
        }
      }
      parent.appendChild(element);
    }
    
    export default {
      render
    };
    

    相关文章

      网友评论

          本文标题:react-2

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