我們知道 Vue 模板是非常強(qiáng)大的,基本可以完成我們?nèi)粘i_(kāi)發(fā)的所有任務(wù)。但是,有一些用例,如基于輸入或插槽值創(chuàng)建動(dòng)態(tài)組件方式,render
函數(shù)會(huì)比模板完成的更好也更出色。
用過(guò) React 開(kāi)發(fā)的人對(duì) render
函數(shù)應(yīng)該非常熟悉,因?yàn)镽eact組件通過(guò) JSX和 render 函數(shù)來(lái)構(gòu)建的。盡管Vue render
函數(shù)也可以用JSX編寫(xiě),但在這里我們使用原生 JS方式,因?yàn)檫@樣,我們可以更輕松地了解Vue組件系統(tǒng)的一些基礎(chǔ)。
值得注意的是,Vue 的模板實(shí)際上在編譯時(shí)也是會(huì)先解析成 render
函數(shù)表示方式。模板只是在render
函數(shù)之上提供了一個(gè)方便且熟悉的語(yǔ)法糖。盡管 render
函數(shù)更強(qiáng)大,但render
函數(shù)可讀性很差,相對(duì)用的也比較少了。
帶有 render
函數(shù)的組件沒(méi)有template
標(biāo)記或?qū)傩?。相反,該組件定義了一個(gè)了名為render
的函數(shù),該函數(shù)接收一個(gè)reateElement(renderElement: String | Component, definition: Object, children: String | Array)
參數(shù)(由于某種原因,通常別名為h
,歸咎于JSX)并返回使用該函數(shù)創(chuàng)建的元素,其他一切保持不變,來(lái)看看事例:
export default {
data() {
return {
isRed: true
}
},
/*
* <template>
* <div :class='{'is-red': isRed}'>
* <p>這是一個(gè) render 事例</p>
* </div>
* </template>
*/
// render 中的渲染結(jié)果與上面等價(jià)
render(h) {
return h('div', {
'class': {
'is-red': this.isRed
}
}, [
h('p', '這是一個(gè) render 事例')
])
}
}
Vue 模板具有各種便捷功能,以便向模板添加基本邏輯和綁定功能,如 v-if
、v-for
、v-moel
指令等。在render
函數(shù)中是無(wú)法使用這些指令的。取而代之的是以純 JS 來(lái)實(shí)現(xiàn),對(duì)于大多數(shù)指令而言,這也是比較簡(jiǎn)單的。
v-if
用純 JS 實(shí)現(xiàn)很簡(jiǎn)單,只需圍繞createElement
調(diào)用使用 if(expr)
語(yǔ)句即可。
v-for
可以使用for-of
,Array.map
,Array.filter
等的JS方法中的任何一種來(lái)實(shí)現(xiàn)。我們可以通過(guò)非常有趣的方式將它們組合在一起,以實(shí)現(xiàn)過(guò)濾或狀態(tài)切片,而無(wú)需計(jì)算屬性。
例如,有以下 Vue 的模板代碼
<template>
<ul>
<li v-for='pea of pod'>
</li>
</ul>
</template>
可以用下面的 render
函數(shù)來(lái)實(shí)現(xiàn)上面的效果:
render(h) {
return h('ul', this.pod.map(pea => h('li', pea.name)));
}
我們知道,v-model
只是bind
屬性與value
的語(yǔ)法糖,并在觸發(fā)input
事件時(shí)設(shè)置數(shù)據(jù)屬性。但是,在render
函數(shù)沒(méi)有這樣的簡(jiǎn)寫(xiě),我們需要自己實(shí)現(xiàn)。
假設(shè),在 Vue 中,我們有如下的結(jié)構(gòu):
<template>
<input v-model='myBoundProperty'/>
</template>
上面代碼等價(jià)于:
<template>
<input :value='myBoundProperty' @input='myBoundProperty = $event.target.value'/>
</template>
在 render 函數(shù)中可以用下面方式來(lái)實(shí)現(xiàn)上面的代碼:
render(h) {
return h('input', {
domProps: {
value: this.myBoundProperty
},
on: {
input: e => {
this.myBoundProperty = e.target.value
}
}
})
}
attribute
和property
這兩種類型的綁定被放在元素定義中,如arttrs
、props
和domProps
( value
和innerHTML
之類的東西)。
render(h) {
return h('div', {
attrs: {
// <div :id='myCustomId'>
id: this.myCustomId
},
props: {
// <div :someProp='someonePutSomethingHere'>
someProp: this.someonePutSomethingHere
},
domProps: {
// <div :value='somethingElse'>
value: this.somethingElse
}
});
}
需要注意的是,對(duì)于 class
和style
的綁定是直接在定義的根進(jìn)行處理,而不是作為attrs
,props
或domProps
處理。
render(h) {
return h('div', {
// “類”是JS中的保留關(guān)鍵字,因此必須引用它。
'class': {
myClass: true,
theirClass: false
},
style: {
backgroundColor: 'green'
}
});
}
對(duì)事件處理程也是直接添加到元素定義中 on
定義
render(h) {
return h('div', {
on: {
click(e) {
console.log('I got clickeded!')
}
}
});
}
事件的修飾符可以在處理程序內(nèi)部實(shí)現(xiàn):
.stop -> e.stopPropagation()
.prevent -> e.preventDefault()
.self -> if (e.target !== e.currentTarget) return
鍵盤修飾符
.[TARGET_KEY_CODE]
-> if (event.keyCode !== TARGET_KEY_CODE) return
.[MODIFIER]
-> if (!event.MODIFIERKey) return
Slots
可以通過(guò)this.$slots
作為createElement()
節(jié)點(diǎn)的數(shù)組來(lái)訪問(wèn)插槽。
作用域插槽存儲(chǔ)在this.$scopedSlots[scope](props:object)
中,作為返回createElement()
節(jié)點(diǎn)數(shù)組的函數(shù)。
原文:https://vuejsbook.com/introduction-to-vue-render-functions-with-examples
原文:https://vuejsbook.com/introduction-to-vue-render-functions-with-examples
作者:Joshua Bemenderfer 譯者:前端小智 來(lái)源:vuejsbook.com
聯(lián)系客服