Vue.js 建立一個 CNODE 社區(6)

render

關於 render 的更詳細的內容能夠查看 Vue 官方文檔javascript

Vue 推薦在絕大多數狀況下使用 template 來建立你的 HTML。然而在一些場景中,你真的須要 JavaScript 的徹底編程的能力,這時你能夠用 render 函數,它比 template 更接近編譯器。html

咱們先設想一下這個場景:vue

使用 v-bind 和 slot ,實現組件切換的功能。經過監聽父組件按鈕的事件,改變父組件 level 的值,而後把新的值經過 props 傳遞給了子組件;而後進行 v-if 判斷,顯示對應的組件。java

使用 template 作法

<div id="app">
      <my-child :level='level'>
          我是組件中內容
      </my-child>
      <br>
      <button @click='changeFontSize(1)'>改變字號-大</button>
      <button @click='changeFontSize(2)'>改變字號-中</button>
      <button @click='changeFontSize(3)'>改變字號-小</button>
      <template id='myTemp'>
          <div>
            <h1 v-if='level==1'><slot></slot></h1>
            <h2 v-if='level==2'><slot></slot></h2>
            <h3 v-if='level==3'><slot></slot></h3>
        </div>
      </template>
  </div>
Vue.component('myChild',{
    props:['level'],
    template:'#myTemp'
})
var app = new Vue({
    el:'#app',
    data:{
        level:3
    },
    methods:{
        changeFontSize:function(value){
            this.level = value
        }
    }
})

demo :JSbinnode

可是,若是有更多的組件須要進行判斷和切換呢?編程

template 就會越寫越長,而且咱們能夠看到:數組

代碼冗餘

每當點擊按鈕切換組件的時候,以前的組件移除後,他們的組件也會渲染在頁面上,會堆積在 DOM 樹中,致使代碼變得冗餘,難以維護。app

經過 render

如今經過 render 函數來改寫一下這個功能:dom

<div id="app">
      <my-child :level='level'>
          我是組件中內容
      </my-child>
      <br>
      <button @click='changeFontSize(1)'>改變字號-大</button>
      <button @click='changeFontSize(2)'>改變字號-中</button>
      <button @click='changeFontSize(3)'>改變字號-小</button>
  </div>
Vue.component('myChild',{
    props:['level'],
    render:function(createElement){
        return createElement('h'+this.level,this.$slots.default)
    }
})
var app = new Vue({
    el:'#app',
    data:{
        level:3
    },
    methods:{
        changeFontSize:function(value){
            this.level = value
        }
    }
})

經過 render 處理可讓結構更簡潔,代碼也沒有冗餘:ide

render

demo : JSbin


render 詳解

demo : JSbin

Vue.component('myChild',{
    props:['level'],
    render:function(createElement){
        return createElement('h'+this.level,this.$slots.default)
    }
})

在 render 函數的方法中,參數必須是 createElement。,createElement 的類型是 function,他會返回一個 vnode 虛擬節點。

  • createElement 的第一個參數

能夠是 String | Object | Function

// String - HTML標籤,會在組件所在的位置渲染一個 div 節點
render:function(createElement){
        return createElement('div')
    }
// Object - 一個含有數據選項的對象
render:function(createElement){
        return createElement({
            template:`<div></div>`
        })
    }
// Function - 一個方法,返回含有數據選項的對象
render:function(createElement){
        let createDom = function(){
            return {
                template:`<div></div>`
            }
        }
        return createElement(createDom)
    }
  • createElement 的第二個參數(可選)

createElement 的第二個參數是數據對象,只能是 object。

// Function - 一個方法,返回含有數據選項的對象
render:function(createElement){
    return createElement({
        template:`<div>這裏字體是紅色的</div>`
    },{
        class:{
            foo:true,
            bar:false
        },
        style:{
            color:'red',
            fontSize:'30px'
        },
        attrs:{
            id:'foo'
        },
        // 用來顯示原生的 dom 屬性
        domProps:{
            innerHTML:`<span style='color:yellow;font-size:16px'>這裏是黃色的</span>`
        }
    })
}

render

能夠看到,咱們經過給 createElement 方法添加第二個參數,爲第一個參數所創造的節點增添了很多的屬性。

  • createElement 的第三個參數(可選)

第三個參數也是可選,String | Array,做爲咱們構建函數的子節點來使用的

render:function(createElement){
    return createElement('div',{
        class:{
            foo:true,
            bar:false
        }},[
        createElement('h1','建立子節點1'),
        createElement('h6','建立子節點2')
    ])
}

第三個參數

