關於 render 的更詳細的內容能夠查看 Vue 官方文檔javascript
Vue 推薦在絕大多數狀況下使用 template 來建立你的 HTML。然而在一些場景中,你真的須要 JavaScript 的徹底編程的能力,這時你能夠用 render 函數,它比 template 更接近編譯器。html
咱們先設想一下這個場景:vue
使用 v-bind 和 slot ,實現組件切換的功能。經過監聽父組件按鈕的事件,改變父組件 level 的值,而後把新的值經過 props 傳遞給了子組件;而後進行 v-if 判斷,顯示對應的組件。java
<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 函數來改寫一下這個功能: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
demo : JSbin
demo : JSbin
Vue.component('myChild',{ props:['level'], render:function(createElement){ return createElement('h'+this.level,this.$slots.default) } })
在 render 函數的方法中,參數必須是 createElement。,createElement 的類型是 function,他會返回一個 vnode 虛擬節點。
能夠是 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 的第二個參數是數據對象,只能是 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>` } }) }
能夠看到,咱們經過給 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')。
利用 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' })
和以前的例子同樣:
<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 } } })
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,從而實現子組件傳值給父組件。
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,學習就先到這兒了。
得趕忙瞭解完基礎概念,才能更快地學到實戰部分...