【like-react】手寫一個類似 react 的框架

vxee發表於2019-06-15

平時寫寫 react,卻不瞭解內部是怎麼把 jsx 轉化為 vdom,然後渲染在介面上,以及當資料流更新時,檢視又是怎麼更新的呢。

 

於是我查閱了大量資料後,自己手寫了一個簡單版的 react,從中大概能瞭解到 react 基本的執行機制。

 

react 一個很方便之處是我們可以像寫原生 html 那樣寫元件,這就是 jsx 語法,那麼 jsx 是如何轉化為 dom 的呢。首先通過 babel 語法樹解析轉化成為 vdom,它的結構大概是

{
    type: 'div',
    props: { id: 'container' },
    children: ['xxxxx']
}  

之後通過 react 內部的 render 方法將 vdom 轉為 dom 節點。

render 方法實現如下:

const _render = (vdom, parent = null) => {
    // custom component 經過 babel 轉義 然後 React.createElement 返回的 vdom.type 型別是 function
    // <p id="label">title</p> vdom = { type: 'p', props: {id: 'label'}, children: ['title']}
    const mount = parent ? (el => parent.appendChild(el)) : (el => el);
    if (typeof vdom === 'string' || typeof vdom === 'number') {
      return mount(document.createTextNode(vdom));
    } if (typeof vdom === 'boolean' || vdom === null) {
      return mount(document.createTextNode(''));
    } if (typeof vdom === 'object' && typeof vdom.type === 'function') {
      return Component.render(vdom, parent);
    } if (typeof vdom === 'object' && typeof vdom.type === 'string') {
      const dom = mount(document.createElement(vdom.type));
      for (const child of [].concat(...vdom.children)) _render(child, dom);
      for (const prop in vdom.props) {
        if (Object.prototype.hasOwnProperty.call(vdom.props, prop)) {
          setAttribute(dom, prop, vdom.props[prop]);
        }
      }
      return dom;
    }
    throw new Error(`Invalid VDOM: ${vdom}.`);
  };

值得一提的是在 ReactDOM.render() 時,首先會遍歷所有節點,然後例項化節點,呼叫 componentWillMount 方法,接著 呼叫內部的 render 將 vdom 轉化為 真實 dom,接受一個 識別符號 key 值,這在更新元件時將會派上用場。緊接著呼叫 componentDidMount 方法。

 

接下來講一下 react 更新 state 時發生了什麼。眾所周知,傳統的比較兩棵樹是否相同的時間複雜度是 O(n^3),而 react 基於一套比較規則將時間複雜度降到了 O(n),這大大提高了計算的時間,提高了渲染的速度。因此 react 在更新狀態時的 patch 方法都做了什麼。其實就是基於 react 的比較演算法:1. 兩棵樹的節點型別都不同時則整棵樹都替換;2.當節點的 key 值相同時則直接遞迴比較所有子節點。

 

詳細實現參見: https://github.com/Vxee/like-react

相關文章