能夠看到,咱們經過給 createElement 方法添加第三個參數,爲第一個參數所創造的節點增添了兩個子節點(h1/h6)以及分別爲兩個子節點添加了一個子節點('建立子節點1'/'建立子節點2')。


this.$slots在render函數中的應用

利用 createElement 的第三個參數能夠存儲數組、建立子節點的特色,能夠把指定的 slot (this.$slots.slotName返回的是一個 vnode 數組)存儲到某個節點中。

demo : JSbin

<div id="app">
    <child>
        <p>正文</p>
        <span>正文段落</span>
        <h1 slot='header'>header</h1>
        <h2 slot='footer'>footer</h2>
    </child>
</div>
Vue.component('child',{
    render:function(createElement){
        // 注意,這裏的 header 是一個 vnode 數組,因此他是 createElement 的第三個參數
        let header = this.$slots.header
        let footer = this.$slots.footer
        let main = this.$slots.default
        return createElement('div',[
            createElement('header',header),
            createElement('main',main),
            createElement('footer',footer)
        ])
    }
})
var app = new Vue({
    el:'#app'
})

在 render 函數中使用 props 傳遞數據

和以前的例子同樣:

<div id="app">
      <my-child :level='level'>
          我是組件中內容
      </my-child>
      <br>
      <button @click='changeFontSize(1)'>改變字號-大</button>
      <button @click='changeFontSize(2)'>改變字號-中</button>
      <button @click='changeFontSize(3)'>改變字號-小</button>
  </div>
Vue.component('myChild',{
    props:['level'],
    render:function(createElement){
        return createElement('h'+this.level,this.$slots.default)
    }
})
var app = new Vue({
    el:'#app',
    data:{
        level:3
    },
    methods:{
        changeFontSize:function(value){
            this.level = value
        }
    }
})

v-model 在 render 函數中的應用

demo : JSbin

<div id="app">
    <app-component :name='name' v-model='name'></app-component> <br>
    {{ name }}
</div>
Vue.component('app-component',{
    render:function(createElement){

        // 這裏的 this 等於子組件的this
        let self = this
        return createElement('input',{
            domProps:{

                // 這句的做用是把父組件傳遞進來的值顯示在輸入框裏
                value:self.name
            },
            on:{
                input:function(event){
                    self.$emit('input',event.target.value)
                }
            }
        })
    },
    props:['name']
})
var app = new Vue({
    el:'#app',
    data:{
        name:'lily'
    }
})

首先咱們註冊了一個子組件,而且經過 render 函數渲染模板,其中設置了一個 value 屬性和 input 的監聽事件;

聲明 self 是爲了肯定 this 的指向是子組件;

value 屬性的值,是父組件經過 :name='name'props 傳遞給子組件的;

子組件經過 v-model 綁定了父組件的數據 name,當用戶在輸入框中輸入時,咱們在子組件中設置的監聽事件就會觸發 $emit 事件,把輸入框中的 value 傳遞給了 v-model,v-model 更新了 name 的值爲 value,從而實現子組件傳值給父組件。


做用域插槽在render函數中的使用

demo : JSbin

<div id="app">
    <app-component>
    <template scope='prop'>
        {{ prop.text}} <br>
        {{ prop.msg}}
    </template>
    </app-component>
</div>
Vue.component('app-component',{
    render:function(createElement){

        // 其實這句話就至關於以前的 template:`<div><slot text='scopeText' msg='scopeMsg'></slot></div>`
        return createElement('div',this.$scopedSlots.default({
            text:'scopeText',
            msg:'scopeMsg'
        }))
    }
})
var app = new Vue({
    el:'#app'
})

函數化組件的應用

以前建立的錨點標題組件是比較簡單,沒有管理或者監放任何傳遞給他的狀態,也沒有生命週期方法。它只是一個接收參數的函數。在這個例子中,咱們標記組件爲 functional,這意味它是無狀態 (沒有響應式數據),無實例 (沒有 this 上下文)。

組件須要的一切都是經過上下文(傳入的 context)傳遞

在添加 functional: true 以後,錨點標題組件的 render 函數之間簡單更新增長 context 參數,this.$slots.default 更新爲 context.children,以後this.level 更新爲 context.props.level。

demo : JSbin

<div id="app">
    <app-component></app-component>
</div>
Vue.component('app-component',{
    functional: true,
    render:function(createElement,context){
        return createElement('button',{
            on:{
                click:function(){
                    console.log(context)
                    console.log(context.parent)
                }
            }
        },'點擊我查看上下文')
    }
})
var app = new Vue({
    el:'#app',
})

關於 render,學習就先到這兒了。

得趕忙瞭解完基礎概念,才能更快地學到實戰部分...

相關文章
相關標籤/搜索