1. 程式人生 > >【Vue原理】VNode

【Vue原理】VNode

如果你覺得排版難看,請點選 下面連結 或者 拉到 下面關注公眾號也可以吧

【Vue原理】VNode - 原始碼版

今天就來探索 VNode 的原始碼,VNode 是 Vue2 渲染機制中很重要的一部分,是深入Vue 必須瞭解的部分

我們以4個問題來開始我們的探索

1、vnode 是什麼及其作用

2、vnode 什麼時候生成

3、vnode 怎麼生成

4、vnode 存放什麼資訊

5、vnode 存放在哪裡

文章很長,看之前值做好準備

公眾號


VNode是什麼及作用

首先,第一個問題已經很爛了,網上有很多相關的內容,為了內容的完整性,所以也放上來哈哈。

VNode 表示 虛擬節點 Virtual DOM,為什麼叫虛擬節點呢,因為不是真的 DOM 節點。

他只是用 javascript 物件來描述真實 DOM,這麼描述,把DOM標籤,屬性,內容都變成 物件的屬性

就像用 JavaScript 物件描述一個人一樣

{sex:'男', name:'神仙朱', salary:5000,children:null}

過程就是,把你的 template 模板 描述成 VNode,然後一系列操作之後通過 VNode 形成真實DOM進行掛載

是什麼?

JavaScript 物件

什麼用?

1、相容性強,不受執行環境的影響。VNode 因為是 JS 物件,不管 Node 還是 瀏覽器,都可以統一操作, 從而獲得了服務端渲染、原生渲染、手寫渲染函式等能力

2、減少操作 DOM。任何頁面的變化,都只使用 VNode 進行操作對比,只需要在最後一步掛載更新DOM,不需要頻繁操作DOM,從而提高頁面效能


VNode怎麼生成

在 Vue 原始碼中,vnode 是通過一個建構函式生成的,建構函式看起來挺簡單的

本來以為很多內容,帶著沉重的心情去探索,然後看到之後就放鬆了下來,看了一會,心情再次沉重了起來

其中涉及的內容還是挺多的....不然哪裡來開篇的那麼多問題

行了,看下 VNode 的建構函式

function VNode(
    tag, data, children, 
    text, elm, context, 
    componentOptions

) {    

    this.tag = tag; // 標籤名

    this.data = data;    

    this.children = children; // 子元素

    this.text = text; // 文字內容

    this.elm = elm; // Dom 節點



    this.context = context;    

    this.componentOptions = componentOptions;    

    this.componentInstance = undefined;    

    this.parent = undefined;    

    this.isStatic = false; // 是否靜態節點

    this.isComment = false; // 是否是註釋節點

    this.isCloned = false; // 是否克隆節點

};

看完上面,先不要糾結都是什麼東西,先來走一遍

比如我們使用 vnode 去描述這樣一個template

<div class="parent" style="height:0" href="2222">
    111111

</div>

使用 VNode 建構函式就可以生成下面的 VNode

{    

    tag: 'div',    

    data: {        

        attrs:{href:"2222"}

        staticClass: "parent",        

        staticStyle: {            

            height: "0"

        }
    },    

    children: [{        

        tag: undefined,        

        text: "111111"

    }]
}

這個 JS 物件,就已經囊括了整個模板的所有資訊,完全可以根據這個物件來構造真實DOM了

至於其中都是什麼意思,請看下個問題


VNode存放什麼資訊

新建一個 vnode 的時候,包含了非常多的屬性,每個屬性都是節點的描述的一部分

我們只撿一些屬性來探索一下,瞭解主體即可

普通屬性

1、data

1、儲存節點的屬性,class,style 等

2、儲存繫結的事件

3、....其他

公眾號

公眾號

2、elm

真實DOM 節點

生成VNode 的時候,並不存在真實 DOM

elm 會在需要建立DOM 時完成賦值,具體函式在 createElm 中

賦值語句就是一句(簡化了原始碼)

公眾號

3、context

渲染這個模板的上下文物件

意思就是,template 裡面的動態資料要從這個 context 中獲取,而 context 就是 Vue 例項

如果是頁面,那麼context 就是本頁面的例項,如果是元件,context則是元件的例項

4 isStatic

是否是靜態節點

當一個節點被標記為靜態節點的時候,說明這個節點可以不用去更新它了,當資料變化的時候,可以忽略去比對他,以提高比對效率

元件相關屬性

1、parent

這個parent 表示是元件的外殼節點

額,什麼是外殼節點,舉個栗子先吧

