日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

React的createElement和render手寫實現示例_React

作者:顏醬 ? 更新時間: 2022-10-27 編程語言

TL;DR

本文的目標是,手寫實現createElementrender

  • React.createElement實現的本質就是整合參數變成對象,這個對象就是react元素
  • ReactDOM.render實現的本質就是根據react元素(對象)創建真實元素及其屬性和子元素

科普概念

  • JSX 語法 - 就是類似 html 的寫法<h1>顏醬<span>最酷</span></h1>
  • JSX 語法本質 - JSX 本質是語法糖,實際會被 babel 編譯成React.createElement
  • react 元素 - 并不是真實 DOM,實際是React.createElement函數返回的對象,結構大約如下,核心屬性就是type、props、props.children
reactElement = {
  type: 'div',
  props: {
    className: 'title',
    style: { color: '#f69' },
    children: 'world',
  },
};

準備工作

  • 先創建一個 react 項目:
create-react-app react-source
  • 安裝cross-env和修改命令,兼容 React 舊寫法
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"
},
  • 刪除 src 里面所有內容,然后新建index.js
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

先看看怎么相當,在線訪問:

  • 分析參數 - 輸入
  • 第一個參數,始終是最外層元素名
  • 第二個參數,始終是最外層的元素屬性集合null
  • 第三個參數及以后的參數,始終是子元素,沒有子元素的話,就沒有第三個參數
  • 幾個子元素,就多幾個參數,每個子元素的類型是字符串React.createElement()
  • 分析返回值 - 輸出
  • 對象,也叫react元素
  • 屬性 type 是元素名
  • 屬性 props 是對象,里面是元素的屬性集合,props.children 可能是字符串、react元素、數組(單項還是字符串 或 react元素
  • 新建source文件夾,建react.js。輸入和輸出已經明了了,接下來就是將輸入變成輸出。
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容器中,從而自動渲染。

  • 分析參數 - 輸入
  • 第一個參數,可能是react元素、普通字符串、null
  • 第二個參數,掛載的元素
  • 分析返回值 - 無
  • 分析執行的結果 - 顯示第一個參數

render 執行之后,將第一個參數變成真實 DOM,插入到第二個參數掛載元素上,因為掛載元素本身存在于文檔之中,所以掛載操作會觸發渲染,顯示出來。因此,render本質就是掛載

第一個參數變成真實 DOM,本質就是創建元素,增加屬性,增加 children

  • source/react-dom.js
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換成我們寫的。

原文鏈接:https://juejin.cn/post/7137603546819592229

欄目分類
最近更新