• React: 有状态组件生成真实DOM结点


    上次我们分析了无状态组件生成 DOM 的过程,无状态组件其实就是纯函数,它不维护内部的状态,只是根据外部输入,输出一份视图数据。而今天我们介绍的有状态组件,它有内部的状态,因此在组件的内部,可以自行对状态进行更改,进而渲染出新的视图。下面我们就来分析有状态组件生成真实 DOM 结点的过程。

    我们先来写的一个 Greeting 组件,每次点击问候按钮,文字部分会更新问候的次数:

    class Greeting extends React.Component {
      constructor() {
        super();
    
        this.state = {
          count: 0,
        };
      }
    
      componentDidMount() {
        console.log('did mount');
      }
    
      greet = () => {
        let {count} = this.state;
    
        this.setState({
          count: ++count,
        });
      };
    
      render() {
        let {name} = this.props;
    
        return (
          <div className="container">
            <div>hello {name} {this.state.count} times</div>
            <button onClick={this.greet}>greet</button>
          </div>
        )
      }
    }
    
    const App = <Greeting name="scott"/>;
    
    console.log(App);
    
    ReactDOM.render(App, document.getElementById('root'));
    

    编译之后的代码如下:

    // 自执行函数变量 _createClass实际上是用来定义props的
    var _createClass = function () {
      
      // 定义属性 props是数组类型 [{key, val}]
      function defineProperties(target, props) {
        for (var i = 0; i < props.length; i++) {
          var descriptor = props[i];
    
          descriptor.enumerable = descriptor.enumerable || false;
          descriptor.configurable = true;
    
          if ("value" in descriptor) {
            descriptor.writable = true;
          }
    
          Object.defineProperty(target, descriptor.key, descriptor);
        }
      }
    
      return function (Constructor, protoProps, staticProps) {
        // 定义原型props
        if (protoProps) {
          defineProperties(Constructor.prototype, protoProps);
        }
    
        // 定义静态props
        if (staticProps) {
          defineProperties(Constructor, staticProps);
        }
    
        return Constructor;
      };
    
    }();
    
    function _possibleConstructorReturn(self, call) {
      return call && (typeof call === "object" || typeof call === "function") ? call : self;
    }
    
    // 继承
    function _inherits(subClass, superClass) {
      
      // 使用Object.create(prototype, {constructor})来实现继承
      subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {
          value: subClass,
          enumerable: false,
          writable: true,
          configurable: true
        }
      });
    
      if (superClass) {
        Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
      }
    }
    
    // 自执行函数变量 表示用户自定义组件
    var Greeting = function (_React$Component) {
      
      // 自定义Greeting组件
      function Greeting() {
        var _this = _possibleConstructorReturn(this, (Greeting.__proto__ || Object.getPrototypeOf(Greeting)).call(this));
    
        // 组件内部state
        _this.state = {
          count: 1
        };
    
        // 组件内部greet方法
        _this.greet = function () {
          var count = _this.state.count;
    
          _this.setState({
            count: ++count
          });
        };
    
        return _this;
      }
    
      // 继承ReactComponent
      _inherits(Greeting, _React$Component);
    
      // 给Greeting定义生命周期方法
      _createClass(Greeting, [
        {
          key: "componentDidMount",
          value: function componentDidMount() {
            console.log('did mount');
          }
        },
        {
          key: "render",
          value: function render() {
            var name = this.props.name;
    
            return React.createElement(
              "div",
              { className: "container" },
              React.createElement(
                "div",
                null,
                "hello ",
                name,
                " ",
                this.state.count,
                " times"
              ),
              React.createElement(
                "button",
                { onClick: this.greet },
                "greet"
              )
            );
          }
        }
      ]);
    
      return Greeting;
    
    }(React.Component);
    
    var App = React.createElement(Greeting, { name: "scott" });
    
    console.log(App);
    
    ReactDOM.render(App, document.getElementById('root'));
    

    模拟组件渲染:

    const React = {
      // 创建DOM描述对象 即虚拟DOM
      createElement(type, props, ...children) {
        let propsChildren = children;
    
        // 组件参数的props.children本身是数组
        // 所以调用组件函数时这里需要特殊处理
        if (Array.isArray(children[0])) {
          propsChildren = children[0];
        }
    
        // 结点
        let vnode = {
          type,
          props: {
            ...props,
            children: propsChildren,
          }
        };
    
        // 挂载组件函数体的虚拟DOM
        if (typeof type === 'function') {
          let componentProps = {
            ...props,
            children,
          };
    
          // 有状态组件
          if (type.prototype && type.prototype.render) {
            let component = new type();
            
            component.props = componentProps;
            component.vnode = vnode;
    
            vnode.body = component.render();
          }
    
          // 无状态组件
          else {
            vnode.body = type(componentProps);
          }
        }
    
        return vnode;
      }
    };
    
    // ReactComponent基类
    function ReactComponent(props) {}
    
    // 实现setState方法
    ReactComponent.prototype.setState = function (partialSate) {
      Object.assign(this.state, partialSate);
      
      let oldDom = this.vnode.dom;
      let newDom = ReactDOM.generateDOM(this.render());
    
      this.vnode.dom = newDom;
    
      // 替换DOM结点
      oldDom.parentNode.replaceChild(newDom, oldDom);
    }
    
    // 模拟React.Component基类
    React.Component = ReactComponent;
    
    const ReactDOM = {
      // 渲染真实DOM
      render(vnode, container) {
        container.appendChild(this.generateDOM(vnode));
      },
      // 获取真实DOM结点
      generateDOM(vnode) {
        if (typeof vnode.type === 'function') {
          // 将组件函数体的虚拟DOM生成真实DOM
          let elem = this.generateDOM(vnode.body);
    
          vnode.dom = elem;
    
          return elem;
        }
    
        let elem = document.createElement(vnode.type);
    
        vnode.dom = elem;
    
        // 特殊key值映射
        let specialKeyMap = {
          className: 'class',
          fontSize: 'font-size',
        };
        let {props} = vnode;
    
        // 设置DOM属性
        props && Object.keys(props).forEach(key => {
          if (key === 'children') {
            // 处理子节点
            props.children.forEach(child => {
              if (['string', 'number'].includes(typeof child)) {
                // 纯内容节点
                elem.appendChild(document.createTextNode(child));
              } else {
                // DOM节点
                elem.appendChild(this.generateDOM(child));
              }
            });
          } else if (key === 'style') {
            // 设置样式属性
            let styleObj = props.style;
            let styleItems = [];
    
            Object.keys(styleObj).forEach(styleKey => {
              styleItems.push(`${specialKeyMap[styleKey] || styleKey}:${styleObj[styleKey]}`);
            });
    
            elem.setAttribute('style', styleItems.join(';'));
          } else if (['onClick'].includes(key)) {
            let eventName = key.replace(/^on/, '').toLowerCase();
    
            // 绑定事件
            elem.addEventListener(eventName, function () {
              props[key]();
            });
          } else {
            // 设置其他属性
            elem.setAttribute(specialKeyMap[key] || key, props[key]);
          }
        });
    
        return elem;
      }
    };
    
  • 相关阅读:
    CentOS 7 安装 Nginx (LNMP环境搭建第一步)
    JS去除字符串最后的逗号
    mkdir如何创建多级目录
    php实现获取数组中相同/不相同的元素
    git 查看远程仓库地址
    windows git pull或者push或者clone代码时弹出安全框解决办法
    【laravel】api路由无法访问
    PHP开发api接口安全验证
    PHP根据身份证号码,获取性别、获取生日、计算年龄等多个信息
    PHP按一定比例压缩图片
  • 原文地址:https://www.cnblogs.com/liuhe688/p/10929080.html
Copyright © 2020-2023  润新知