亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解Vue3的虛擬DOM是如何生成的

 更新時間:2023年09月11日 09:10:38   作者:田八  
這篇文章給大家詳細介紹了 Vue3 的虛擬DOM生成規(guī)則,文章通過代碼示例和圖片介紹的非常詳細,具有一定的參考價值,對我們的學習或工作有一定的幫助,需要的朋友可以參考下

h 函數(shù)

在官網(wǎng)上可以看到對h函數(shù)的介紹和函數(shù)簽名;

可以先去看看官網(wǎng)的介紹,然后再來看看源碼的實現(xiàn),傳送門:

h 函數(shù)的實現(xiàn)

還是跟著我們之前的節(jié)奏,可以直接在h函數(shù)調(diào)用上面打上斷點,然后開始調(diào)試進入源碼:

const {h} = Vue;
debugger;
h('div');

直接就這樣進入了h函數(shù)的實現(xiàn),我們來看看h函數(shù)的實現(xiàn):

function h(type, propsOrChildren, children) {
    // 通過參數(shù)數(shù)量來進行重載
    const l = arguments.length;
    // 如果參數(shù)數(shù)量為2,那么就有兩種情況
    if (l === 2) {
        // 如果第二個參數(shù)是對象,并且不是數(shù)組
        if (isObject(propsOrChildren) && !isArray(propsOrChildren)) {
            // 如果第二個參數(shù)是虛擬dom,那么就將第二個參數(shù)作為子節(jié)點進行處理
            if (isVNode(propsOrChildren)) {
                return createVNode(type, null, [propsOrChildren]);
            }
            // 如果第二個參數(shù)是對象,那么就將第二個參數(shù)作為props進行處理
            return createVNode(type, propsOrChildren);
        } else {
            // 如果第二個參數(shù)是數(shù)組,那么就將第二個參數(shù)作為子節(jié)點進行處理
            return createVNode(type, null, propsOrChildren);
        }
    } else {
        // 如果參數(shù)數(shù)量不是2
        if (l > 3) {
            // 并且參數(shù)數(shù)量大于3,那么就將第三個參數(shù)以及后面的參數(shù)作為子節(jié)點進行處理
            children = Array.prototype.slice.call(arguments, 2);
        } else if (l === 3 && isVNode(children)) {
            // 如果參數(shù)數(shù)量等于3,并且第三個參數(shù)是虛擬dom,那么就將第三個參數(shù)作為子節(jié)點進行處理
            children = [children];
        }
        // 最后將第二個參數(shù)作為props,其余的參數(shù)作為子節(jié)點進行處理
        return createVNode(type, propsOrChildren, children);
    }
}

h函數(shù)就是一個重載函數(shù),根據(jù)參數(shù)的不同,會有不同的處理邏輯,其實沒有什么好看的;

它最后將所有的參數(shù)都傳遞給了createVNode函數(shù),也就是核心是createVNode函數(shù);

createVNode 函數(shù)

由于我們上面的示例代碼中,只傳入了一個參數(shù),所以會跳過很多邏輯,簡化后的createVNode函數(shù)如下:

function _createVNode(type, props = null, children = null, patchFlag = 0, dynamicProps = null, isBlockNode = false) {
    // 獲取 shapeFlag
    const shapeFlag = isString(type) ? 1 :
        isSuspense(type) ? 128 :
            isTeleport(type) ? 64 :
                isObject(type) ? 4 :
                    isFunction(type) ? 2 : 0;
    // 如果是一個組件,并且還被設置成響應式的了,則會提示并解包
    if (shapeFlag & 4 && isProxy(type)) {
        type = toRaw(type);
        warn(
            `Vue received a Component which was made a reactive object. This can lead to unnecessary performance overhead, and should be avoided by marking the component with \`markRaw\` or using \`shallowRef\` instead of \`ref\`.`,
            `
Component that was made reactive: `,
            type
        );
    }
    // 最后調(diào)用 createBaseVNode 創(chuàng)建 VNode
    return createBaseVNode(
        type,
        props,
        children,
        patchFlag,
        dynamicProps,
        shapeFlag,
        isBlockNode,
        true
    );
}

