vue渲染函數(shù)使用“render”命令。vue中是使用模板HTML語法組建頁面的,使用render函數(shù)可以用js語言來構(gòu)建DOM。因為vue是虛擬DOM,所以在拿到template模板時也要轉(zhuǎn)譯成VNode的函數(shù),而用render()函數(shù)構(gòu)建DOM,vue就免去了轉(zhuǎn)譯的過程。
本教程操作環(huán)境:windows7系統(tǒng)、vue3版,DELL G3電腦。
在絕大多數(shù)情況下,Vue 推薦使用模板語法來創(chuàng)建應(yīng)用。然而在某些使用場景下,我們真的需要用到 JavaScript 完全的編程能力。這時渲染函數(shù)–render就派上用場了。
1. render函數(shù)的介紹
簡單的說,在vue中我們使用模板HTML語法組建頁面的,使用render函數(shù)我們可以用js語言來構(gòu)建DOM。 因為vue是虛擬DOM,所以在拿到template模板時也要轉(zhuǎn)譯成VNode的函數(shù),而用render函數(shù)構(gòu)建DOM,vue就免去了轉(zhuǎn)譯的過程。
當(dāng)使用render函數(shù)描述虛擬DOM時,vue提供一個函數(shù),這個函數(shù)是就構(gòu)建虛擬DOM所需要的工具。官網(wǎng)上給它起了個名字叫createElement。還有約定的簡寫叫h。
1.1 虛擬 DOM
Vue 通過建立一個虛擬 DOM 來追蹤自己要如何改變真實 DOM。請仔細看這行代碼:
return createElement('h1', this.blogTitle)
createElement 到底會返回什么呢?其實不是一個實際的 DOM 元素。它更準(zhǔn)確的名字可能是createNodeDescription,因為它所包含的信息會告訴 Vue 頁面上需要渲染什么樣的節(jié)點,包括及其子節(jié)點的描述信息。我們把這樣的節(jié)點描述為“虛擬節(jié)點 (virtual node)”,也常簡寫它為“VNode”?!疤摂M DOM”是我們對由 Vue 組件樹建立起來的整個 VNode 樹的稱呼。
1.2 createElement 接受的參數(shù)
// @returns {VNode} createElement( // {String | Object | Function} // 一個 HTML 標(biāo)簽名、組件選項對象,或者 // resolve 了上述任何一種的一個 async 函數(shù)。必填項。 'div', // {Object} // 一個與模板中屬性對應(yīng)的數(shù)據(jù)對象??蛇x。 { // (詳情見1.3) }, // {String | Array} // 子級虛擬節(jié)點 (VNodes),由 `createElement()` 構(gòu)建而成, // 也可以使用字符串來生成“文本虛擬節(jié)點”。可選。 [ '先寫一些文字', createElement('h1', '一則頭條'), createElement(MyComponent, { props: { someProp: 'foobar' } }) ] )
1.3 render函數(shù)的使用
render:(h) => { return h('div',{ // 給div綁定class屬性 class: { child: true, more: false }, // 給div綁定樣式 style:{ width:'200px', height:'200px', }, // 給div綁定點擊事件 on: { click: () => { console.log('點擊事件') } }, }) }
1.4 深入render函數(shù)數(shù)據(jù)對象
正如 v-bind:class 和 v-bind:style 在模板語法中會被特別對待一樣,它們在 VNode 數(shù)據(jù)對象中也有對應(yīng)的頂層字段。該對象也允許你綁定普通的 HTML attribute,也允許綁定如 innerHTML 這樣的 DOM 屬性 (這會覆蓋 v-html 指令)
{ // 與 `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 屬性 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', // 其它特殊頂層屬性 key: 'myKey', ref: 'myRef', // 如果你在渲染函數(shù)中給多個元素都應(yīng)用了相同的 ref 名, // 那么 `$refs.myRef` 會變成一個數(shù)組。 refInFor: true }
1.5 約束
組件樹中的所有 VNode 必須是唯一的。
這意味著,下面的渲染函數(shù)是不合法的:
render: function (createElement) { var myParagraphVNode = createElement('p', 'hi') return createElement('div', [ // 錯誤 - 重復(fù)的 VNode myParagraphVNode, myParagraphVNode ]) }
如果你真的需要重復(fù)很多次的元素/組件,你可以使用工廠函數(shù)來實現(xiàn)。
例如,下面這渲染函數(shù)用完全合法的方式渲染了 20 個相同的段落:
render: function (createElement) { return createElement('div', Array.apply(null, { length: 20 }).map(function () { return createElement('p', 'hi') }) ) }
2. render函數(shù)的應(yīng)用
2.1 渲染一個簡單的元素
// app.vue (根組件) <template> <div id="app"> <myRender></myRender> </div> </template> <script> import myRender from './components/myRender' export default { components:{ myRender } } </script>
// myRender.vue <script> export default { render:(h) => { return h('div',{ class: { child: true, more: false }, attrs: { id: 'foo', name: 'child' }, style: { width:'100%', height:'200px', }, domProps: { innerHTML: '我是render渲染的子組件' } }) } } </script> <style scoped> .child { background: pink font-size 24px letter-spacing 2px } .more { background: red } </style>
2.2 添加子標(biāo)簽
<script> export default { render:(h) => { return h('div', { class: 'wrapper', attrs: { id: 'wrapper', }, style: { width:'100%', height:'250px' }, },[ h('h2','標(biāo)題'), h('div',{ class: 'content', attrs: { id: 'content', }, style:{ width:'800px', height:'100px' }, domProps:{ innerHTML:'我是內(nèi)容' } }) ] ) } } </script> <style scoped> .wrapper background: pink letter-spacing 2px .content margin 0 auto background: red color #ffffff font-size 24px </style>
2.3 使用 JavaScript 代替模板功能
只要在原生的 JavaScript 中可以輕松完成的操作,Vue 的渲染函數(shù)就不會提供專有的替代方法。
1、v-if 和 v-for 模板語法中:
<ul v-if="items.length"> <li v-for="item in items">{{ item.name }}</li> </ul> <p v-else>No items found.</p> <script> export default { data(){ return{ items:[1,2,3] } } } </script>
render函數(shù)實現(xiàn):
<script> export default { 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.') } }, data(){ return{ items:[1,2,3] } } } </script>
2、v-model
<script> export default { render:function(createElement) { var self = this return createElement('div',[ createElement('div',{class: 'showContent'},self.inputValue), createElement('input',{ class: 'content', domProps:{ value:self.inputValue }, on:{ input:function(event){ self.inputValue = event.target.value } } }) ] ) }, data(){ return{ inputValue:'' } }, watch:{ inputValue:function(){ console.log(this.inputValue) } }, } </script> <style scoped> .showContent font-size 32px letter-spacing 2px .content margin 10px auto color blue font-size 24px </style>
2.4 靜態(tài)插槽
this.$slots的用法
1、父組件
<template> <div id="app"> <myRender> <template v-slot:header> <div > 頭部 </div> </template> <template #footer> <div > 腳部 </div> </template> </myRender> </div> </template> <script> import myRender from './components/myRender' export default { components:{ myRender } } </script>
2、子組件
<script> export default { render:function(createElement) { let childHeader = this.$slots.header let childFooter = this.$slots.footer return createElement( 'div', { class: 'showContent', style:{ width:'100%' } }, [ createElement('div',{class:'childHeader'},childHeader), createElement('div',childFooter), ] ) }, } </script> <style scoped> .showContent letter-spacing 2px background-color red .childHeader color blue font-size 24px </style>
2.5 作用域插槽
this.$scopedSlots的用法
1、父組件
<template> <div id="app"> <myRender :myLayout="layout"> <template slot-scope="childMsg"> <div > {{childMsg.text}} </div> </template> </myRender> </div> </template> <script> import myRender from './components/myRender' export default { data(){ return{ layout:{ header:'頭部', footer:'腳部' } } }, components:{ myRender } } </script>
2、子組件
<script> export default { render:function(createElement) { let self = this return createElement( 'div', { style:{ width:'100%' }, },[ self.$scopedSlots.default({ text: this.myLayout.header }) ] ) }, props:{ myLayout:Object } } </script>
【