亲宝软件园·资讯

展开

React createElement render

颜酱 人气:0

TL;DR

本文的目标是,手写实现createElementrender

科普概念

reactElement = {
  type: 'div',
  props: {
    className: 'title',
    style: { color: '#f69' },
    children: 'world',
  },
};

准备工作

create-react-app react-source
yarn add cross-env

修改package.json的命令:

"scripts": {
  "start": "cross-env DISABLE_NEW_JSX_TRANSFORM=true react-scripts start",
  "build": "cross-env DISABLE_NEW_JSX_TRANSFORM=true react-scripts build"
},
import React from 'react';
import ReactDOM from 'react-dom';
const reactElement_text = 'only text';
const reactElement_oneNode = (
  <div className="title" style={{ color: '#f69' }}>
    one node
  </div>
);
const reactElement_oneChildNode = (
  <div>
    <span>one child node</span>
  </div>
);
const reactElement_multipleNode = (
  <div>
    <span>first child</span>
    text child
  </div>
);
// only text
console.log(reactElement_text);
// { type: 'div', props: { className: 'title', style: { color: '#f69' }, children: 'one node' }, };
console.log(reactElement_oneNode);
// { type: 'div', props: { children: { type: 'span', props: { children: 'one child node' }, }, }, };
console.log(reactElement_oneChildNode);
// { type: 'div', props: { children: [ { type: 'span', props: { children: 'first child node' } }, 'text', ], }, };
console.log(reactElement_multipleNode);
ReactDOM.render(reactElement_multipleNode, document.getElementById('root'));

留心看,props.children的结构,有三种类型:字符串、对象、数组。

npm start

(^▽^),就可以看到http://localhost:3000/了!

实现 createElement

JSX 语法相当于React.createElement,那么先实现createElement

先看看怎么相当,在线访问

function createElement(type, config, ...children) {
  console.log(type, config, ...children)
  // children分 0个 单个 多个
  const isNoneChildren = children.length === 0;
  const isOneChildren = children.length === 1;
  children = isNoneChildren
    ? null
    : isOneChildren
    ? children[0]
    : children;
  const res = {
    type,
    props: {
      ...config,
      children,
    },
  };
  return res;
}
const React = {
  createElement,
};
export default React;

实现 render

render说白了就是将React元素转化成真实DOM,插入到root容器中,从而自动渲染。

render 执行之后,将第一个参数变成真实 DOM,插入到第二个参数挂载元素上,因为挂载元素本身存在于文档之中,所以挂载操作会触发渲染,显示出来。因此,render本质就是挂载

第一个参数变成真实 DOM,本质就是创建元素,增加属性,增加 children

function render(vdom, container) {
  // 本质就是挂载,因container本身存在于文档之中,所以挂载操作会触发渲染
  mount(vdom, container);
}
function mount(vdom, container) {
  // 将第一个参数变成真实DOM,插入到第二个参数挂载元素上
  const DOM = createDOM(vdom);
  container.append(DOM);
}
function createDOM(vdom) {
  const isTextNode = typeof vdom === 'string' || vdom == null;
  if (isTextNode) return document.createTextNode(vdom || '');
  const isElementNode = typeof vdom === 'object';
  if (isElementNode) return createElementDOM(vdom);
  function createElementDOM(vdom) {
    const { type, props } = vdom;
    let DOM = document.createElement(type);
    if (props) {
      updateProps(DOM, props);
      const { children } = props;
      children && updateChildren(DOM, children);
    }
    return DOM;
  }
}
function updateProps(DOM, props) {
  console.log(DOM, props);
  // 正常遍历就好,特殊的特殊处理
  for (const key in props) {
    if (key === 'children') continue;
    if (key === 'style') {
      updateStyle(DOM, props[key]);
      continue;
    }
    DOM[key] = props[key];
  }
  function updateStyle(DOM, styleObj) {
    for (const key in styleObj) {
      DOM.style[key] = styleObj[key];
    }
  }
}
function updateChildren(DOM, children) {
  // 单个节点,直接插入(挂载)到DOM上; 多个节点,遍历插入
  const isOneChildren = !Array.isArray(children);
  isOneChildren
    ? mount(children, DOM)
    : children.forEach((child) => mount(child, DOM));
}
const ReactDOM = {
  render,
};
export default ReactDOM;

测试

index.js里面的reactreact-dom换成我们写的。

加载全部内容

相关教程
猜你喜欢
用户评论