這里主要是獲取了shapeFlag,我們上面?zhèn)魅肓艘粋€字符串的div,所以shapeFlag的值為1

這里的shapeFlag其實是一個二進制的值,它的值是由type的類型來決定的,在ts的源碼中有他們的定義:

// packages\shared\src\shapeFlags.ts
export const enum ShapeFlags {
    ELEMENT = 1, // 普通dom元素  二進制:0000 0001  十進制:1
    FUNCTIONAL_COMPONENT = 1 << 1, // 函數(shù)組件  二進制:0000 0010  十進制:2
    STATEFUL_COMPONENT = 1 << 2, // 有狀態(tài)組件  二進制:0000 0100  十進制:4
    TEXT_CHILDREN = 1 << 3, // 文本子節(jié)點  二進制:0000 1000  十進制:8
    ARRAY_CHILDREN = 1 << 4, // 數(shù)組子節(jié)點  二進制:0001 0000  十進制:16
    SLOTS_CHILDREN = 1 << 5, // 插槽  二進制:0010 0000  十進制:32
    TELEPORT = 1 << 6, // TELEPORT組件  二進制:0100 0000  十進制:64
    SUSPENSE = 1 << 7, // SUSPENSE組件  二進制:1000 0000  十進制:128
    COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8, // 沒弄清  二進制:0001 0000 0000  十進制:256
    COMPONENT_KEPT_ALIVE = 1 << 9, // 沒弄清  二進制:0010 0000 0000  十進制:512
    COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT // 普通組件,應該是有狀態(tài)組件和函數(shù)組件的并集
}

這里我們可以驗證一下這些值,寫個demo來看看:

    const {h} = Vue;
// 普通元素
const element = h('div');
console.log('ELEMENT', element.shapeFlag);
// 函數(shù)式組件
const functionalComponent = h(() => h('div'));
console.log('FUNCTIONAL_COMPONENT', functionalComponent.shapeFlag);
// 有狀態(tài)組件
const statefulComponent = h({
    render() {
        return h('div');
    }
});
console.log('STATEFUL_COMPONENT', statefulComponent.shapeFlag);
// 文本子節(jié)點
const textChildren = h('div', 'text');
console.log('TEXT_CHILDREN', textChildren.shapeFlag);
// 數(shù)組子節(jié)點
const arrayChildren = h('div', [h('span'), h('span')]);
console.log('ARRAY_CHILDREN', arrayChildren.shapeFlag);
// 插槽子節(jié)點
const slotsChildren = h({
    render() {
        return h('div', this.$slots.default());
    }
}, null, () => 'slotChildren');
console.log('SLOTS_CHILDREN', slotsChildren.shapeFlag);
// teleport組件
const teleport = h(Vue.Teleport);
console.log('TELEPORT', teleport.shapeFlag);
// suspense組件
const suspense = h(Vue.Suspense);
console.log('SUSPENSE', suspense.shapeFlag);

可以看到的是驗證結(jié)果和我們上面的定義是一致的:

這里的文本子節(jié)點和數(shù)組子節(jié)點的值是917,這里的值是由shapeFlag的值和TEXT_CHILDRENARRAY_CHILDREN的值進行或運算得到,這就要進入到createBaseVNode函數(shù)中去看看了。

createBaseVNode 函數(shù)

這里的createBaseVNode函數(shù)就是定義了VNode的一些屬性,我們拿文本子節(jié)點來做示例看看運行邏輯(刪除不會執(zhí)行的邏輯的簡化版代碼):

