vue進階篇- Render函數

一、什麼是Virtual Dom
React和Vue2都使用了Virtual Dom技術,Virtual Dom並不是真正意義上的Dom,而是一個輕量級的JavaScript對象,在狀態發生變化時,Virtual Dom會進行Diff運算,來更新只需要被替換的DOM,而不是全部重繪。
與DOM操作相比,Virtual Dom是基於JavaScript計算的,所以開銷會小很多。
正常的DOM節點在HTML中是這樣的:

<div id="main">
  <p>文本內容</p>
  <p>文本內容</p>
</div>

在這裏插入圖片描述
用Virtual Dom 創建的JavaScript 對象一般會是這樣:

var vNode ={
     tag:'div',
    attributes:{
        id:'main'
    },
    children:{
        //p節點
    }
}

vNode對象通過一些特定的選項描述了真實的DOM結構。
在Vue.js2中,Virtual Dom就是通過一種VNode類表達的,每個DOM元素或組件都對應一個VNode對象.在Vue.js 遠嗎中是這樣定義:

export interface Vnode{
    tag ?: string;
    data? :vNodeData;
    children?: VNode[];
    text?: string;
    elm?:Node;
    ns?:string;
    context?:Vue
    key?:string |number;
    componentOptIons ?: VNodeComponentOptions;
    componentInstance?: Vue;
    parent?: Vnode;
    raw?: boolean;
    isRootInsert :boolean;
    isComment: boolean;
}

具體包含如下:

  • tag 當前節點的標籤名。
  • data 當前節點的數據對象。
    VNodeData代碼如下:
export interface VNodeData {
    key?: string | number;
    slot?: string;
    scopedSlots?: { [key: string]: ScopedSlot };
    ref?: string;
    tag?: string;
    staticClass?: string;
    class?: any;
    staticStyle?: { [key: string]: any };
    style?: Object[] | Object;
    props?: { [key: string]: any };
    attrs?: { [key: string]: any };
    domProps?: { [key: string]: any };
    hook?: { [key: string]: Function };
    on?: { [key: string]: Function | Function[] };
    nativeOn?: { [key: string]: Function | Function[] };
    transition?: Object;
    show?: boolean;
    inlineTemplate?: {
        render: Function;
        staticRenderFns: Function[];
    };
    directives?: VNodeDirective[];
    keepAlive?: boolean;
}
  • children 子節點,數組,也是VNode類型。
  • text 當前節點的文本,一般文本節點或註釋節點會有該屬性。
  • elm 當前虛擬節點對應的真實的DOM節點。
  • ns 節點的namespace
  • context 編譯作用域。
  • functionalContext 函數化組件的作用域。
  • key 節點的key屬性,用於作爲節點的標識,有利於patch的優化。
  • componentOptions 創建組件實例時會用到的信息選項。
  • child 當前節點對應的組件實例。
  • parent 組件的佔位節點。
  • raw 原始html。
  • isStatic 靜態節點的標識。
  • isRootInsert 是否作爲根節點插入,被<transition>包裹的節點,該屬性的值爲false
  • isComment 當前節點是否是註釋節點。
  • isCloned 當前節點是否爲克隆節點。
  • isOnce 當前節點是否有v-once指令。

VNode主要可以分爲以下幾類:

  • EmptyVNode 沒有內容的註釋節點
  • ComponentVNode 組件節點
  • TextVNode 文本節點
  • ElementVNode 普通元素節點
  • CloneVNode 克隆節點,可以是以上任意類型的節點,唯一的區別在於isCloned 屬性爲true

使用Virtual Dom就可以完全發揮JavaScript的能力。在多數場景下,我們使用template就足夠了,但在一些特定的場景下,使用Virtual Dom會更簡單,下節我們來介紹Vue的Render函數的用法。

二、什麼是render函數
Render函數通過createElement參數來創建Virtual Dom,結構精簡,代碼少且清晰,這裏使用了一個demo實例來說明,我未對實例進行摘抄,我們只有清楚Render函數所有神奇的地方都在這個createElement裏就可以了,我們在下一節來詳細介紹它的詳細配置和用法。

