-
Notifications
You must be signed in to change notification settings - Fork 4.6k
/
Copy pathcomposition-api.md
273 lines (203 loc) Β· 7.3 KB
/
composition-api.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# Composition API
> This section uses [single-file component](../guide/single-file-component.html) syntax for code examples
## `setup`
A component option that is executed **before** the component is created, once the `props` are resolved. It serves as the entry point for composition APIs.
- **Arguments:**
- `{Data} props`
- `{SetupContext} context`
Similar to `this.$props` when using Options API, the `props` object will only contain explicitly declared props. Also, all declared prop keys will be present on the `props` object, regardless of whether it was passed by the parent component or not. Absent optional props will have a value of `undefined`.
If you need to check the absence of an optional prop, you can give it a Symbol as its default value:
```js
const isAbsent = Symbol()
export default {
props: {
foo: { default: isAbsent }
},
setup(props) {
if (props.foo === isAbsent) {
// foo was not provided.
}
}
}
```
- **Typing**:
```ts
interface Data {
[key: string]: unknown
}
interface SetupContext {
attrs: Data
slots: Slots
emit: (event: string, ...args: unknown[]) => void
expose: (exposed?: Record<string, any>) => void
}
function setup(props: Data, context: SetupContext): Data
```
::: tip
To get type inference for the arguments passed to `setup()`, the use of [defineComponent](global-api.html#definecomponent) is needed.
:::
- **Example**
With the template:
```vue-html
<!-- MyBook.vue -->
<template>
<div>{{ readersNumber }} {{ book.title }}</div>
</template>
<script>
import { ref, reactive } from 'vue'
export default {
setup() {
const readersNumber = ref(0)
const book = reactive({ title: 'Vue 3 Guide' })
// expose to template
return {
readersNumber,
book
}
}
}
</script>
```
With render function:
```js
// MyBook.vue
import { h, ref, reactive } from 'vue'
export default {
setup() {
const readersNumber = ref(0)
const book = reactive({ title: 'Vue 3 Guide' })
// Please note that we need to explicitly use ref value here
return () => h('div', [readersNumber.value, book.title])
}
}
```
If you return a render function then you can't return any other properties. If you need to expose properties so that they can be accessed externally, e.g. via a `ref` in the parent, you can use `expose`:
```js
// MyBook.vue
import { h } from 'vue'
export default {
setup(props, { expose }) {
const reset = () => {
// Some reset logic
}
// Expose can only be called once.
// If you need to expose multiple properties, they must all
// be included in the object passed to expose.
expose({
reset
})
return () => h('div')
}
}
```
- **See also**: [Composition API `setup`](../guide/composition-api-setup.html)
## Lifecycle Hooks
Lifecycle hooks can be registered with directly-imported `onX` functions:
```js
import { onMounted, onUpdated, onUnmounted } from 'vue'
const MyComponent = {
setup() {
onMounted(() => {
console.log('mounted!')
})
onUpdated(() => {
console.log('updated!')
})
onUnmounted(() => {
console.log('unmounted!')
})
}
}
```
These lifecycle hook registration functions can only be used synchronously during [`setup()`](#setup), since they rely on internal global state to locate the current active instance (the component instance whose `setup()` is being called right now). Calling them without a current active instance will result in an error.
The component instance context is also set during the synchronous execution of lifecycle hooks. As a result, watchers and computed properties created synchronously inside of lifecycle hooks are also automatically tore down when the component unmounts.
- **Mapping between Options API Lifecycle Options and Composition API**
- ~~`beforeCreate`~~ -> use `setup()`
- ~~`created`~~ -> use `setup()`
- `beforeMount` -> `onBeforeMount`
- `mounted` -> `onMounted`
- `beforeUpdate` -> `onBeforeUpdate`
- `updated` -> `onUpdated`
- `beforeUnmount` -> `onBeforeUnmount`
- `unmounted` -> `onUnmounted`
- `errorCaptured` -> `onErrorCaptured`
- `renderTracked` -> `onRenderTracked`
- `renderTriggered` -> `onRenderTriggered`
- `activated` -> `onActivated`
- `deactivated` -> `onDeactivated`
- **See also**: [Composition API lifecycle hooks](../guide/composition-api-lifecycle-hooks.html)
## Provide / Inject
`provide` and `inject` enables dependency injection. Both can only be called during [`setup()`](#setup) with a current active instance.
- **Typing**:
```ts
interface InjectionKey<T> extends Symbol {}
function provide<T>(key: InjectionKey<T> | string, value: T): void
// without default value
function inject<T>(key: InjectionKey<T> | string): T | undefined
// with default value
function inject<T>(key: InjectionKey<T> | string, defaultValue: T): T
// with factory
function inject<T>(
key: InjectionKey<T> | string,
defaultValue: () => T,
treatDefaultAsFactory: true
): T
```
Vue provides an `InjectionKey` interface which is a generic type that extends `Symbol`. It can be used to sync the type of the injected value between the provider and the consumer:
```ts
import { InjectionKey, provide, inject } from 'vue'
const key: InjectionKey<string> = Symbol()
provide(key, 'foo') // providing non-string value will result in error
const foo = inject(key) // type of foo: string | undefined
```
If using string keys or non-typed symbols, the type of the injected value will need to be explicitly declared:
```ts
const foo = inject<string>('foo') // string | undefined
```
- **See also**:
- [Provide / Inject](../guide/component-provide-inject.html)
- [Composition API Provide / Inject](../guide/composition-api-provide-inject.html)
## `getCurrentInstance`
`getCurrentInstance` enables access to an internal component instance.
:::warning
`getCurrentInstance` is only exposed for advanced use cases, typically in libraries. Usage of `getCurrentInstance` is strongly discouraged in application code. Do **NOT** use it as an escape hatch to get the equivalent of `this` in Composition API.
:::
```ts
import { getCurrentInstance } from 'vue'
const MyComponent = {
setup() {
const internalInstance = getCurrentInstance()
internalInstance.appContext.config.globalProperties // access to globalProperties
}
}
```
`getCurrentInstance` **only** works during [setup](#setup) or [Lifecycle Hooks](#lifecycle-hooks)
> When using outside of [setup](#setup) or [Lifecycle Hooks](#lifecycle-hooks), please call `getCurrentInstance()` on `setup` and use the instance instead.
```ts
const MyComponent = {
setup() {
const internalInstance = getCurrentInstance() // works
const id = useComponentId() // works
const handleClick = () => {
getCurrentInstance() // doesn't work
useComponentId() // doesn't work
internalInstance // works
}
onMounted(() => {
getCurrentInstance() // works
})
return () =>
h(
'button',
{
onClick: handleClick
},
`uid: ${id}`
)
}
}
// also works if called on a composable
function useComponentId() {
return getCurrentInstance().uid
}
```