Vue 推薦在絕大多數狀況下使用 template 來建立你的 HTML。然而在一些場景中,你真的須要 JavaScript 的徹底編程的能力,這就是 render 函數,它比 template 更接近編譯器。html
export default { name: 'renderTest', data() {}, render: (createElement) => { return createElement(tag, data, context) }, props: {}, methods: {}, created () {} ... }
Vue 經過創建一個虛擬 DOM 對真實 DOM 發生的變化保持追蹤。
render函數提供了一個參數createElement
(能夠簡寫爲h
), 用來生成DOM, 其有三個參數:vue
createElement()
構建而成, 或使用字符串來生成「文本節點」。數據對象詳解git
{ // 和`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' }, // 事件監聽器基於 `on` // 因此再也不支持如 `v-on:keyup.enter` 修飾器 // 須要手動匹配 keyCode。 on: { click: this.clickHandler }, // 僅對於組件,用於監聽原生事件,而不是組件內部使用 `vm.$emit` 觸發的事件。 nativeOn: { click: this.nativeClickHandler }, // 自定義指令。注意,您沒法對綁定中的 `oldValue` 賦值 // Vue 會爲您持續追蹤 directives: [ { name: 'my-custom-directive', value: '2', expression: '1 + 1', arg: 'foo', modifiers: { bar: true } } ], // Scoped slots in the form of // { name: props => VNode | Array<VNode> } scopedSlots: { default: props => createElement('span', props.text) }, // 若是組件是其餘組件的子組件,需爲插槽指定名稱 slot: 'name-of-slot', // 其餘特殊頂層屬性 key: 'myKey', ref: 'myRef' }
v-if 、 v-for 與 v-model
render 函數中沒有與v-if 、 v-for 、v-model 相應的 api, 必須本身來實現相應的邏輯github
render: function (createElement) { if (this.items.length) { return createElement('ul', this.items.map(function (item) { return createElement('li', item) })) } else { return createElement('p', 'No items found.') } }
完整示例
renderTest組件express
export default { name: 'renderTest', data() { return { } }, render: function (createElement) { return createElement( 'h' + this.level, { on: { click: this.clickHandler } }, [ createElement('a', { attrs: { href: '##' } }, this.$slots.default) ] ) }, props: { level: { type: Number, required: true } }, methods: { clickHandler () {} } }
使用組件npm
<renderTest :level="1"> <button> Hello world!</button> </renderTest>
render組件編程
export default { name: 'renderTest', functional: true, props: { render: Function, item: String, index: Number }, render: (h, ctx) => { const params = { item: ctx.props.item, index: ctx.props.index } return ctx.props.render(h, params) } }
咱們將組件記爲 functional,這意味它無狀態(沒有 data),無實例(沒有 this 上下文)。
在添加 functional: true 以後,組件的 render 函數之間將增長 ctx 參數
組件須要的一切都是經過 ctx 傳遞,包括:api
list組件數組
<template> <ul class="bs-list left" style="width:200px;"> <li v-for="(item, i) in listDate" class="list-item" :key="i"> <span>{{item}}</span> <renderTest v-if="renderfun" :item="item" :index="i" :render="renderfun"></renderTest> </li> </ul> </template> <script> import renderTest from './renderTest.js' export default { name: 'lists', components: { renderTest }, props: { listDate: { type: Array }, renderfun: { type: Function, default() { return () => false } } } } </script>
使用組件babel
<template> <div class="text"> <lists :listDate="listDate" :renderfun="renderfun"></lists> </div> </template> <script> import lists from './lists.vue' export default { name: 'text', components: { lists }, data() { return { listDate: [ 'list item 1', 'list item 2', 'list item 3', 'list item 4', 'list item 5' ], renderfun: (h, ctx) => { return h('div', { style: {display: 'inline-block', float: 'right'} }, [ h('button', { class: ['btn-primary'], on: { click: () => { this.listDate.splice(ctx.index, 1) } } }, '刪除') ]) } } } } </script>
安裝babel依賴
npm install babel-plugin-syntax-jsx babel-plugin-transform-vue-jsx babel-helper-vue-jsx-merge-props babel-preset-env --save-dev
.babelrc 文件配置
{ "presets": ["env"], "plugins": ["transform-vue-jsx"] }
書寫格式
render (h) { return ( <div // 普通的屬性 id="foo" // DOM 屬性,添加前綴`domProps` domPropsInnerHTML="bar" // 事件監聽,添加前綴 `on` or `nativeOn` onClick={this.clickHandler} nativeOnClick={this.nativeClickHandler} // 其餘屬性 class={{ foo: true, bar: false }} style={{ color: 'red', fontSize: '14px' }} key="key" ref="ref" slot="slot"> </div> ) }
實例
export default { name: 'renderJSX', data() { return { num: 1 } }, render() { const data = { class: ['b', 'c'] } return (<div class = 'div'> <span>123</span> <button onClick={this.clickHandler} class='btn-primary'>btn</button> <span class='a' {...data}>{this.num}</span> </div>) }, props: { level: { type: Number } }, methods: { clickHandler() { this.num++ console.log('clickHandler') } }, created() {} }
https://vuefe.cn/v2/guide/render-function.html
https://github.com/vuejs/babel-plugin-transform-vue-jsx