Vue渲染函数详解(2)

正如在模板语法中,v-bind:class 和 v-bind:style ,会被特别对待一样,在 VNode 数据对象中,下列属性名是级别最高的字段。该对象也允许绑定普通的 HTML 特性,就像 DOM 属性一样,比如 innerHTML (这会取代 v-html 指令)

{ // 和`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 => createElement('span', props.text) }, // 如果组件是其他组件的子组件,需为插槽指定名称 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 必须是唯一的。这意味着,下面的 render function 是无效的:

render: function (createElement) { var myParagraphVNode = createElement('p', 'hi') return createElement('div', [ // 错误-重复的 VNodes myParagraphVNode, myParagraphVNode ]) }

如果真的需要重复很多次的元素/组件,可以使用工厂函数来实现。例如,下面这个例子 render 函数完美有效地渲染了 20 个重复的段落:

render: function (createElement) { return createElement('div', Array.apply(null, { length: 20 }).map(function () { return createElement('p', 'hi') }) ) }

JS代替模板

【v-if和v-for】

由于使用原生的 JavaScript 来实现某些东西很简单,Vue 的 render 函数没有提供专用的 API。比如,template 中的 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>

这些都会在 render 函数中被 JavaScript 的 if/else 和 map 重写:

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】

ender 函数中没有与 v-model 相应的 api,必须自己来实现相应的逻辑:

render: function (createElement) { var self = this return createElement('input', { domProps: { value: self.value }, on: { input: function (event) { self.value = event.target.value self.$emit('input', event.target.value) } } }) }

这就是深入底层要付出的,尽管麻烦了一些,但相对于 v-model 来说,可以更灵活地控制

【事件&按键修饰符】

对于 .passive、.capture 和 .once事件修饰符,Vue 提供了相应的前缀可以用于 on:

Modifier(s) Prefix .passive   & .capture   ! .once     ~ .capture.once   or .once.capture ~!

下面是一个例子

on: { '!click': this.doThisInCapturingMode, '~keyup': this.doThisOnce, `~!mouseover`: this.doThisOnceInCapturingMode }

对于其他的修饰符,前缀不是很重要,因为可以直接在事件处理函数中使用事件方法:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wyzpxz.html