Custom v-model and .sync patterns
About this series
Communication between components is one of the core parts of frontend frameworks. First, same as React, Vue's data flow is also one-way. You might have come across "two-way data flow using v-model" in some tutorials, but that is not accurate. Data flow in Vue is unidirectional. Vuex is great for sharing data between components, but when it comes to some scenarios such as building a UI library or a state management library, Vuex is not suitable anymore, and we need to utilize the build-in mechanism of Vue itself.
Custom v-model pattern
Here we have two components - Parent and ChildA:
Parent displays a list of desserts which is an object array defined in the data option, and also passes it to ChildA's prop value. It also binds an event named input onto the ChildA instance, with the callback function being its parentUpdate method.
Now let's look at ChildA:
ChildA also has a reactive data field named desserts and its value is from the prop value that Parent passes in. At line 29, notice the spread operator - because of reference type, we need to make a copy of the data passed in by props. Otherwise, line 34 will modify the prop array directly, which will break Vue's one-way data flow.
This is how the page looks at the moment:
When the button is clicked, ChildA will emit the input event bound onto it by Parent, and the argument passed to
Parent.parentUpdate() is ChildA.desserts. At line 47 in Parent.parentUpdate(), Parent updates its desserts using the value passed from ChildA. Now the view:
This pattern can make sure some data in Parent and ChildA is "in sync". Data still flows one-way as we can clearly see above.
We can also make the code a bit more concise by writing the function inline:
because the logic here is just an assignment statement, we don't really need to define a method, and it works as before. Also, because this type of code is very common in application development, Vue has a shortcut syntax for it: v-model. If we replace the <ChildA> tag in Parent with this one:
We get the exact same result.
In addition, you can change the prop and event name using the model option https://vuejs.org/v2/api/#model.
.sync pattern
Since the event name is just a string registered in the event system as a key, how about in Parent we change the event name bound onto ChildA from input to the string update:value, to be more meaningful. Let's go back to the example before using v-model and update the event name:
As the event name is changed, in ChildA now it needs to emit the new event name to trigger the event:
Run the code, and it works exactly the same as before. And similar to v-model, Vue has another syntax shortcut for it which is v-bind:value.sync. If we update Parent:
The result is the same.
What's under the hood
When Vue uses its compileToFunctions function to compile the <template> of a component:
- for any
v-modelin the<template>, it will generatev-bind:value="xxx"andv-on:input="val => this.xxx = val". - for any
v-bind:value.sync="xxx"in the<template>, it will generatev-bind:value="xxx"andv-on:update:value="val => this.xxx = val". - for any event bound onto a child component tag:
<Child
@anyEvent="parentUpdate"
/>
it will generate an object like so: {anyEvent: parentUpdate}, and this object will be passed onto the Child component instance (of VueComponent type). When the Child component gets this object, it will mount it onto itself via this.$on('anyEvent', parentUpdate). This way, the Child component can trigger its anyEvent event via this.emit('anyEvent', args) ( an event name is just a key registered in Vue's event system), and the callback function registered (a method on the Parent component) will be executed.
Conclusion
The custom v-mode pattern and the .sync pattern work well in many cases, but what if we have a grandchild component and a grand grandchild component? Of course, we can repeat these two patterns in each descendant component, but will it start to become a bit clumsy? And this is what the Part 2 of this series is about.
Also check out the rest articles in this Vue.js components patterns series:
Vue.js Components Communication Patterns (without Vuex) - Part 2
Vue.js Components Communication Patterns (without Vuex) - Part 3
Vue.js Components Communication Patterns (without Vuex) - Part 4
Vue.js Components Communication Patterns (without Vuex) - Part 5
Vue.js Components Communication Patterns (without Vuex) - Part 6
Vue.js Components Communication Patterns (without Vuex) - Part 7









Top comments (0)