1、存在這樣一個元件 test

公眾號

2、頁面中使用這個元件

公眾號

誒,到這裡就有意思了,元件其實應有兩種 VNode

公眾號

這兩種VNode 名義上都是對的,都有理,誰是正牌不好說

最後尤大判定第一個 VNode 是 第二個 VNode 的爸爸,也就是外殼節點

公眾號

外殼節點通常是 父元件和 子元件的 關聯,用於儲存一些父元件傳給子元件的資料 等

2 componentInstance

這個顧名思義,就是元件生成的例項,儲存在這裡

上面 test 元件的外殼節點中的 componentInstance

公眾號

3 componentOptions

這個就儲存一些 父子元件 PY 交易的證據

比如 props,事件,slot 什麼的,列印看下

公眾號

公眾號

其中 children 儲存的就是 slot,listeners 儲存 事件,propsData 儲存 props


VNode怎麼生成

在初始化完選項,解析完模板之後,就需要掛載 DOM了。此時就需要生成 VNode,才能根據 VNode 生成 DOM 然後掛載

掛載 DOM 第一步,就是先執行渲染函式,得到整個模板的 VNode

比如有以下渲染函式,執行會返回 VNode,就是 _c 返回的VNode

function (){ 
    with(this){  
        return _c('div',{attrs:{"href":"xxxx"}},["1111"]).
    } 
}

渲染函式會繫結上下文物件,加上 with 的作用,_c 其實就是 vm._c

現在就來看 vm._c 是什麼東西

vm._c = function(a, b, c, d) {    

    return createElement(vm, a, b, c, d, false);

};
function createElement(

    context, tag, data, 

    children, normalizationType

) {    

    var vnode;    

    if (tag是正常html標籤) {

        vnode = new VNode(

            tag, data, children, undefined, 

            undefined, context

        );
    } 
    else if (tag 是元件) {
        vnode = createComponent(

            Ctor, data, context, 

            children, tag

        );

    }    

    return vnode

}

我們可以看到,正常標籤 和 元件會走不同流程

1 、正常標籤

比如有這樣一個正常標籤模板

公眾號

解析成渲染函式如下

function (){    

    with(this){  

        return _c('div',{

            attrs:{"href":"xxxx"}},

            ["1111"]

        )

    }
}

看上面_c 原始碼,可以知道經過 _c 把引數傳導,這樣去構建 VNode

new VNode(tag, data, children, undefined, undefined, context);

公眾號

這樣就儲存了 tag,data,children 和 context

2、元件

比如頁面使用了test元件

公眾號

解析成渲染函式如下

with(this){  
    return _c('div',[
        _c('test',
            {attrs:{"name":name}},
            ["1111"]
        )
    ],1)
}

看上面 _c 程式碼知道 ,_c 會先呼叫 createComponent

createComponent(Ctor, data, context, children, tag);}

公眾號

createComponent 中也會呼叫 VNode 建構函式,生成VNode 並返回

function createComponent(

    Ctor, data, context, 

    children, tag

) {    

    // extractPropsFromVNodeData 作用是把傳入data的 attr 中屬於 props的篩選出來
    var propsData = extractPropsFromVNodeData(data, Ctor, tag);    


    var vnode = new VNode(

        ("vue-component-" + (Ctor.cid) + tag),
        data, undefined, undefined, undefined,

        context, {            

            Ctor: Ctor, 

            // 父元件給子元件繫結的props
            propsData: propsData, 

            // 父元件給子元件繫結的事件
            listeners: listeners, 

            tag: tag,            

            children: children

        });    

    return vnode

}

VNode存放在哪裡

那麼創建出來的 VNode 是否有被存起來,毫無疑問,肯定是要的啊

主要是三個位置存了 vnode,分別是

parent ,_vnode ,$vnode

parent 上面已經說過,就先不提了,剩下兩個全部是掛在 Vue 例項一級屬性上的

列印一下元件的例項,可以很清楚看到這兩個屬性

公眾號

下面來說說這兩個東西

1、_vnode

_vnode 存放表示當前節點的 VNode

什麼叫當前,也就是可以通過這個VNode 直接對映成 當前真實DOM

他的作用是什麼呢?

用來比對更新,比如你的資料變化了,此時會生成一個新的 VNode,然後再拿到儲存的_vnode 對比,就可以得到最小區域,從而只用更新這部分

所以, _vnode 存放的可以說是當前節點,也可以說是舊節點

另外,_vnode 中儲存有一個 parent,這個parent 就是外殼節點,上面說 vnode 的時候已經說過了

