Skip to content
On this page

Custom Directives

Challenge

For Custom Directives, some lifecycle hook names changed to align with those used in components. also, accessing the current component's instance works different in both versions.

Solution

Writing cross-compatible custom directives is possible with the use of the defineDirective() helper function. You write your directive for Vue 3 and the helper takes care of the necessary conversions for Vue 2:

  • Vue 3 no-op, returns custom directive definition as-is
  • Vue 2 renames lifecycle hooks to their Vue 2 counterparts

It will also throw warnings when use use a hook that has no counterpart in one of the versions (see Mapping below).

Mapping

Vue 3Vue 2
createddo not use
beforeMountbind
mountedinserted
beforeUpdatedo not use
do not useupdate
updatedcomponentUpdated
beforeUnmountdo not use
unmountedunbind

Example

import { defineDirective } from '@vue-bridge/runtime'

export const myDirective = defineDirective({
  beforeMount(el, binding, vnode) {

  },
  unmounted(el, binding, vnode)

})

Accessing the component instance

In some edge cases, it might be necessary to get access to the component instance. This works differently in Vue 2 and Vue 3:

  • In Vue 2, you can access the instance via vnode.context
  • In Vue 3, you can access the instance via binding.instance

To make this easier, defineDirective() adds the binding.instance property when running in Vue 2, so you can use it like you are in Vue 3.

For Usage with Typescript, the following might be better to keep types working:

import { isVue2 } from '@vue-bridge/runtime'

const myDirective = defineDirective({
  beforeMount(el, binding, vnode) {
  const vm = isVue2 ? vnode.context, binding.instance
  }
})

Working with VNodes

As vnodes are totally different in Vue 2 and Vue 3, it's recommended to not access them in custom directives.

You can try and find a way that works for both by using vue-demi

Caveat: Directive on Components

When using a directive on a component, the instance will be different in Vue 2 and Vue 3:

  • in Vue 2, vnode.context gives you the parent component instance that set the directive on the child component.
  • in Vue 3, binding.instance would give your the child component instance that the directive was used on.

This can lead to bugs, so be aware of it.

Released under the MIT license