Skip to content

Commit 484d597

Browse files
committed
docs (#145): break out custom directives api migration into its own section with updated headings
1 parent b527141 commit 484d597

File tree

3 files changed

+112
-103
lines changed

3 files changed

+112
-103
lines changed

src/.vuepress/config.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,11 @@ const sidebar = {
4848
children: [
4949
{
5050
title: 'Reactivity',
51-
children: ['/guide/reactivity', '/guide/reactivity-fundamentals', '/guide/reactivity-computed-watchers']
51+
children: [
52+
'/guide/reactivity',
53+
'/guide/reactivity-fundamentals',
54+
'/guide/reactivity-computed-watchers'
55+
]
5256
},
5357
{
5458
title: 'Composition API',
@@ -85,7 +89,8 @@ const sidebar = {
8589
children: [
8690
'migration/global-api',
8791
'migration/treeshaking',
88-
'migration/functional-components'
92+
'migration/functional-components',
93+
'migration/custom-directives'
8994
]
9095
},
9196
{

src/guide/migration.md

-101
Original file line numberDiff line numberDiff line change
@@ -92,104 +92,3 @@ Using the example from before, it can now be simplified to:
9292
```
9393

9494
By passing our prop title to the `v-model` directive, Vue will listen for an `onUpdate:title` event that will be emitted from the child component in order to update the data accordingly.
95-
96-
## Custom Directives
97-
98-
### Custom Directive Lifecycle API
99-
100-
Abstractions can be incredibly powerful to development, but they tend to break down when there is no escape hatch or extension beyond the original implementation. Custom directives allow us to create directives that are unique to our application.
101-
102-
Previously, we constructed these directives by using the hooks listed below to target an element’s lifecycle, all of which are optional:
103-
104-
- **bind** - Occurs once the directive is bound to the element. Occurs only once.
105-
- **inserted** - Occurs once the element is inserted into the parent DOM.
106-
- **update** - This hook is called when the element updates, but children haven't been updated yet.
107-
- **componentUpdated** - This hook is called once the component and the children have been updated.
108-
- **unbind** - This hook is called once the directive is removed. Also called only once.
109-
110-
Here’s an example of this:
111-
112-
```html
113-
<p v-highlight="yellow">Highlight this text bright yellow</p>
114-
```
115-
116-
```js
117-
Vue.directive('highlight', {
118-
bind(el, binding, vnode) {
119-
el.style.background = binding.value
120-
}
121-
})
122-
```
123-
124-
Here, in the initial setup for this element, the directive binds a style by passing in a value, that can be updated to different values through the application.
125-
126-
In Vue 3, however, we’ve created a more cohesive API for custom directives. As you can see, they differ greatly from our component lifecycle methods even though we’re hooking into similar events. We’ve now unified them like so:
127-
128-
- bind → **beforeMount**
129-
- inserted → **mounted**
130-
- **beforeUpdate**: new! this is called before the element itself is updated, much like the component lifecycle hooks.
131-
- update → removed! There were too many similarities to updated, so this is redundant. Please use updated instead.
132-
- componentUpdated → **updated**
133-
- **beforeUnmount** new! similar to component lifecycle hooks, this will be called right before an element is unmounted.
134-
- unbind -> **unmounted**
135-
136-
The final API is as follows:
137-
138-
```js
139-
const MyDirective = {
140-
beforeMount(el, binding, vnode, prevVnode) {},
141-
mounted() {},
142-
beforeUpdate() {},
143-
updated() {},
144-
beforeUnmount() {}, // new
145-
unmounted() {}
146-
}
147-
```
148-
149-
The resulting API could be used like this, mirroring the example from earlier:
150-
151-
```html
152-
<p v-highlight="yellow">Highlight this text bright yellow</p>
153-
```
154-
155-
```js
156-
Vue.directive('highlight', {
157-
beforeMount(el, binding, vnode) {
158-
el.style.background = binding.value
159-
}
160-
})
161-
```
162-
163-
Now that the custom directive lifecycle hooks mirror those of the components themselves, they become easier to reason about and remember!
164-
165-
#### Implementation Details
166-
167-
In Vue 3, we’re now supporting fragments, which allow us to return more than one DOM node per component. You can imagine how handy that is for something like a component with multiple lis or the children elements of a table:
168-
169-
```html
170-
<>
171-
<li>Hello</li>
172-
<li>Vue</li>
173-
<li>Devs!</li>
174-
</>
175-
```
176-
177-
As wonderfully flexible as this is, we can potentially encounter a problem with a custom directive that could potentially have multiple root nodes.
178-
179-
As a result, custom directives are included as part of a virtual DOM node’s data. When a custom directive is used on a component, hooks are passed down to the component as extraneous props and end up in this.\$attrs.
180-
181-
[//]: # 'TODO: add in a better example'
182-
183-
[example]
184-
185-
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:
186-
187-
[write a better example with more text when it’s implemented]
188-
189-
```html
190-
<div @vnodeMounted="myHook" />
191-
```
192-
193-
[//]: # 'TODO: add in link'
194-
195-
This is consistent with the attribute fallthrough behavior, so when a child component uses `v-bind="$attrs"` on an inner element, it will apply any custom directives used on it as well.
+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Custom Directives
2+
3+
## Overview
4+
5+
Here is a quick summary of what has changed:
6+
7+
- API has been renamed to better align with component lifecycle
8+
- Custom directives will be controlled by child component via `v-bind="$attrs"`
9+
10+
For more information, read on!
11+
12+
## Introduction
13+
14+
Abstractions can be incredibly powerful to development, but they tend to break down when there is no escape hatch or extension beyond the original implementation. Custom directives allow us to create directives that are unique to our application.
15+
16+
## Previous Syntax
17+
18+
Previously, we constructed these directives by using the hooks listed below to target an element’s lifecycle, all of which are optional:
19+
20+
- **bind** - Occurs once the directive is bound to the element. Occurs only once.
21+
- **inserted** - Occurs once the element is inserted into the parent DOM.
22+
- **update** - This hook is called when the element updates, but children haven't been updated yet.
23+
- **componentUpdated** - This hook is called once the component and the children have been updated.
24+
- **unbind** - This hook is called once the directive is removed. Also called only once.
25+
26+
Here’s an example of this:
27+
28+
```html
29+
<p v-highlight="yellow">Highlight this text bright yellow</p>
30+
```
31+
32+
```js
33+
Vue.directive('highlight', {
34+
bind(el, binding, vnode) {
35+
el.style.background = binding.value
36+
}
37+
})
38+
```
39+
40+
Here, in the initial setup for this element, the directive binds a style by passing in a value, that can be updated to different values through the application.
41+
42+
## Current Syntax
43+
44+
In Vue 3, however, we’ve created a more cohesive API for custom directives. As you can see, they differ greatly from our component lifecycle methods even though we’re hooking into similar events. We’ve now unified them like so:
45+
46+
- bind → **beforeMount**
47+
- inserted → **mounted**
48+
- **beforeUpdate**: new! this is called before the element itself is updated, much like the component lifecycle hooks.
49+
- update → removed! There were too many similarities to updated, so this is redundant. Please use updated instead.
50+
- componentUpdated → **updated**
51+
- **beforeUnmount** new! similar to component lifecycle hooks, this will be called right before an element is unmounted.
52+
- unbind -> **unmounted**
53+
54+
The final API is as follows:
55+
56+
```js
57+
const MyDirective = {
58+
beforeMount(el, binding, vnode, prevVnode) {},
59+
mounted() {},
60+
beforeUpdate() {},
61+
updated() {},
62+
beforeUnmount() {}, // new
63+
unmounted() {}
64+
}
65+
```
66+
67+
The resulting API could be used like this, mirroring the example from earlier:
68+
69+
```html
70+
<p v-highlight="yellow">Highlight this text bright yellow</p>
71+
```
72+
73+
```js
74+
Vue.directive('highlight', {
75+
beforeMount(el, binding, vnode) {
76+
el.style.background = binding.value
77+
}
78+
})
79+
```
80+
81+
Now that the custom directive lifecycle hooks mirror those of the components themselves, they become easier to reason about and remember!
82+
83+
## Implementation Details
84+
85+
In Vue 3, we're now supporting fragments, which allow us to return more than one DOM node per component. You can imagine how handy that is for something like a component with multiple lis or the children elements of a table:
86+
87+
```html
88+
<template>
89+
<li>Hello</li>
90+
<li>Vue</li>
91+
<li>Devs!</li>
92+
</template>
93+
```
94+
95+
As wonderfully flexible as this is, we can potentially encounter a problem with a custom directive that could potentially have multiple root nodes.
96+
97+
As a result, custom directives are now included as part of a virtual DOM node’s data. When a custom directive is used on a component, hooks are passed down to the component as extraneous props and end up in `this.$attrs`.
98+
99+
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:
100+
101+
```html
102+
<div @vnodeMounted="myHook" />
103+
```
104+
105+
This is consistent with the attribute fallthrough behavior, so when a child component uses `v-bind="$attrs"` on an inner element, it will apply any custom directives used on it as well.

0 commit comments

Comments
 (0)