在哪裡賦值?

我們來完整地走一遍流程,涉及原始碼很多,但是我已經非常精簡了,大概瞭解個流程

function Vue() {
    ...初始化元件選項等
    mountComponent()

}

function mountComponent() {

    ....解析模板,生成渲染函式
   
    // 用於生成VNode,生成DOM,掛載DOM
    updateComponent = function() {
        vm._update(vm._render());
    };    

    // 新建 watcher,儲存updateComponent為更新函式,新建的時候會立即執行一遍
    new Watcher(vm, updateComponent)
}

function Watcher(vm, expOrFn) {    

    this.getter = expOrFn ;    

    this.getter()

}

// 執行前面解析得到的渲染函式,返回生成的 VNode
Vue.prototype._render = () {}

// 根據vnode,生成DOM 掛載
Vue.prototype._update = function(vnode) {    

    var prevVnode = vm._vnode;

    vm._vnode = vnode;    

    if (不存在舊節點) { ...使用vnode建立DOM並直接掛載 }    

    else { ...存在舊節點,開始比對舊節點和新節點,然後建立DOM並掛載 }

}

2、$vnode

$vnode 只有元件例項才有,因為 $vnode 存放的是外殼節點,頁面例項中是不存在 $vnode 的

本來也想走下流程的,無奈兜兜轉轉太多,涉及原始碼更多

在哪裡進行賦值?

我就放最後一步 updateChildComponent

updateChildComponent 會在上個 _vnode 提到的 vm._update 執行流程中呼叫

function updateChildComponent(
    vm, parentVnode

) {

    vm.$options._parentVnode = parentVnode;
    vm.$vnode = parentVnode; 
    if (vm._vnode) {
        vm._vnode.parent = parentVnode;
    }
}

最後

鑑於本人能力有限,難免會有疏漏錯誤的地方,請大家多多包涵,如果有任何描述不當的地方,歡迎後臺聯絡本人,有重謝

相關推薦

Vue原理VNode

如果你覺得排版難看,請點選 下面連結 或者 拉到 下面關注公眾號也可以吧 【Vue原理】VNode - 原始碼版 今天就來探索 VNode 的原始碼,VNode 是 Vue2 渲染機制中很重要的一部分,是深入Vue 必須瞭解的部分 我們以4個問題來開始我們的探索 1、vnode 是什麼及其作用

Vue原理Watch

如果你覺得排版難看,請點選 下面連結 或者 拉到 下面關注公眾號也可以吧 【Vue原理】Watch - 原始碼版 今天繼續探索 Watch 原始碼,廢話不多說了 帶著我的幾個疑問開始 1、什麼時候初始化 2、怎麼確定監聽哪些值 3、深度監聽怎麼回事 4、怎麼觸發我的函式 這些問題的答案會摻

Vue原理Props

如果你覺得排版難看,請點選 下面連結 或者 拉到 下面關注公眾號也可以吧 【Vue原理】Props - 原始碼版 今天記錄 Props 原始碼流程,哎,這東西,就算是研究過了,也真是會隨著時間慢慢忘記的。 幸好我做了詳細的文章,忘記了什麼的,回憶起來必然是很快的。 好的,回到正題,Props 請你

Vue原理Mixins

如果你覺得排版難看,請點選 下面連結 或者 拉到 下面關注公眾號也可以吧 【Vue原理】Mixins - 原始碼版 今天探索的是 mixins 的原始碼,mixins 根據不同的選項型別會做不同的處理 篇幅會有些長,你知道的,有很多種選項型別的嘛,但不是很難。只是涉及原始碼難免會有些煩, 不過這篇文

Vue原理依賴更新

如果你覺得排版難看,請點選 下面連結 或者 拉到 下面關注公眾號也可以吧 【Vue原理】依賴更新 - 原始碼版 如果對依賴收集完全沒有概念的同學,可以先看我這篇白話版 響應式原理 - 白話版 我們已經講過了 依賴收集 【Vue原理】依賴收集 - 原始碼版之基本資料型別 【Vue

Vue原理Event

如果你覺得排版難看,請點選 下面連結 或者 拉到 下面關注公眾號也可以吧 【Vue原理】Event - 原始碼版 之 自定義事件 Vue 的自定義事件很簡單,就是使用 觀察者模式 進行事件的監聽和分發 Vue 封裝的這個觀察者模式,可以說是很完善了,這個可以獨立抽取出來的在其他專案中使用的程式碼,

Vue原理NextTick

