You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/patterns/computed-v-model.md
+13-11Lines changed: 13 additions & 11 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -3,9 +3,9 @@ import ProxyExample from './computed-v-model/proxy-example.vue'
3
3
</script>
4
4
# Computed with v-model
5
5
6
-
The principle of one-way data flow, with 'props down, events up', is just an extension of the idea that data should only be modified by its owner. This same idea can be extended to other scenarios, such as using Vuex, where the store is considered the owner of the data and so it should only be mutated by the store.
6
+
The principle of one-way data flow, with 'props down, events up', is just an extension of the idea that data should only be modified by its owner. This same idea can be extended to other scenarios, such as using Pinia, where the store is considered the owner of the data and so it should only be mutated by the store.
7
7
8
-
This causes problems when working with `v-model`, which attempts to modify the value directly. We can work around that using a `computed` value with a`get` and `set`.
8
+
This causes problems when working with `v-model`, which attempts to modify the value directly. One way we can address this is by using `computed()`with `get` and `set`.
9
9
10
10
There are more complete examples for [Checkbox](../components/checkbox.html) and [Radio](../components/radio.html) components, but to reduce it down to the essentials with an `<input>`:
11
11
@@ -26,26 +26,28 @@ const inputValue = computed({
26
26
})
27
27
```
28
28
29
-
So for Vuex we might do something like this:
29
+
So for a prop passed down from the parent component we might do something like this:
A similar idea is common when working with data passed down from a parent component:
38
+
Using an event with a name of the form `update:propName` allows it to be used with `v-model` on the parent. The default prop name for this would be `modelValue`. As such, the technique described here is the standard way to 'pass on' a `v-model` from a component's parent to one of its children.
39
+
40
+
A similar approach would work for updating data via a Pinia action:
39
41
40
42
```js
43
+
conststore=useMyStore()
44
+
41
45
constinputValue=computed({
42
-
get: () =>props.value,
43
-
set:newValue=>emit('update:value', newValue)
46
+
get: () =>store.title,
47
+
set:newValue=>store.updateTitle(newValue)
44
48
})
45
49
```
46
50
47
-
Using an event with a name of the form `update:propName` allows it to be used with `v-model` on the parent. The default prop name for this would be `modelValue`. As such, the technique described here is the standard way to 'pass on' a `v-model` from a component's parent to one of its children.
48
-
49
51
## Libraries
50
52
51
53
This pattern is so common that it can be found in composable libraries:
@@ -88,7 +90,7 @@ Putting that all together gives:
88
90
<proxy-example />
89
91
</live-example>
90
92
91
-
While it might look a bit fiddly if you aren't used to working with a `Proxy`, most of this code is very reusable and can be hidden away behind a utility function. We could create a similar utility function for performing the same bit of trickery with an object coming from a Vuex store.
93
+
While it might look a bit fiddly if you aren't used to working with a `Proxy`, most of this code is very reusable and can be hidden away behind a utility function. We could create a similar utility function for performing the same bit of trickery with an object coming from a store.
92
94
93
95
The approach does violate another best practice. The usual recommendation is to avoid mutating the properties of an object returned from a `computed`, as they're considered transient. However, we're breaking that rule knowingly here as mutating those properties is the whole point of the approach.
This allows us to use `this.$http`, just like in Vue 2.
26
26
27
-
You'll find this approach used in some official Vue libraries. For example, Vuex exposes `$store` and Vue Router exposes `$router` and `$route`. The mapping helpers provided by Vuex, e.g. `mapState`, all rely on using `this.$store` to access the correct store. These helpers wouldn't be possible using most of the other techniques described on this page.
27
+
You'll find this approach used in some official Vue libraries. For example, Vue Router exposes `$router` and `$route`. The mapping helpers provided by Vuex, e.g. `mapState`, all rely on using `this.$store` to access the correct store. These helpers wouldn't be possible using most of the other techniques described on this page.
28
28
29
29
However, the use of `globalProperties` in those official libraries shouldn't be seen as an endorsement to use them everywhere. Those libraries have some specific requirements that typically don't apply to application code.
30
30
@@ -70,7 +70,7 @@ But the lack of availability in `setup` is a major problem, so let's consider so
70
70
71
71
## Application-level `provide` / `inject`
72
72
73
-
The standard solution for use with the Composition API is to use `provide` on the application and `inject` in `setup`. The latter will usually be wrapped in a dedicated helper function. We see this approach with Vuex and Vue Router, which have functions such as `useStore` and `useRouter` implemented this way.
73
+
The standard solution for use with the Composition API is to use `provide` on the application and `inject` in `setup`. The latter will usually be wrapped in a dedicated helper function. We see this approach with Vue Router, which has functions such as `useRouter` and `useRoute` implemented this way.
74
74
75
75
Returning to the earlier example for `axios`:
76
76
@@ -148,9 +148,9 @@ import http from './http.js'
148
148
149
149
This flips the dependency. Rather than having a value injected from outside, the dependency is an explicit import from a specific location. The gain in explicitness comes with a small loss in the reusability of the component.
150
150
151
-
ES modules also work well outside of Vue components. For the `axios` example, it's common to need the same `axios` instance in Vuex actions. While there are other ways that can be achieved, using an ES module is the most widely used in real applications.
151
+
ES modules also work well outside of Vue components. For the `axios` example, it's common to need the same `axios` instance in Pinia actions. While there are other ways that can be achieved, using an ES module is the most widely used in real applications.
152
152
153
-
## Sharing state using Pinia or Vuex
153
+
## Sharing state using Pinia
154
154
155
155
It can also be useful to share state globally. We could just use ES modules and Vue's Reactivity APIs to implement shared state ourselves:
156
156
@@ -160,15 +160,16 @@ export default reactive({})
160
160
161
161
Then anything that needs this state object can use `import` to pull it in.
162
162
163
-
However, this roll-your-own approach tends to lead to problems. Standard solutions, such as [Pinia](https://pinia.vuejs.org/) or [Vuex](https://vuex.vuejs.org/), provide several benefits:
163
+
However, using [Pinia](https://pinia.vuejs.org/), the official Vue state management library, provides several benefits:
164
164
165
165
- Documented APIs.
166
166
- Familiarity among developers.
167
167
- Standard usage patterns that, by design, avoid common problems.
168
168
- Integration with Vue Devtools.
169
+
- Third-party plugins.
169
170
- You don't have to maintain the code yourself.
170
171
171
-
So if you need a reactive, central store for your state, you should probably be using either Pinia or Vuex.
172
+
So if you need a reactive, central store for your state, you should probably be using Pinia.
172
173
173
174
## Global mixins
174
175
@@ -216,15 +217,15 @@ If you want the equivalent of `v-if`/`v-else` you can use two slots:
216
217
217
218
The `permission-check` component would still need some way of accessing the 'global' data, and for that it would use one of the other techniques described on this page. The key thing about this approach is that only this one component would need to worry about how the global data is passed around, everything else would just use the component. If you need to refactor how the data is passed around it will only impact this one component.
218
219
219
-
To give a concrete example of how `permission-check` might be implemented in conjunction with Vuex:
220
+
To give a concrete example of how `permission-check` might be implemented in conjunction with Pinia:
220
221
221
222
```js
222
-
import { useStore } from'vuex'
223
+
import { useAuthStore } from'@/store/auth.js'
223
224
224
225
// Using a functional component, so this is effectively just a render function.
225
226
// It doesn't render any VNodes of its own, just those created by its slots.
0 commit comments