DEV Community

Cover image for Vue.js Components Communication Patterns (without Vuex) - Part 1
Chris Y.
Chris Y.

Posted on

Vue.js Components Communication Patterns (without Vuex) - Part 1

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:

Image description

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:

Image description

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:

Image description

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:

Image description

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:

Image description

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:

Image description

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:

Image description

As the event name is changed, in ChildA now it needs to emit the new event name to trigger the event:

Image description

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:

Image description

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-model in the <template>, it will generate v-bind:value="xxx" and v-on:input="val => this.xxx = val".
  • for any v-bind:value.sync="xxx" in the <template>, it will generate v-bind:value="xxx" and v-on:update:value="val => this.xxx = val".
  • for any event bound onto a child component tag:
    <Child
      @anyEvent="parentUpdate"
    />
Enter fullscreen mode Exit fullscreen mode

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)