Vue渲染函数详解(3)

Modifier(s) Equivalent in Handler .stop event.stopPropagation() .prevent event.preventDefault() .self if (event.target !== event.currentTarget) return Keys: .enter, .13 if (event.keyCode !== 13) return (...) Modifiers Keys: .ctrl, .alt, .shift, .meta if (!event.ctrlKey) return (...)

下面是一个使用所有修饰符的例子:

on: { keyup: function (event) { // 如果触发事件的元素不是事件绑定的元素 // 则返回 if (event.target !== event.currentTarget) return // 如果按下去的不是 enter 键或者 // 没有同时按下 shift 键 // 则返回 if (!event.shiftKey || event.keyCode !== 13) return // 阻止 事件冒泡 event.stopPropagation() // 阻止该元素默认的 keyup 事件 event.preventDefault() // ... } }

【插槽】

可以从 this.$slots 获取 VNodes 列表中的静态内容:

render: function (createElement) { // `<div><slot></slot></div>` return createElement('div', this.$slots.default) }

还可以从 this.$scopedSlots 中获得能用作函数的作用域插槽,这个函数返回 VNodes:

render: function (createElement) { // `<div><slot :text="msg"></slot></div>` return createElement('div', [ this.$scopedSlots.default({ text: this.msg }) ]) }

如果要用渲染函数向子组件中传递作用域插槽,可以利用 VNode 数据中的 scopedSlots 域:

render (createElement) { return createElement('div', [ createElement('child', { // pass `scopedSlots` in the data object // in the form of { name: props => VNode | Array<VNode> } scopedSlots: { default: function (props) { return createElement('span', props.text) } } }) ]) }

JSX

如果写了很多 render 函数,可能会觉得痛苦

createElement( 'anchored-heading', { props: { level: 1 } }, [ createElement('span', 'Hello'), ' world!' ] )

特别是模板如此简单的情况下:

<anchored-heading :level="1"> <span>Hello</span> world! </anchored-heading>

这就是为什么会有一个 Babel 插件,用于在 Vue 中使用 JSX 语法的原因,它可以让我们回到更接近于模板的语法上

import AnchoredHeading from './AnchoredHeading.vue' new Vue({ el: '#demo', render (h) { return ( <AnchoredHeading level={1}> <span>Hello</span> world! </AnchoredHeading> ) } })

[注意]将 h 作为 createElement 的别名是 Vue 生态系统中的一个通用惯例,实际上也是 JSX 所要求的,如果在作用域中 h 失去作用,在应用中会触发报错

函数式组件

之前创建的锚点标题组件是比较简单,没有管理或者监听任何传递给它的状态,也没有生命周期方法。它只是一个接收参数的函数。

在这个例子中,我们标记组件为 functional,这意味它是无状态 (没有 data),无实例 (没有 this 上下文)

一个 函数式组件 就像这样:

Vue.component('my-component', { functional: true, // 为了弥补缺少的实例 // 提供第二个参数作为上下文 render: function (createElement, context) { // ... }, // Props 可选 props: { // ... } })

[注意]在 2.3.0 之前的版本中,如果一个函数式组件想要接受 props,则 props 选项是必须的。在 2.3.0 或以上的版本中,你可以省略 props 选项,所有组件上的属性都会被自动解析为 props

组件需要的一切都是通过上下文传递,包括:

props:提供 props 的对象 children: VNode 子节点的数组 slots: slots 对象 data:传递给组件的 data 对象 parent:对父组件的引用 listeners: (2.3.0+) 一个包含了组件上所注册的 v-on 侦听器的对象。这只是一个指向 data.on 的别名。 injections: (2.3.0+) 如果使用了 inject 选项,则该对象包含了应当被注入的属性。

在添加 functional: true 之后,锚点标题组件的 render 函数之间简单更新增加 context 参数,this.$slots.default 更新为 context.children,之后this.level 更新为 context.props.level。

因为函数式组件只是一个函数,所以渲染开销也低很多。然而,对持久化实例的缺乏也意味着函数式组件不会出现在 Vue devtools 的组件树里。

在作为包装组件时它们也同样非常有用,比如,当需要做这些时:

1、程序化地在多个组件中选择一个

2、在将 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对比】

为什么同时需要 slots() 和 children。slots().default 不是和 children 类似的吗?在一些场景中,是这样,但是如果是函数式组件和下面这样的 children 呢?

<my-functional-component> <p slot="foo"> first </p> <p>second</p> </my-functional-component>

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

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