寫文章不容易,點個讚唄兄弟 專注 Vue 原始碼分享,文章分為白話版和 原始碼版,白話版助於理解工作原理,原始碼版助於瞭解內部詳

Vue原理Render

寫文章不容易,點個讚唄兄弟 專注 Vue 原始碼分享,文章分為白話版和 原始碼版,白話版助於理解工作原理,原始碼版助於瞭解內部詳

Vue原理Diff

寫文章不容易,點個讚唄兄弟 <br> <br> 專注 Vue 原始碼分享,文章分為白話版和 原始碼版,

vue.js入門

emp 寫到 logs 組件 images href one mooc 渲染 慕課網視頻學習筆記:http://www.imooc.com/learn/694 1.將html、js、css寫到一個後綴名.vue的文件中,區分這三種類型是通過<template>、

底層原理四位計算機的原理及其實現

一點 led燈 waiting lean div rm2 src and nvt 你是否想過,計算機為什麽會加減乘除?或者更直接一點,計算機的原理到底是什麽? Waitingforfriday有一篇詳細的教程,講解了如何自己動手,制作一臺四位計算機。從中可以看到,二進制、數

計算機原理程序執行過程

進程 cnblogs div 空間 時間片 chat 內存管理 tro alt 本章主要介紹程序執行過程中操作系統、CPU都幹了什麽 運行前 程序在運行前,只是在硬盤上待著,此時就是一堆二進制代碼而已,沒有任何作用。 程序只有進入了內存才能運行,但是要進入內存,則需要服從操

計算機原理CPU部分.md

工作 信號 通過 臃腫 流水線 處理 2.6 操作 ade 本文由CPU阿甘改編而得,主要講的是系統啟動和程序執行時CPU做的工作。 CPU的構成 中央處理器(CPU,Central Processing Unit)由運算器、控制器、Cache等。 控制器:主要是對指令進

組成原理第一章 計算機系統概述

表示 運算 傳遞 intro 指令 掌握 周期 style 主存 重點掌握:MAR和MDR的含義,主存容量大小、CPU執行時間的計算,性能指標CPI、MIPS、主頻等等。 1. 存儲單元:CPU訪問存儲器的基本單位,每個單元有一個地址。通常是字節大小的整數倍。 2. CPU

編譯原理c++實現自下而上語法分析器

不可 acm times style size PC -i 表達式 鏈接 寫在前面:本博客為本人原創,嚴禁任何形式的轉載!本博客只允許放在博客園(.cnblogs.com),如果您在其他網站看到這篇博文,請通過下面這個唯一的合法鏈接轉到原文! 本博客全網唯一合法URL:ht

計數原理UVA11538 Chess Queen

put typename return col nbsp 題意 putchar cst putc 傳送門 Description   給你一個n*m的棋盤,在棋盤上放置一黑一白兩個皇後,求兩個皇後能夠互相攻擊的方案個數 Input   多組數據,每組數據包括:

MongoDB 復制集 第 二 部 之選舉原理

command primary red and 優先權 mongo mongodb 主機 set 目錄: 1·復制與選舉的原理與驗證2·oplog 日誌調整3·配置復制集的優先級4·部署認證的復制5·總結 復制與選舉的原理: 上一篇文章搭建了多臺實例,部署成復制集,

Memcached 主主復制 + Keepalived 高可用架構附上原理

ima pki nag ali event 主服務器 figure tel outer 目錄: 1·Memcached 主主復制概念2·Memcached 高可用的實現3·案例部署4·總結 Memcached 主主復制概念 (1)主主復制概念: Memcached

Windows原理非同步IO-_APC(非同步過程呼叫)

// 同步IO的缺點是, 在讀寫檔案時, 如果檔案太大, 或者讀寫的時間太長, 就會在讀寫函式中 // 阻塞住. // 非同步IO解決了這個問題, 非同步IO讀寫檔案時, 檔案再大也不會阻塞住 // 但是非同步IO要完成這樣的特性是有一點付出的 // 非同步讀寫檔案後, 需要通過一些方式

Windows原理IO非同步-等待事件物件

/* 同步IO的缺點是, 在讀寫檔案時, 如果檔案太大, 或者讀寫的時間太長, 就會在讀寫函式中 阻塞住. 非同步IO解決了這個問題, 非同步IO讀寫檔案時, 檔案再大也不會阻塞住 但是非同步IO要完成這樣的特性是有一點付出的 非同步讀寫檔案後, 需要通過一些方式才能知道檔案讀寫(IO