前言:
通過前面的學習,我們已經(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ù),
-
第一個標簽參數(shù)為必填項, 類型可以為 Function(createElement ),
render返回值是VNode(虛擬節(jié)點),類型可以是String 、Array。
2. createElement 用法
createElement函數(shù)接受三個參數(shù)
- 第一個參數(shù): 是生成在頁面上顯示的標簽元素(必需參數(shù))
- 第二個參數(shù): 是包含配置信息的數(shù)據(jù)對象(諸如HTML特性,屬性,事件偵聽器已經(jīng)要綁定的class和style)
- 第三個參數(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標簽被渲染出來了

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)容

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é)果

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元素,如下圖所示:

2.3 第三個參數(shù): {String | Array}
這個參數(shù)是可選的,可以給其傳一個String或Array處理子節(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é)果:

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é)果:

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)下面的效果

我們可能會使用如下的方法
<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就都是唯一的了