新聞中心
Vue.js 2.0 Render 函數(shù)基礎(chǔ)
Vue 推薦使用在絕大多數(shù)情況下使用 template 來(lái)創(chuàng)建你的 HTML。然而在一些場(chǎng)景中,你真的需要 JavaScript 的完全編程的能力,這就是 render 函數(shù),它比 template 更接近編譯器。

專注于為中小企業(yè)提供網(wǎng)站設(shè)計(jì)制作、網(wǎng)站設(shè)計(jì)服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)陜州免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了近1000家企業(yè)的穩(wěn)健成長(zhǎng),幫助中小企業(yè)通過(guò)網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。
讓我們先深入一個(gè)使用 render 函數(shù)的簡(jiǎn)單例子,假設(shè)你想生成一個(gè)帶錨鏈接的標(biāo)題:
Hello world!
在 HTML 層, 我們決定這樣定義組件接口:
Hello world! 當(dāng)我們開(kāi)始寫(xiě)一個(gè)通過(guò) level prop 動(dòng)態(tài)生成heading 標(biāo)簽的組件,你可很快能想到這樣實(shí)現(xiàn):
Vue.component('anchored-heading', {
template: '#anchored-heading-template',
props: {
level: {
type: Number,
required: true
}
}
})template 在這種場(chǎng)景中就表現(xiàn)的有些冗余了。雖然我們重復(fù)使用
雖然模板在大多數(shù)組件中都非常好用,但是在這里它就不是很簡(jiǎn)潔的了。那么,我們來(lái)嘗試使用 render 函數(shù)重寫(xiě)上面的例子:
Vue.component('anchored-heading', {
render: function (createElement) {
return createElement(
'h' + this.level, // tag name 標(biāo)簽名稱
this.$slots.default // 子組件中的陣列
)
},
props: {
level: {
type: Number,
required: true
}
}
})簡(jiǎn)單清晰很多!簡(jiǎn)單來(lái)說(shuō),這樣代碼精簡(jiǎn)很多,但是需要非常熟悉 Vue 的實(shí)例屬性。在這個(gè)例子中,你需要知道當(dāng)你不使用 slot 屬性向組件中傳遞內(nèi)容時(shí),比如 anchored-heading 中的 Hello world!, 這些子元素被存儲(chǔ)在組件實(shí)例中的 $slots.default中。如果你還不了解,在深入 render 函數(shù)之前推薦閱讀 instance 屬性 API。
createElement 參數(shù)
第二件你需要熟悉的是如何在 createElement 函數(shù)中生成模板。這里是 createElement接受的參數(shù):
// @returns {VNode}
createElement(
// {String | Object | Function}
// 一個(gè) HTML 標(biāo)簽,組件設(shè)置,或一個(gè)函數(shù)
// 必須 Return 上述其中一個(gè)
'div',
// {Object}
// 一個(gè)對(duì)應(yīng)屬性的數(shù)據(jù)對(duì)象
// 您可以在 template 中使用.可選項(xiàng).
{
// (下一章,將詳細(xì)說(shuō)明相關(guān)細(xì)節(jié))
},
// {String | Array}
// 子節(jié)點(diǎn)(VNodes). 可選項(xiàng).
[
createElement('h1', 'hello world'),
createElement(MyComponent, {
props: {
someProp: 'foo'
}
}),
'bar'
]
)
完整數(shù)據(jù)對(duì)象
有一件事要注意:在 templates 中,v-bind:class 和 v-bind:style ,會(huì)有特別的處理,他們?cè)?VNode 數(shù)據(jù)對(duì)象中,為最高級(jí)配置。
{
// 和`v-bind:class`一樣的 API
'class': {
foo: true,
bar: false
},
// 和`v-bind:style`一樣的 API
style: {
color: 'red',
fontSize: '14px'
},
// 正常的 HTML 特性
attrs: {
id: 'foo'
},
// 組件 props
props: {
myProp: 'bar'
},
// DOM 屬性
domProps: {
innerHTML: 'baz'
},
// 事件監(jiān)聽(tīng)器基于 "on"
// 所以不再支持如 v-on:keyup.enter 修飾器
// 需要手動(dòng)匹配 keyCode。
on: {
click: this.clickHandler
},
// 僅對(duì)于組件,用于監(jiān)聽(tīng)原生事件,而不是組件使用 vm.$emit 觸發(fā)的事件。
nativeOn: {
click: this.nativeClickHandler
},
// 自定義指令. 注意事項(xiàng):不能對(duì)綁定的舊值設(shè)值
// Vue 會(huì)為您持續(xù)追踨
directives: [
{
name: 'my-custom-directive',
value: '2'
expression: '1 + 1',
arg: 'foo',
modifiers: {
bar: true
}
}
],
// 如果子組件有定義 slot 的名稱
slot: 'name-of-slot'
// 其他特殊頂層屬性
key: 'myKey',
ref: 'myRef'
}
完整示例
有了這方面的知識(shí),我們現(xiàn)在可以完成我們最開(kāi)始想實(shí)現(xiàn)的組件:
var getChildrenTextContent = function (children) {
return children.map(function (node) {
return node.children
? getChildrenTextContent(node.children)
: node.text
}).join('')
}
Vue.component('anchored-heading', {
render: function (createElement) {
// create kebabCase id
var headingId = getChildrenTextContent(this.$slots.default)
.toLowerCase()
.replace(/\W+/g, '-')
.replace(/(^\-|\-$)/g, '')
return createElement(
'h' + this.level,
[
createElement('a', {
attrs: {
name: headingId,
href: '#' + headingId
}
}, this.$slots.default)
]
)
},
props: {
level: {
type: Number,
required: true
}
}
})
約束
VNodes 必須唯一
所有組件樹(shù)中的 VNodes 必須唯一。這意味著,下面的 render function 是無(wú)效的:
render: function (createElement) {
var myParagraphVNode = createElement('p', 'hi')
return createElement('div', [
// Yikes - duplicate VNodes!
myParagraphVNode, myParagraphVNode
])
}如果你真的需要重復(fù)很多次的元素/組件,你可以使用工廠函數(shù)來(lái)實(shí)現(xiàn)。例如,下面這個(gè)例子 render 函數(shù)完美有效地渲染了 20 個(gè)重復(fù)的段落:
render: function (createElement) {
return createElement('div',
Array.apply(null, { length: 20 }).map(function () {
return createElement('p', 'hi')
})
)
}
使用 JavaScript 代替模板功能
無(wú)論什么都可以使用原生的 JavaScript 來(lái)實(shí)現(xiàn),Vue 的 render 函數(shù)不會(huì)提供專用的 API。比如, template 中的 v-if 和 v-for:
- {{ item.name }}
No items found.
這些都會(huì)在 render 函數(shù)中被 JavaScript 的 if/else 和 map 重寫(xiě):
render: function (createElement) {
if (this.items.length) {
return createElement('ul', this.items.map(function (item) {
return createElement('li', item.name)
}))
} else {
return createElement('p', 'No items found.')
}
}
JSX
如果你寫(xiě)了很多 render 函數(shù),可能會(huì)覺(jué)得痛苦:
createElement(
'anchored-heading', {
props: {
level: 1
}
}, [
createElement('span', 'Hello'),
' world!'
]
)特別是模板如此簡(jiǎn)單的情況下:
Hello world!
這就是會(huì)有一個(gè) Babel plugin 插件,用于在 Vue 中使用 JSX 語(yǔ)法的原因,它可以讓我們回到于更接近模板的語(yǔ)法上。
import AnchoredHeading from './AnchoredHeading.vue'
new Vue({
el: '#demo',
render (h) {
return (
Hello world!
)
}
})
將 h 作為 createElement 的別名是 Vue 生態(tài)系統(tǒng)中的一個(gè)通用慣例,實(shí)際上也是 JSX 所要求的,如果在作用域中 h 失去作用, 在應(yīng)用中會(huì)觸發(fā)報(bào)錯(cuò)。
更多關(guān)于 JSX 映射到 JavaScript,閱讀 使用文檔。
函數(shù)化組件
之前創(chuàng)建的錨點(diǎn)標(biāo)題組件是比較簡(jiǎn)單,沒(méi)有管理或者監(jiān)聽(tīng)任何傳遞給他的狀態(tài),也沒(méi)有生命周期方法。它只是一個(gè)接收參數(shù)的函數(shù)。在這個(gè)例子中,我們標(biāo)記組件為 functional, 這意味它是無(wú)狀態(tài)(沒(méi)有 data),無(wú)實(shí)例(沒(méi)有 this 上下文)。一個(gè)函數(shù)化組件就像這樣:
Vue.component('my-component', {
functional: true,
// 為了彌補(bǔ)缺少的實(shí)例
// 提供第二個(gè)參數(shù)作為上下文
render: function (createElement, context) {
// ...
},
// Props 可選
props: {
// ...
}
})組件需要的一切都是通過(guò)上下文傳遞,包括:
- props: 提供props 的對(duì)象
- children: VNode 子節(jié)點(diǎn)的數(shù)組
- slots: slots 對(duì)象
- data: 傳遞給組件的 data 對(duì)象
- parent: 對(duì)父組件的引用
在添加 functional: true 之后,錨點(diǎn)標(biāo)題組件的 render 函數(shù)之間簡(jiǎn)單更新增加 context參數(shù),this.$slots.default 更新為 context.children,之后this.level 更新為 context.props.level。
函數(shù)化組件只是一個(gè)函數(shù),所以渲染開(kāi)銷(xiāo)也低很多。但同樣它也有完整的組件封裝,你需要知道這些, 比如:
- 程序化地在多個(gè)組件中選擇一個(gè)
- 在將 children, props, data 傳遞給子組件之前操作它們。
下面是一個(gè)依賴傳入 props 的值的 smart-list 組件例子,它能代表更多具體的組件:
var EmptyList = { /* ... */ }
var TableList = { /* ... */ }
var OrderedList = { /* ... */ }
var UnorderedList = { /* ... */ }
Vue.component('smart-list', {
functional: true,
render: function (createElement, context) {
function appropriateListComponent () {
var items = context.props.items
if (items.length === 0) return EmptyList
if (typeof items[0] === 'object') return TableList
if (context.props.isOrdered) return OrderedList
return UnorderedList
}
return createElement(
appropriateListComponent(),
context.data,
context.children
)
},
props: {
items: {
type: Array,
required: true
},
isOrdered: Boolean
}
})
slots() 和 children 對(duì)比
你可能想知道為什么同時(shí)需要 slots() 和 children。slots().default 不是和 children 類(lèi)似的嗎?在一些場(chǎng)景中,是這樣,但是如果是函數(shù)式組件和下面這樣的 children 呢?
first
second
對(duì)于這個(gè)組件,children 會(huì)給你兩個(gè)段落標(biāo)簽,而 slots().default 只會(huì)傳遞第二個(gè)匿名段落標(biāo)簽,slots().foo 會(huì)傳遞第一個(gè)具名段落標(biāo)簽。同時(shí)擁有 children 和 slots() ,因此你可以選擇讓組件通過(guò) slot() 系統(tǒng)分發(fā)或者簡(jiǎn)單的通過(guò) children 接收,讓其他組件去處理。
模板編譯
你可能有興趣知道,Vue 的模板實(shí)際是編譯成了 render 函數(shù)。這是一個(gè)實(shí)現(xiàn)細(xì)節(jié),通常不需要關(guān)心,但如果你想看看模板的功能是怎樣被編譯的,你會(huì)發(fā)現(xiàn)會(huì)非常有趣。下面是一個(gè)使用 Vue.compile 來(lái)實(shí)時(shí)編譯模板字符串的簡(jiǎn)單 demo:
render:
function anonymous(
) {
with(this){return _h('div',[_m(0),(message)?_h('p',[_s(message)]):_h('p',["No message."])])}
}staticRenderFns:
_m(0): function anonymous(
) {
with(this){return _h('h1',["I'm a template!"])}
} 文章名稱:創(chuàng)新互聯(lián)VUE2教程:Vue.js2.0Render函數(shù)
分享路徑:http://www.5511xx.com/article/dhsggod.html


咨詢
建站咨詢
