【系列 1】react 是怎么运行的?

1,379 阅读1分钟

react 是怎么运行的?

import React from 'react';
import ReactDOM from 'react-dom';

const App = <div className="title" style={{color:'red'}}>hello world</div>
console.log('App', App)

ReactDOM.render(
  App,
  document.getElementById('root')
);

react 运行的主要3阶段:

1. jsx 转成 js 代码

由于 js 不认识 jsx 代码,所以需要将下面 jsx 语法转化成 js 代码

<div className="title" style={{color:'red'}}>hello world</div>

点击此 jsx to react 复制上述代码进去即可转化成 js 代码, 代码如下:

React.createElement("div", {
  className: "title",
  style: {
    color: 'red'
  }
}, "hello world");

在 create-react-app 项目中,我们通过 react-scripts start 可以运行 react 项目是因为 react-scripts 将 jsx 转化成了 js 代码。

2. js 代码生成 vdom

现在知道为什么没有使用 react 都需要 import React from 'react'; 引入了吧, 因为 jsx 转 js 后的代码会用到 react。

执行如下方法会生成什么呢?

React.createElement("div", {
  className: "title",
  style: {
    color: 'red'
  }
}, "hello world");

会生成如下对象:

{
  '$$typeof': Symbol(react.element), // 标记是 react 节点
  type: 'div',                       // 标签类型
  key: null,
  ref: null,
  props: {                           // 标签的属性
    className: 'title',
    style: { color: 'red' },
    children: 'hello world'
  }
}

该对象就是通过 js 对象的形式描述了一个 html 节点,也就是所谓的 vdom (虚拟dom), 后续 diff 算法就是直接给它进行对比更新dom

3. vdom 转化成 dom 挂载到 #root

ReactDOM.render(
  {
      '$$typeof': Symbol(react.element), // 标记是 react 节点
      type: 'div',                       // 标签类型
      key: null,
      ref: null,
      props: {                           // 标签的属性
        className: 'title',
        style: { color: 'red' },
        children: 'hello world'
      }
  },
  document.getElementById('root')
);

ReactDOM.render 内部主要执行过程如下:

function render(vdom, container) {
    let newDOM = createDOM(vdom);  // 对象转 dom
    container.appendChild(newDOM); // 生成的节点挂载到 root 节点里面
}


function createDOM(vdom) {
  let { type, props } = vdom;
  let dom;
  if (type === REACT_TEXT) {
    dom = document.createTextNode(props.content);
  } else {
    dom = document.createElement(type);
  }
  if (props) {
    updateProps(dom, {}, props);
    if (typeof props.children == "object" && props.children.type) {
      mount(props.children, dom);
    } else if (Array.isArray(props.children)) {
      reconcileChildren(props.children, dom);
    }
  }
  vdom.dom = dom;
  return dom;
}
function updateProps(dom, oldProps={}, newProps={}) {
    for (let key in newProps) {
        if (key === 'children') {
            continue;
        } else if (key === 'style') {
            let styleObj = newProps[key];
            for (let attr in styleObj) {
                dom.style[attr] = styleObj[attr];
            }
        }else {
            dom[key] = newProps[key];
        }
    }
    for(let key in oldProps){
        if(!newProps.hasOwnProperty(key)){
            dom[key] = null;
        }
    }
}
function reconcileChildren(childrenVdom, parentDOM) {
  for (let i = 0; i < childrenVdom.length; i++) {
    let childVdom = childrenVdom[i];
    mount(childVdom, parentDOM);
  }
}

const ReactDOM = { render };

此时打开浏览器我们就看见了 hello world

image.png

本系列代码链接
GitHubgithub.com/shunyue1320…
Giteegitee.com/shunyue/min…