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