Vue Render 函数
- 基础
需要 JavaScript 的完全编程的能力,这就是 render 函数,它比 template 更接近编译器。 - createElement 参数
// @returns {VNode} createElement( // {String | Object | Function} // 一个 HTML 标签,组件选项,或一个函数 // 必须 Return 上述其中一个 'div', // {Object} // 一个对应属性的数据对象 // 您可以在 template 中使用.可选项. { // (下一章,将详细说明相关细节) }, // {String | Array} // 子节点(VNodes). 可选项. [ createElement('h1', 'hello world'), createElement(MyComponent, { props: { someProp: 'foo' } }), 'bar' ] )
- 完整数据对象
有一件事要注意:在 templates 中,v-bind:class
和v-bind:style
,会有特别的处理,他们在 VNode 数据对象中,为最高级配置。
{ // 和`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 }, // 自定义指令. 注意事项:不能对绑定的旧值设值 // 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 => h('span', props.text) }, // 如果子组件有定义 slot 的名称 slot: 'name-of-slot' // 其他特殊顶层属性 key: 'myKey', ref: 'myRef' }
- 完整示例
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 必须唯一
- 使用 JavaScript 代替模板功能
- v-if and v-for
无论什么都可以使用原生的 JavaScript 来实现,Vue 的 render 函数不会提供专用的 API。
<ul v-if="items.length"> <li v-for="item in items">{{ item.name }}</li> </ul> <p v-else>No items found.</p>
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.') } }
- v-model
no directv-model
counterpart in render functions - have to implement the logic yourself:
render: function (createElement) { var self = this return createElement('input', { domProps: { value: self.value }, on: { input: function (event) { self.value = event.target.value } } }) }
- Event & Key Modifiers
Vue提高了on修饰的前缀——
- Slots
- JSX
有一个 Babel plugin 插件,用于在 Vue 中使用 JSX 语法的原因,它可以让我们回到于更接近模板的语法上
import AnchoredHeading from './AnchoredHeading.vue' new Vue({ el: '#demo', render (h) { return ( <AnchoredHeading level={1}> <span>Hello</span> world! </AnchoredHeading> ) } })
- 函数化组件
无状态(没有
data
),无实例(没有this
上下文)。组件需要的一切都是通过上下文传递,包括:
props
: 提供props 的对象children
: VNode 子节点的数组slots
: slots 对象data
: 传递给组件的 data 对象parent
: 对父组件的引用
在添加
functional: true
之后,
锚点标题组件的 render 函数之间简单更新增加context
参数,this.$slots.default
更新为context.children
,
之后this.level
更新为context.props.level
函数化组件只是一个函数,所以渲染开销也低很多。但同样它也有完整的组件封装,你需要知道这些, 比如:
- 程序化地在多个组件中选择一个
- 在将 children, props, data 传递给子组件之前操作它们。
依赖传入 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 对比
<my-functional-component> <p slot="foo"> first </p> <p>second</p> </my-functional-component>
- 模板编译