美文网首页
手动实现react.js

手动实现react.js

作者: key君 | 来源:发表于2019-11-09 19:37 被阅读0次
    index.js

    引入了自定义的React和ReactDOM,定义了jsx模板,里面有class组件(继承自定义的React.Component),原生标签和function组件,使用了自定义ReactDOM.render方法渲染模板

    kReact/index.js

    引入了自定义Component,实现createElement方法,根据传入的type判断是什么节点,并保存children到props上

    react-dom.js

    把传入的虚拟节点转化为真实节点 append到容器上

    virtual-dom.js

    根据传入的虚拟节点,取出他们各自的类型 用不同的方法生成真实dom

    Component.js

    定义了Component class保存传入的props 增加属性isReactComponent以区分是class组件还是function组件 实现了setState,setState内部执行forceUpdate方法

    diff.js

    把传入的新虚拟节点转化为真实节点 替换旧的节点

    index.js

    // import React, { Component } from "react";
    // import ReactDOM from "react-dom";
    
    // import React from "./kkreact/";
    // import ReactDOM from "./kkreact/ReactDOM";
    
    import React from "./kReact";
    import ReactDOM from "./kReact/react-dom";
    
    import "./index.css";
    
    class ClassCpm extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          counter: 0,
        };
      }
      handle = () => {
        this.setState({
          counter: this.state.counter + 1,
        });
        console.log("handle", this.state.counter);
      };
      render() {
        return (
          <div className="border">
            {this.props.name}
            <button onClick={this.handle}>{this.state.counter}</button>
            {[0, 1, 2].map(item => {
              return <FuncCmp key={item} name={"function组件" + item} />;
            })}
          </div>
        );
      }
    }
    
    function FuncCmp(props) {
      return <div className="border">{props.name}</div>;
    }
    
    let jsx = (
      <div className="box border">
        <p className="border">这是React</p>
        <FuncCmp name="function组件" />
        <ClassCpm name="class组件" />
      </div>
    );
    
    ReactDOM.render(jsx, document.getElementById("root"));
    
    

    index.css

    body {
      margin: 20px;
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
        "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
        sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
    }
    
    .border {
      margin: 10px;
      padding: 10px;
      border: solid 1px red;
    }
    
    

    src/kReact/index.js

    import Component from "./Component";
    
    function createElement(type, props, ...children) {
      // console.log("arg", arguments);
      props.children = children;
      let vtype; //如果是文本节点, 就是undefined
      if (typeof type === "string") {
        //原生标签
        vtype = 1;
      } else if (typeof type === "function") {
        //class: 2, function: 3
        vtype = type.isReactComponent ? 2 : 3;
      }
      return {
        vtype,
        type,
        props,
      };
    }
    
    export default {
      createElement,
      Component,
    };
    
    

    src/kReact/react-dom.js

    import { initVnode } from "./virtual-dom";
    
    function render(vnode, container) {
      //vnode->node
      const node = initVnode(vnode, container);
      container.appendChild(node);
    }
    
    export default {
      render,
    };
    
    

    src/kReact/Component.js

    import { diff } from "./diff";
    
    class Component {
      static isReactComponent = {};
      constructor(props) {
        this.props = props;
        this.$cache = {};
        this.state = {};
      }
      setState = (nextState, callback) => {
        ///这是个假的setState
        this.state = {
          ...this.state,
          ...nextState,
        };
        this.forceUpdate();
      };
      forceUpdate = () => {
        console.log("setState");
        let newVnode = this.render();
        const newNode = diff(this.$cache, newVnode);
        //vnode newVnode ->node
        this.$cache = {
          ...this.$cache,
          vnode: newVnode,
          node: newNode,
        };
      };
    }
    
    export default Component;
    
    

    src/kReact/virtual-dom.js

    export function initVnode(vnode, container) {
      //vnode->node
      const { vtype } = vnode;
      let node;
      if (!vtype) {
        node = initTxtNode(vnode, container);
      }
      if (vtype === 1) {
        //原生标签
        node = initHtmlNode(vnode, container);
      }
      if (vtype === 2) {
        //class组件
        node = initClassNode(vnode, container);
      }
      if (vtype === 3) {
        //function组件
        node = initFunctionNode(vnode, container);
      }
      return node;
    }
    
    function initTxtNode(vnode, container) {
      const node = document.createTextNode(vnode);
      return node;
    }
    
    //原生vnode->node
    function initHtmlNode(vnode, container) {
      const { type, props } = vnode;
      const node = document.createElement(type);
      const { children, ...rest } = props;
      children.map(item => {
        if (Array.isArray(item)) {
          item.map(c => {
            node.appendChild(initVnode(c, node));
          });
        } else {
          node.appendChild(initVnode(item, node));
        }
      });
      for (let key in rest) {
        if (key === "className") {
          node.setAttribute("class", rest[key]);
        } else if (key.slice(0, 2) === "on") {
          node.addEventListener("click", rest[key]);
        }
      }
      return node;
    }
    
    function initFunctionNode(vnode, container) {
      const { type, props } = vnode;
      const node = type(props); //vnode
      return initVnode(node, container);
    }
    
    function initClassNode(vnode, container) {
      const { type, props } = vnode;
      const componet = new type(props);
      const vvnode = componet.render(); //vnode
      const node = initVnode(vvnode, container);
      componet.$cache = {
        vnode: vvnode,
        node,
        parentNode: container,
      };
      return node;
    }
    
    

    src/kReact/diff.js

    import { initVnode } from "./virtual-dom";
    
    //这不是diff
    export function diff(cache, newVnode) {
      console.log("new", newVnode);
      const { vnode, node, parentNode } = cache;
      const newNode = initVnode(newVnode, parentNode);
      console.log("con", parentNode);
      parentNode.replaceChild(newNode, node);
      return newNode;
    }
    
    

    相关文章

      网友评论

          本文标题:手动实现react.js

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