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;
  }
};
React: 有状态组件生成真实DOM结点的更多相关文章
- React: 无状态组件生成真实DOM结点
		在上一篇文章中,我们总结并模拟了 JSX 生成真实 DOM 结点的过程,今天接着来介绍一下无状态组件的生成过程. 先以下面一段简单的代码举例: const Greeting = function ({ ... 
- React系列文章:无状态组件生成真实DOM结点
		在上一篇文章中,我们总结并模拟了JSX生成真实DOM结点的过程,今天接着来介绍一下无状态组件的生成过程. 先以下面一段简单的代码举例: const Greeting = function ({name ... 
- React系列文章:JSX生成真实DOM结点
		在上一篇文章中,我们介绍了Babel是如何将JSX代码编译成可执行代码的,随后也实现了一个自己的解析器,模拟了Babel编译的过程. 现在我们再来回顾一下,假定有如下业务代码: const style ... 
- React: JSX生成真实DOM结点
		在上一篇文章中,我们介绍了 Babel 是如何将 JSX 代码编译成可执行代码的,随后也实现了一个自己的解析器,模拟了 Babel 编译的过程. 现在我们再来回顾一下,假定有如下业务代码: const ... 
- react篇章-React State(状态)-组件都是真正隔离的
		<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title&g ... 
- [react] 什么是虚拟dom?虚拟dom比操作原生dom要快吗?虚拟dom是如何转变成真实dom并渲染到页面的?
		壹 ❀ 引 虚拟DOM(Virtual DOM)在前端领域也算是老生常谈的话题了,若你了解过vue或者react一定避不开这个话题,因此虚拟DOM也算是面试中常问的一个点,那么通过本文,你将了解到如下 ... 
- React之父子组件传递和其它一些要点
		react是R系技术栈中最基础同时也是最核心的一环,2年不到获取了62.5k star(截止到目前),足可见其给力程度.下面对一些react日常开发中的注意事项进行罗列. React的组件生命周期 r ... 
- Vue视图渲染原理解析,从构建VNode到生成真实节点树
		前言 在 Vue 核心中除了响应式原理外,视图渲染也是重中之重.我们都知道每次更新数据,都会走视图渲染的逻辑,而这当中牵扯的逻辑也是十分繁琐. 本文主要解析的是初始化视图渲染流程,你将会了解到从挂载组 ... 
- 从DOM操作看Vue&React的前端组件化,顺带补齐React的demo
		前言 接上文:谈谈我对前端组件化中“组件”的理解,顺带写个Vue与React的demo 上次写完博客后,有朋友反应第一内容有点深,看着迷迷糊糊:第二是感觉没什么使用场景,太过业务化,还不如直接写Vue ... 
随机推荐
- 关于System.InvalidOperationException异常
			什么是InvalidOperationException 操作无效异常.当方法调用对对象的当前状态无效时引发的异常. 继承 Object Exception SystemException Inval ... 
- SpreadJS 生成报表
			空了再写个完整的demo吧 //报表控件 输入参数待定 function SpreadObj(response) { var spread = null; //数据列表 var dataArray = ... 
- 【CSP-S膜你考】不怕噩梦 (模拟)
			不怕噩梦 题面 蚊子最近经常做噩梦,然后就会被吓醒.这可不好.. 疯子一直在发愁,然后突然有一天,他发现蚊子其实就是害怕某些事. 如果那些事出现在她的梦里,就会害怕. 我们可以假定那个害怕的事其实是一 ... 
- 利用$a_n$与$S_n$的关系求通项$a_n$
			前言 由\(a_n\)与\(S_n\)的关系求数列\(\{a_n\}\)的通项公式,在求通项公式题型中占有比较大的份额,是一个重要的求解思路和方法.是要求重点掌握的类型. 一.方法依据 二者关系:\( ... 
- 招聘.net高级工程师
			1. 本科及以上学历(必须): 2. 精通.net框架和常见web框架,精通常见设计模式并熟练应用. 3. 扎实的技术功底,有良好的数据结构和算法基础,深入理解面向对象编程思想, 熟悉面向对象的基本设 ... 
- 在GitHub中创建目录
			需求:假定我们需要在 Answer 目录下创建一个目录 [注]GitHub无法单独创建一个空目录,但是可以在创建一个文件的同时创建它的所属目录 1.点击进入所需的目录 Answer 2.点击“Crea ... 
- Docker从入门到实践(3)
			三.安装 Docker Docker 分为 CE 和 EE 两大版本.CE 即社区版(免费,支持周期 7 个月),EE 即企业版,强调安全,付费使用,支持周期 24 个月. Docker CE 分为 ... 
- TCMalloc - 细节
			1,释放速度控制 在将一个Span删除掉的时候,会优先将它加入到normal队列中,这之后会尝试从normal队列中释放一部分同样大小的内存给系统. 释放内存给系统的时候,tcmalloc使用了一个延 ... 
- FusionInsight大数据开发---HDFS应用开发
			HDFS应用开发 HDFS(Dadoop Distributed File System) HDFS概述 高容错性 高吞吐量 大文件存储 HDFS架构包含三部分 Name Node DataNode ... 
- Java中使用OpenSSL生成公钥私钥进行数据加解密
			当前使用的是Linux系统,已经安装OpenSSL软件包. 一.使用OpenSSL来生成私钥和公钥1.执行命令openssl version -a 验证机器上已经安装openssl $ openssl ... 
