- 开始时间:2019-04-09
- 目标主要版本:3.x
- 引用 issue:N/A
- 实现的 PR:N/A
摘要
- 重新设计自定义指令的 API,使其与组件的生命周期更好的保持一致。
- 自定义指令在组件上的使用将会遵循 Attribute Fallthrough Behavior RFC 中讨论的相同规则。它将有子组件通过
v-bind="$attrs"控制。
基本范例
之前
const MyDirective = {bind(el, binding, vnode, prevVnode) {},inserted() {},update() {},componentUpdated() {},unbind() {}}
之后
const MyDirective = {beforeMount(el, binding, vnode, prevVnode) {},mounted() {},beforeUpdate() {},updated() {},beforeUnmount() {}, // newunmounted() {}}
动机
使得自定义指令的钩子名称与组件的生命周期更加一致。
具体设计
钩子重命名
现有的钩子被重新命名,以更好的映射到组件的生命周期,并进行进了一些时序调整。传递给钩子的参数保持不变。
- 新的 create(在 vnode 的 props 应用到 DOM 节点之前调用)
- bind 变为 beforeMount(在 vnode 的 props 应用到 DOM 节点之后调用)
- inserted 变为 mounted(在子节点被插入到 DOM 节点,并且 DOM 节点本身被插到父节点之后被调用)
- 新的 beforeUpdate(在元素自身被更新之前调用)
- 移除
update,该用 updated - componentUpdated 变为 updated(在元素本身和其子元素被更新之后调用)
- 新的 beforeUpdated
- unbind 改为 unmounted
在组件上的用法
在 3.0 中,有了 fragment 的支持,组件可能存在多个根节点。当在一个多根节点的组件上使用自定义指令时,这就产生了一个问题。
为了解释自定义指令在 3.0 中如何组件上工作的细节,我们首先要理解自定义指令在 3.0 中是如何被编译的。对于像这样的指令:
<div v-foo="bar"></div>
大致会被编译为:
const vFoo = resolveDirective('foo')return withDirectives(h('div'), [[vFoo, bar]])
其中 vFoo 将是用户写的指令对象,它包含了像 mounted 和 updated 钩子。
withDirective 返回一个克隆的 VNode,用户钩子被包装并作为 vnode 生命周期注入(更多细节见 Render Function API Changes):
{onVnodeMounted(vnode) {// call vFoo.mounted(...)}}
因此,自定义指令被完全包含在 VNode 的数据中。当一个自定义指令被用在一个组件上时,这些 **onVnodeXXX** 钩子被作为无关的 props 传递给组件,并最终进入到 **this.$attrs**。
这也意味着可以像这样在模版中直接 hook 一个元素的生命周期,这在自定义指令过于复杂的情况下很方便。
<div @vnodeMounted="myHook" />
这与 vue/rfcs #26 中讨论的属性破坏性行为是一致的。所以,组件上的自定义指令的规则将和其他外在属性一样:由子组件决定在哪里以及是否应用它。当子组件在内部元素上使用 v-bind="$attrs",它也将应用在它上面使用的任何自定义指令。
缺点
N/A
备选方案
N/A
采纳策略
- 重命名应该很容易在 compat 构建中得到支持。
- Codemod 也应该直截了当。
- 对于在组件上使用指令,Attribute Fallthrough Behavior 中讨论的关于未使用的
$attrs的警告也应该适用。
没有解决的问题
N/A
