1. 程式人生 > >深入Preact原始碼分析(二)virtualDOM如何變為真實dom

深入Preact原始碼分析(二)virtualDOM如何變為真實dom

一個簡單的Preact程式碼如下

// 一個簡單的Preact demo
import { h, render, Component } from 'preact';

class Clock extends Component {
    render() {
        let time = new Date().toLocaleTimeString();
        return <span>{ time }</span>;
    }
}

render(<Clock />, document.body);

呼叫了preact的render方法將virtualDOM渲染到真實dom。

// render.js
import { diff } from './vdom/diff';
export function render(vnode, parent, merge) {
    return diff(merge, vnode, {}, false, parent, false);
}

可見,render方法的第一個引數一個vnode,第二個引數是要掛載到的dom的節點,這裡暫時不考慮第三個引數。而render方法實際上又是
去呼叫/vdom/diff.js下的diff方法

//diff函式的定義
export function diff(dom, vnode, context, mountAll, parent, componentRoot)
{
}

render函式使vnode轉換成真實dom主要進行了以下操作
- render函式實際上呼叫了diff方法,diff方法進而呼叫了idiff。
- idiff方法會返回真實的html。idiff內將vnode分為4大型別進行處理封裝在html
- 然後呼叫diffAttributes,將vnode上的屬性值更新到html domnode的屬性上。(通過setAccessor)
- 初次render時,下面if條件恆為真,所以真實html就這樣被裝進了。

 if (parent && ret.parentNode !== parent) parent.appendChild(ret);

這樣初次的vnode轉化成真實html就完成了

這裡寫圖片描述

tips:在diff中會見到很多的out[ATTR_KEY],這個是用來將dom的attributrs陣列每一項的name value轉化為鍵值對存進 out[ATTR_KEY]。

元件的buildComponentFromNode是怎樣的?

buildComponentFromNode的定義

 /** Apply the Component referenced by a VNode to the DOM.
 *  @param {Element} dom    The DOM node to mutate
 *  @param {VNode} vnode    A Component-referencing VNode
 *  @returns {Element} dom  The created/mutated element
 *  @private
 */
export function buildComponentFromVNode(dom, vnode, context, mountAll) {}
 ```

初次呼叫時 buildComponentFromNode(undefined,vnode,{},false)。因此,初次render時的buildComponentFromVNode內部只是呼叫瞭如下的邏輯(不執行的程式碼去掉了)

 export function buildComponentFromVNode(dom, vnode, context, mountAll) {
    let c = dom && dom._component, // undefined
        originalComponent = c,//undefined
        oldDom = dom,// undefined
        isDirectOwner = c && dom._componentConstructor===vnode.nodeName,//undefined
        props = getNodeProps(vnode);// 這個函式除了一般的props獲取外,還會加上defaultProps。
        c = createComponent(vnode.nodeName, props, context);// 建立元件
        setComponentProps(c, props, SYNC_RENDER, context, mountAll);
        dom = c.base;
    return dom;
}
 ```

元件的建立和後續處理,後面講解