function createBaseVNode(type, props = null, children = null, patchFlag = 0, dynamicProps = null, shapeFlag = type === Fragment ? 0 : 1, isBlockNode = false, needFullChildrenNormalization = false) {
    // 定義 vnode
    const vnode = {
        __v_isVNode: true,
        __v_skip: true,
        type,
        props,
        key: props && normalizeKey(props),
        ref: props && normalizeRef(props),
        scopeId: currentScopeId,
        slotScopeIds: null,
        children,
        component: null,
        suspense: null,
        ssContent: null,
        ssFallback: null,
        dirs: null,
        transition: null,
        el: null,
        anchor: null,
        target: null,
        targetAnchor: null,
        staticCount: 0,
        shapeFlag,
        patchFlag,
        dynamicProps,
        dynamicChildren: null,
        appContext: null,
        ctx: currentRenderingInstance
    };
    // 普通節(jié)點固定走這個分支
    if (needFullChildrenNormalization) {
        // 使用 normalizeChildren 處理 children
        normalizeChildren(vnode, children);
    }
    // 最后返回 vnode
    return vnode;
}

這里的代碼并不復雜,就是定義了vnode,然后對children進行了處理,最后返回了vnode;

我們當前測試的文本子節(jié)點,shapeFlag的值為9,這里就是通過normalizeChildren函數(shù)來處理的,我們來看看normalizeChildren函數(shù)的實現(xiàn):

function normalizeChildren(vnode, children) {
    let type = 0;
    const { shapeFlag } = vnode;
    if (children == null) {
        // ...
    } else if (isArray(children)) {
        // ...
    } else if (typeof children === "object") {
        // ...
    } else if (isFunction(children)) {
        // ...
    } else {
        // 走到這里,說明 children 需要被規(guī)范為文本節(jié)點
        // 直接轉(zhuǎn)為字符串
        children = String(children);
        // 如果是 teleport ,子節(jié)點會被標記為 16,也就是數(shù)組節(jié)點
        if (shapeFlag & 64) {
            type = 16;
            // 這里會將 children 轉(zhuǎn)為數(shù)組
            children = [createTextVNode(children)];
        } else {
            // 如果是普通節(jié)點,直接標記為文本節(jié)點,也就是 8
            type = 8;
        }
    }
    // 最后將 children 賦值給 vnode.children
    vnode.children = children;
    // 然后將 type 的值進行或運算,賦值給 vnode.shapeFlag
    vnode.shapeFlag |= type;
}

可以看到這里寫了一堆條件分支,來判斷不同的子節(jié)點類型,最后將children賦值給vnode.children,然后將type的值進行或運算,賦值給vnode.shapeFlag;

或運算會得到什么結(jié)果呢?其實我們完全可以自己嘗試一下:

1 | 8 的結(jié)果是9,這里的1就是ELEMENT,8就是TEXT_CHILDREN,所以最后的結(jié)果就是ELEMENT | TEXT_CHILDREN,也就是9

位運算

這樣做有什么意義呢?其實閱讀了這么長時間的源碼,不難發(fā)現(xiàn)經(jīng)常會出現(xiàn)這樣的代碼:

if (shapeFlag & 8) {
    // ...
}

這里就是一個位運算,這樣寫無疑是增加了閱讀的難度,但是對代碼的性能以及一些邏輯上的判斷是有幫助的;

還是我們剛才的例子,我們來看看ELEMENTTEXT_CHILDREN合并的值是9,ELEMENTARRAY_CHILDREN合并的值是17

我們對它進行一個位運算,看看結(jié)果是什么:

  • ELEMENTTEXT_CHILDREN合并的值,與所以類型進行與運算,結(jié)果如下:

  • ELEMENTARRAY_CHILDREN合并的值,與所有類型進行與運算,結(jié)果如下:

可以看到合并后的值,只會與參與合并的值進行與運算得到的結(jié)果是參與合并的值,這樣就可以通過與運算來判斷shapeFlag的值是否包含某個類型;

而將這個過程進行二進制來描述,就是這樣的:

# 這是 ELEMENT 和 TEXT_CHILDREN 合并的值
0000 1001
# 這是 ELEMENT 的值
0000 0001
# 進行與運算
0000 1001
&&&& &&&&
0000 0001
= = = = =
0000 0001

