第二十七節(jié):Vue渲染函數(shù)Render

前言:

通過前面的學習,我們已經(jīng)知道了在vue中,如何使用template模板編寫組件,但是使用模板并不是唯一能讓vue知道應該在頁面顯示什么內(nèi)容的方法,那接下來看看其他的方式

Render 函數(shù)是 Vue2.x 新增的一個函數(shù)、主要用來提升節(jié)點的性能,它是基于 JavaScript 計算。使用 Render 函數(shù)將 Template 里面的節(jié)點解析成虛擬的 Dom 。

Vue 推薦在絕大多數(shù)情況下使用模板來創(chuàng)建你的 HTML。然而在一些場景中,你真的需要 JavaScript 的完全編程的能力。這時你可以用渲染函數(shù),它比模板更接近編譯器。

簡而言之: 在 Vue 中使用模板 HTML 語法組建頁面,使用 Render 函數(shù)是為了讓我們用 Js 語言來構(gòu)建 DOM。

原理: Vue框架的核心是虛擬DOM,編譯template模板時要轉(zhuǎn)譯成VNode的函數(shù),當用render函數(shù)構(gòu)建DOM時,Vue就免去了轉(zhuǎn)譯的步驟。

1. render渲染函數(shù)基本了解

可以將一個函數(shù)傳遞給Vue 實例選項對象中render屬性, 該函數(shù)會接受一個creatElement函數(shù),可以使用它指定需要在頁面上顯示的內(nèi)容,createElment 就是一個用來創(chuàng)建虛擬DOM(VNode)的函數(shù)

render這個方法有接收參數(shù),

  1. 第一個標簽參數(shù)為必填項, 類型可以為 Function(createElement ),

    render返回值是VNode(虛擬節(jié)點),類型可以是String 、Array。

2. createElement 用法

createElement函數(shù)接受三個參數(shù)

  1. 第一個參數(shù): 是生成在頁面上顯示的標簽元素(必需參數(shù))
  2. 第二個參數(shù): 是包含配置信息的數(shù)據(jù)對象(諸如HTML特性,屬性,事件偵聽器已經(jīng)要綁定的class和style)
  3. 第三個參數(shù): 是一個子節(jié)點字符串或者包含子節(jié)點的數(shù)組.

這里是 createElement 接受的參數(shù):

// @returns {VNode}
createElement(
  // {String | Object | Function}
  // 一個 HTML 標簽名、組件選項對象,或者
  // resolve 了上述任何一種的一個 async 函數(shù)。必填項。

  'div',

  // {Object}
  // 一個與模板中 attribute 對應的數(shù)據(jù)對象??蛇x。
  {
    // (詳情見下一節(jié))
  },

  // {String | Array}
  // 子級虛擬節(jié)點 (VNodes),由 `createElement()` 構(gòu)建而成,
  // 也可以使用字符串來生成“文本虛擬節(jié)點”??蛇x。
  [
    '先寫一些文字',
    createElement('h1', '一則頭條'),
    createElement(MyComponent, {
      props: {
        someProp: 'foobar'
      }
    })
  ]
)

那么接下來讓我們好好看看這幾個參數(shù)

2.1 第一個參數(shù){String | Object | Function}

第一個參數(shù)是一個必須的參數(shù),這個參數(shù)可以是字符串string、對象object,或者一個函數(shù)function。

比如我們想創(chuàng)建一個標簽,以前的寫法是這樣的

<div id="app">

</div>

<script>
    //  實例
    const vm = new Vue({
        el: "#app",
        template:"<h2></h2>",
    })
</script>

這是最基本的用法

那么我們?nèi)绾问褂娩秩竞瘮?shù)來創(chuàng)建標簽呢,

2.1.1 字符串:

第一個參數(shù)可以是標簽名的字符串

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

<script>
    const vm = new Vue({
        el: "#app",
        render:function(createElement){
            return createElement("h2")
        }
    })
</script>

給createElement 傳入了一個字符串參數(shù)h2, 我們就會發(fā)現(xiàn)頁面上h2標簽被渲染出來了

image
2.1.2 對象,

參數(shù)除了是字符串外,可以是組件的選項對象

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

<script>
    //  組件選項對象
    let MyComponent = {
        template:"<h3>我就一個組件而已</h3>",
    };

    //  實例中注冊組件
    const vm = new Vue({
        el: "#app",
        render(createElement){
            return createElement(MyComponent)
        }
    })

</script>

我們發(fā)現(xiàn)會直接顯示組件的內(nèi)容

image
2.1.3 函數(shù):

其實第一個參數(shù)也可以是一個函數(shù),只不過這個函數(shù)執(zhí)行完畢后,需要返回一個標簽名的字符串或者組件對象

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

<script>
    //  實例中注冊組件
    const vm = new Vue({
        el: "#app",
        render(createElement){
            let eleFn = function(){
                return {
                    template:"<div>Hello Vue!</div>"
                }
            }
            return createElement(eleFn())
        }
    })

</script>

顯示結(jié)果

image
2.2 第二個參數(shù):{Object}

是一個可選參數(shù),這個參數(shù)是一個Object。關(guān)于第一個參數(shù)創(chuàng)建的 標簽的屬性

