Subscribe on changes!

Expect custom-directive to support the composition API

avatar
Jan 12th 2021

What problem does this feature solve?

Can use the composition API in custom directives

What does the proposed API look like?

const app = Vue.createApp({})
app.directive('focus', {
  setup() {
    onMounted(()=> console.log('mounted'))
    onUnmounted(()=> console.log('unmounted'))
    // provide, inject, and other lifecycle hooks etc.
  }
})
avatar
Jan 15th 2021

The so-called composition API, in order to solve the phenomenon that the option API causes the same function code to be scattered, and the related function codes are organized together in an orderly manner, and the custom-directive support the composition API. How should it be achieved?

avatar
Jan 16th 2021

I'm not too sure about this. While I get the stylistic appeal for people who (like me) like the composition API, this adds new internal complexity for us.

Conceptually, directives so far have been though to be stateless as best as possible, whereas wrapping the hooks in setup opens up the possibility of having state in the closure provided by setup. While this opens up a few new possibilities, my worry would be that this leads to a less clean separation of what a component should do (manage UI state and behavior around that state) vs. what a directive should do (add imperative behavior to specific DOM Elements when that is not possible or ergonomic from within a component).

In terms of internal complexity, the lifecycle hooks of directives are meant to be pure functions that the renderer can add as Vnode hooks like this:

<div> onVNodeMounted="myDirective.mounted" onVNodeUnmounted="myDirective.unMounted">
</div>

With a composition style approach, the hooks added by the onMounted() function and its siblings would need to be managed differently internally, adding more complexity to maintain, for little gain if we want to keep a directive's scope of responsibility to what it currently is (see previous point).

Also, in practical terms, I don't see how we could cleanly expose the hook functions from the vue package with these names, as the callbacks passed to them expect different arguments:

// in component
onMounted((/* no arguments */) => ... )

// in directive
onMounted((el, binding, newVNode, oldVNode) => ...)

...the function could serve both purposes depending on the context it is used in, but then I imagine Typescript typings could get messy/problematic.

So Summing this up: This is not a straightforward thing to add, and should be discussed in an RFC if the community feels that it might be actually beneficial.

avatar
Nov 15th 2021

If someone needs this: I was able to create a hacky work-around for this:

https://github.com/dpschen/vue3-directive-script-setup

You have to use custom hooks, so they are not the normal vue ones as mentioned by LinusBorg.