通過上面的例子,其實與運算就是將兩個值的二進制中的相同位置的值進行比較,如果都是1,那么結(jié)果就是1,否則就是0;

Vue將每個節(jié)點的類型都定義成了2的n次方,這樣就可以避免會出現(xiàn)相同位置的1,這樣在進行或運算的時候,就可以將所有的類型進行合并,從而產(chǎn)生一個新的值;

如果是相同類型的節(jié)點,那么shapeFlag的值就是相同的,在進行或運算的時候會得到相同的值,新值和原來的值是相同的,因為本身就包含了這個類型;

這樣新值就會包含所有參與合并的值的類型,就可以通過與運算來判斷shapeFlag的值是否包含某個類型,設計非常的巧妙;

總結(jié)

這一篇主要學習了vnode的擦創(chuàng)建過程,其實一個vnode就是一個js對象,本身并沒有什么特殊的;

特殊的是這個vnode自帶的屬性,例如這一章詳細介紹的sahpeFlag,這個屬性就是通過位運算來進行合并的,這樣就可以通過與運算來判斷shapeFlag的值是否包含某個類型;

而一個vnode中并不是只有一個shapeFlag屬性,還有很多其他的屬性,例如我們傳入的props、children、slot等等;

這些屬性在Vue的整個系統(tǒng)中又是如何使用的呢?這些將會在我們繼續(xù)深入源碼的過程中一一揭曉;

以上就是詳解Vue3的虛擬DOM是如何生成的的詳細內(nèi)容,更多關于Vue3虛擬DOM生成的資料請關注腳本之家其它相關文章!

相關文章

  • VueRouter?原理解讀之初始化流程

    VueRouter?原理解讀之初始化流程

    這篇文章主要為大家介紹了VueRouter原理解讀之初始化流程實例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-05-05
  • IDEA安裝vue插件圖文詳解

    IDEA安裝vue插件圖文詳解

    這篇文章主要為大家詳細介紹了IDEA安裝vue插件圖文,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-09-09
  • vue移動端html5頁面根據(jù)屏幕適配的四種解決方法

    vue移動端html5頁面根據(jù)屏幕適配的四種解決方法

    在vue移動端h5頁面當中,其中適配是經(jīng)常會遇到的問題,這塊主要有四個方法可以適用。這篇文章主要介紹了vue移動端h5頁面根據(jù)屏幕適配的四種方案 ,需要的朋友可以參考下
    2018-10-10
  • 使用vue實現(xiàn)一個電子簽名組件的示例代碼

    使用vue實現(xiàn)一個電子簽名組件的示例代碼

    這篇文章主要介紹了使用vue實現(xiàn)一個電子簽名組件的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-01-01
  • uniapp使用scroll-view下拉刷新無法取消的坑及解決

    uniapp使用scroll-view下拉刷新無法取消的坑及解決

    這篇文章主要介紹了uniapp使用scroll-view下拉刷新無法取消的坑及解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • vue實現(xiàn)商品規(guī)格選擇功能

    vue實現(xiàn)商品規(guī)格選擇功能

    這篇文章主要為大家詳細介紹了vue實現(xiàn)商品規(guī)格選擇,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • vue中使用swiper5方式

    vue中使用swiper5方式

    這篇文章主要介紹了vue中使用swiper5方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • vue項目實現(xiàn)面包屑導航

    vue項目實現(xiàn)面包屑導航

    這篇文章主要為大家詳細介紹了vue項目實現(xiàn)面包屑導航,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • vue數(shù)據(jù)雙向綁定的注意點

    vue數(shù)據(jù)雙向綁定的注意點

    這篇文章主要為大家詳細介紹了vue數(shù)據(jù)雙向綁定的注意點,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • Vue彈窗Dialog最佳使用方案實戰(zhàn)

    Vue彈窗Dialog最佳使用方案實戰(zhàn)

    這篇文章主要為大家介紹了極度舒適的Vue彈窗Dialog最佳使用方案實戰(zhàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-11-11

最新評論