{
  // 與 `v-bind:class` 的 API 相同,
  // 接受一個字符串、對象或字符串和對象組成的數(shù)組
  'class': {
    foo: true,
    bar: false
  },
  // 與 `v-bind:style` 的 API 相同,
  // 接受一個字符串、對象,或?qū)ο蠼M成的數(shù)組
  style: {
    color: 'red',
    fontSize: '14px'
  },
  // 普通的 HTML attribute
  attrs: {
    id: 'foo'
  },
  // 組件 prop
  props: {
    myProp: 'bar'
  },
  // DOM property
  domProps: {
    innerHTML: 'baz'
  },
  // 事件監(jiān)聽器在 `on` 內(nèi),
  // 但不再支持如 `v-on:keyup.enter` 這樣的修飾器。
  // 需要在處理函數(shù)中手動檢查 keyCode。
  on: {
    click: this.clickHandler
  },
  // 僅用于組件,用于監(jiān)聽原生事件,而不是組件內(nèi)部使用
  // `vm.$emit` 觸發(fā)的事件。
  nativeOn: {
    click: this.nativeClickHandler
  },
  // 自定義指令。注意,你無法對 `binding` 中的 `oldValue`
  // 賦值,因為 Vue 已經(jīng)自動為你進行了同步。
  directives: [
    {
      name: 'my-custom-directive',
      value: '2',
      expression: '1 + 1',
      arg: 'foo',
      modifiers: {
        bar: true
      }
    }
  ],
  // 作用域插槽的格式為
  // { name: props => VNode | Array<VNode> }
  scopedSlots: {
    default: props => createElement('span', props.text)
  },
  // 如果組件是其它組件的子組件,需為插槽指定名稱
  slot: 'name-of-slot',
  // 其它特殊頂層 property
  key: 'myKey',
  ref: 'myRef',
  // 如果你在渲染函數(shù)中給多個元素都應用了相同的 ref 名,
  // 那么 `$refs.myRef` 會變成一個數(shù)組。
  refInFor: true
}

//  class style的不同寫法
{
    class:["class1",{"class2":true}],
    style: {background: red}
}

請注意,class,style并沒有在attrs屬性中,他們是單獨設(shè)置的,這是因為v-bind指令的特性,如果僅僅將class或者style設(shè)置在attrs對象中的一個屬性,就不能將class和style設(shè)置為數(shù)組或是對象了,

示例:

const vm = new Vue({
    el: "#app",
    render: function (createElement) {

        // 第一個參數(shù)是一個簡單的HTML標簽字符 “必選”
        // 第二個參數(shù)是一個包含模板相關(guān)屬性的數(shù)據(jù)對象 “可選”
        return createElement('div', {
            'class': {
                foo: true,
                bar: false
            },
            style: {
                color: 'skyblue',
                fontSize: '24px'
            },
            attrs: {
                id: 'foo'
            },
            domProps: {
                innerHTML: 'Hello Vue!'
            }
        })
    }
})

最終生成的DOM,將會帶一些屬性和內(nèi)容的div元素,如下圖所示:

image
2.3 第三個參數(shù): {String | Array}

這個參數(shù)是可選的,可以給其傳一個StringArray處理子節(jié)點

2.3.1 字符串

當?shù)谌齻€參數(shù)為字符串是,就是節(jié)點的文本內(nèi)容

<script>
    const vm = new Vue({
        el: "#app",
        render:function(createElement){
            return createElement("input", {
                class:"title"
            },"這是標題")
        }
    })
</script>

顯示結(jié)果:

image
2.3.2 數(shù)組

數(shù)組中可以放多個節(jié)點,可以說是字符串的文本節(jié)點, 也可以是通過createElement創(chuàng)建的節(jié)點

<script>
    const vm = new Vue({
        el: "#app",
        render(h){
            return h("div",{
                class:"box",
            },[
                h("span","我是一個span標簽"),
                h("input",{
                    attrs:{
                        type:"button",
                        value:"按鈕"
                    }
                })
            ])
        }
    })
</script>

顯示結(jié)果:

image
2.4 渲染函數(shù)簡寫

我們會發(fā)現(xiàn)每次寫createElement會非常繁瑣,所以我們可以給這個函數(shù)定義別名h

那么我們的代碼就可以修改為

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

<script>
    const vm = new Vue({
        el: "#app",
        render(h){
            return h("input", {
                class:"title"
            },["這是標題"])
        }
    })
</script>

3. 約束:

3.1 約束的理解

組件樹中的所有虛擬DOM(VNode)必須是唯一的,

例如,我想通過render函數(shù)實現(xiàn)下面的效果

image

我們可能會使用如下的方法

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

<script>
    //  實例中注冊組件
    const vm = new Vue({
        el:"#app",
        data:{
            msg:'hello'
        },
        render(h){
            // 1\. 創(chuàng)建虛擬DOM 節(jié)點
            let myVNode = h("p","hello");

            // 2\. 返回最終的虛擬DOM
            return h("div",[
                myVNode,myVNode,myVNode,myVNode
            ])
        }
    })
</script>

我們會發(fā)現(xiàn)雖然可以實現(xiàn)效果, 也沒有報錯,但是不合法

那么我們應該如何實現(xiàn)呢

3.2 建議使用的方法

使用數(shù)據(jù)map幫我們處理創(chuàng)建多個虛擬DOM

示例:

<script>
    const vm = new Vue({
        el:"#app",
        data:{
            msg:'hello'
        },
        render(h){
            // 返回最終的虛擬DOM
            return h("div",
                     Array.apply(null,{length:4}).map(() => {
                        return h("p","hello")
                    })
             )
        }
    })
</script>

這樣創(chuàng)建的虛擬DOM就都是唯一的了

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容