<div id="app">
    <anchor :level="2" title="特性">特性</anchor>
</div>
<script>
    Vue.component('anchor', {
        props: {
            level: {
                type: Number,
                required: true
            },
            title: {
                type: String,
                default: ''
            }
        },
        render: function (createElement) {
            return createElement(
                'h' + this.level,
                [
                    createElement(
                        'a',
                        {
                            domProps: {
                                href: '#' + this.title
                            }
                        },
                        this.$slots.default
                    )
                ]
            )
        }
    });

    var app = new Vue({
        el: '#app'
    })
</script>

三、createElement用法
1、基本參數
createElement構成了Vue Virtual Dom的模板,它有3個參數:

createElement(
        // {String | Object | Function}
        // 一個HTML標籤,組件選項,或一個函數
        // 必須return上述其中一個
        'div',
        // {Object}
        // 一個對應屬性的數據對象,可選
        // 可以在template中使用
        {
            // 稍後詳細介紹
        },
        // {String | Array}
        // 子節點(VNodes),可選
        [
            createElement('h1', 'hello world'),
            createElement(MyComponent, {
                props: {
                    someProp: 'foo'
                }
            }),
            'bar'
        ]
    )

第一個參數必選,可以是一個HTML標籤,也可以是一個組件或函數;第二個是可選參數,數據對象,在template中使用。第三個是子節點,也是可選參數,用法一致。

對於第二個參數“數據對象”。具體的選項如下:

{
    //和v-bind:class一樣的API
    'class': {
        foo: true,
        bar: false
    },
    //和v-bind:style一樣的API
    'style': {
        color: 'red',
        fontSize: '14px'
    },
    //正常的HTML特性
    attrs {
        id: 'foo'
    },
    //組件props
    props: {
        myProp: 'bar'
    },
    //DOM屬性
    domProps: {
        innerHTML: 'baz'
    },
    //自定義事件監聽器"on"
    //不支持如v-on:keyup.enter的修飾器
    //需要手動匹配keyCode
    on: {
        click: this.clickHandler
    },
    //進對於組件,用於監聽原生事件
    //而不是組件使用vm.$emit觸發的自定義事件
    nativeOn: {
        click: this.nativeClickHandler
    },
    //自定義指令
    directives: [
        {
            name: 'my-custom-directive',
            value: '2',
            expression: '1+1',
            arg: 'foo',
            modifiers: {
                bar: true
            }
        }
    ],
    //作用域slot
    //{name: props => VNode | Array<Vnode> }
    scopedSlots: {
        default: props => h('span', props.text)
    },
    //如果子組件有定義slot的名稱
    slot: 'name-of-slot',
    //其他特殊頂層屬性
    key: 'myKey',
    ref: 'myRef'
}

以往在template裏,我們都是在組件的標籤上使用形容 v-bind:class、v-bind:style、v-on:click 這樣的指令,在Render 函數都將其寫在了數據對象裏,傳統的template 寫法是:

<div id="app">
 <ele></ele>
</div>

vue.component('ele',{
template:'\
<div id="element" \
:class="{show: show}" \
@click= "handleClick"> 文本內容</div>,
data:function() {
   return{
     show: true
},
methods:{
handleClick:function(){
    console.log('clicked');
   }
}
});

var app = new vue({
 el: '#app'
})

使用Render 改寫:

<div id="app">
<ele></ele>
</div>
Vue.component('ele',{
   render: function (createElement){
    return createElement{
    'div',
    {
   //動態綁定class,同 :class
   class:{
     'show':this.show
},
//普通html 特性
 attrs:{
    id:'element'
},
//給div 綁定click 時間
on:{
   click: this .handleClick
}
},
'文本內容'
}
},
data: function(){
   return{
     show:true
}
},
methods:{
  handleClick:function(){
console.log('clicked!');
}
}
});
var app = new Vue({
   el: '#app'
})

對比下 template的寫法比Render 寫法要可讀而且簡潔。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章