Skip to content

Latest commit

 

History

History
116 lines (80 loc) · 3.84 KB

0012-custom-directive-api-change.md

File metadata and controls

116 lines (80 loc) · 3.84 KB
  • Start Date: 2019-04-09
  • Target Major Version: 3.x
  • Reference Issues: N/A
  • Implementation PR: N/A

Summary

  • Re-design custom directive API so that it better aligns with component lifecycle

  • Custom directives usage on components will follow the same rules as discussed in the Attribute Fallthrough Behavior RFC. It will be controlled by the child component via v-bind="$attrs".

Basic example

Before

const MyDirective = {
  bind(el, binding, vnode, prevVnode) {},
  inserted() {},
  update() {},
  componentUpdated() {},
  unbind() {}
}

After

const MyDirective = {
  beforeMount(el, binding, vnode, prevVnode) {},
  mounted() {},
  beforeUpdate() {},
  updated() {},
  beforeUnmount() {}, // new
  unmounted() {}
}

Motivation

Make custom directive hook names more consistent with the component lifecycle.

Detailed design

Hooks Renaming

Existing hooks are renamed to map better to the component lifecycle, with some timing adjustments. Arguments passed to the hooks remain unchanged.

  • new created (called before vnode props are applied to DOM node)
  • bind -> beforeMount (called after vnode props have been applied to DOM node)
  • inserted -> mounted (called after children have been inserted into the DOM node, and the DOM node itself has been inserted into parent element)
  • new beforeUpdate (called before the element itself is updated)
  • update removed, use updated instead
  • componentUpdated -> updated (called after the element itself and its children have been updated)
  • new beforeUnmount
  • unbind -> unmounted

Usage on Components

In 3.0, with fragments support, components can potentially have more than one root nodes. This creates an issue when a custom directive is used on a component with multiple root nodes.

To explain the details of how custom directives will work on components in 3.0, we need to first understand how custom directives are compiled in 3.0. For a directive like this:

<div v-foo="bar"></div>

Will roughly compile into this:

const vFoo = resolveDirective('foo')

return withDirectives(h('div'), [
  [vFoo, bar]
])

Where vFoo will be the directive object written by the user, which contains hooks like mounted and updated.

withDirectives returns a cloned VNode with the user hooks wrapped and injected as vnode lifecycle hooks (see Render Function API Changes for more details):

{
  onVnodeMounted(vnode) {
    // call vFoo.mounted(...)
  }
}

As a result, custom directives are fully included as part of a VNode's data. When a custom directive is used on a component, these onVnodeXXX hooks are passed down to the component as extraneous props and end up in this.$attrs.

This also means it's possible to directly hook into an element's lifecycle like this in the template, which can be handy when a custom directive is too involved:

<div @vnodeMounted="myHook" />

This is consistent with the attribute fallthrough behavior discussed in vuejs/rfcs#26. So, the rule for custom directives on a component will be the same as other extraneous attributes: it is up to the child component to decide where and whether to apply it. When the child component uses v-bind="$attrs" on an inner element, it will apply any custom directives used on it as well.

Drawbacks

N/A

Alternatives

N/A

Adoption strategy

  • The renaming should be easy to support in the compat build
  • Codemod should also be straightforward
  • For directives used on components, the warning on unused $attrs as discussed in Attribute Fallthrough Behavior should apply as well.

Unresolved questions

N/A