This article describes the principles and usage of Vue's two-way binding. Share it for your reference, as follows:
When you need to enter what content in Vue, you will naturally think of using it.<input v-model="xxx" />
to achieve two-way binding. Here is the simplest example
<div > <h2>What's your name:</h2> <input v-model="name" /> <div>Hello {{ name }}</div> </div>
new Vue({ el: "#app", data: { name: "" } });
The contents entered in the input box of this example will be presented later. This is a Vue native pair<input>
Good support is also a typical example of two-way data transfer between parent and child components. butv-model
It is a new feature added to Vue 2.2.0. Before that, Vue only supported one-way data streams.
Vue's one-way data stream
Vue's one-way data flow is similar to React. The parent component can pass data to the child component by setting the properties (Props) of the child component. If the parent component wants to obtain the data of the child component, it must register an event with the child component. This event is triggered when the child component is happy to pass the data. To sum up, Props passes data downwards and events pass data upwards.
The above example, if not usedv-model
, it should be like this
<input :value="name" @input="name = $" />
Since event processing is written inline mode, the script part does not need to be modified. But in most cases, events are generally defined as one method, and the code will be much more complex
<input :value="name" @input="updateName" />
new Vue({ // .... methods: { updateName(e) { = ; } } })
From the above examplev-model
Save a lot of code, the most important thing is to define one less event handling function. sov-model
Actual events include
- use
v-bind
(Right now:
) One-way binding of a property (example::value="name"
) - Bind
input
Events (i.e.@input
) to a default implementation of event handler function (example:@input=updateName
- This default event handler will modify the bound data based on the value brought in by the event object (example:
=
)
Custom component v-model
Vue encapsulates native components, so<input>
It will trigger when inputinput
event. But what should custom components be? Here you might as well use the Todo List example of JsFiddle Vue boilerplate.
Vue boilerplate for JsFiddle
Click the logo of JsFilddle and select the Vue template in the pop-up panel above.
The boilerplate code contains two parts: HTML and Vue(js), and the code is as follows:
<div > <h2>Todos:</h2> <ol> <li v-for="todo in todos"> <label> <input type="checkbox" v-on:change="toggle(todo)" v-bind:checked=""> <del v-if=""> {{ }} </del> <span v-else> {{ }} </span> </label> </li> </ol> </div>
new Vue({ el: "#app", data: { todos: [ { text: "Learn JavaScript", done: false }, { text: "Learn Vue", done: false }, { text: "Play around in JSFiddle", done: true }, { text: "Build something awesome", done: true } ] }, methods: { toggle: function(todo){ = ! } } })
Define Todo components
JsFiddle's Vue template implements the display of a Todo list by default. The data is fixed and all content is completed in one template. The first thing we have to do is to change a single Todo into a child component. Because it cannot be written in JsFiddle as a multi-file form, the component uses()
Defining in scripts is mainly to<li>
The part of the content is taken out:
("todo", { template: ` <label> <input type="checkbox" @change="toggle" :checked="isDone"> <del v-if="isDone"> {{ text }} </del> <span v-else> {{ text }} </span> </label> `, props: ["text", "done"], data() { return { isDone: }; }, methods: { toggle() { = !; } } });
Originally defined in the Apptoggle()
The method has also been slightly changed and defined within the component.toggle()
When calling, it will modify whether it is completed.done
value. But becausedone
It is defined inprops
The attributes in the file cannot be assigned directly, so the first method recommended by the official is adopted to define a dataisDone
, initialized toand use it within the component
isDone
to control whether this state is completed.
The templates and codes of the corresponding App parts have been reduced a lot:
<div > <h2>Todos:</h2> <ol> <li v-for="todo in todos"> <todo :text="" :done=""></todo> </li> </ol> </div>
new Vue({ el: "#app", data: { todos: [ { text: "Learn JavaScript", done: false }, { text: "Learn Vue", done: false }, { text: "Play around in JSFiddle", done: true }, { text: "Build something awesome", done: true } ] } });
But up to this point, the data is still one-way. From the perspective of effect, clicking the check box can feedback the deleted line effect, but these dynamic changes aretodo
Completed internally by the component, there is no problem of data binding.
Add a count for Todo List
To maketodo
The state changes inside the component can be presented in the Todo List. We add counts to the Todo List to show the number of Todos that have been completed. Because this quantity istodo
The influence of the internal state of the component (data) requirestodo
Internal data changes are reflected into its parent component, which is whatv-model
where it works.
This quantity we have in the titlen/m
Form presentation, such as2/4
It means there are 4 Todos in total and 2 have been completed. This requires modification to the template and code parts of Todo List and addingcountDone
andcount
Two computed properties:
<div > <h2>Todos ({{ countDone }}/{{ count }}):</h2> <!-- ... --> </div>
new Vue({ // ... computed: { count() { return ; }, countDone() { return (todo => ).length; } } });
The count is now presented, but changing the task state now will not have an impact on this count. We want to make changes in child components affect the data of parent components.v-model
Let’s talk about it later, use the most common method first, event:
- Subcomponents
todo
existtoggle()
Triggeredtoggle
Events andisDone
As event parameter - Parent component is child component
toggle
Event definition event handling function
("todo", { //... methods: { toggle(e) { = !; this.$emit("toggle", ); } } });
<!-- Other codes in #app --><todo :text="" :done="" @toggle=" = $event"></todo>
Here is@toggle
The bound is an expression. Because heretodo
is a temporary variable, ifmethods
It is difficult to bind this temporary variable to define a special event handler function (of course, it is possible to define ordinary methods through call forms).
Event handling functions generally correspond directly to the things to be processed, such as definitiononToggle(e)
, bound to@toggle="onToggle"
. In this case, it cannot be transmittedtodo
As a parameter.Ordinary method can be defined as
toggle(todo, e)
, in the event definition, in the form of a function call expression:@toggle="toggle(todo, $event)". It and
= $event` same-genuity expression.Note the difference between the two. The former is a bound processing function (reference), and the latter is a bound expression (reference)
Now the expected results have been achieved through events
Transformed into v-model
We said we should use it beforev-model
Implemented, let’s revamp it now. Pay attention to implementationv-model
Several elements of
- Subcomponents pass
value
Properties (Prop) accept input - Subcomponent triggers
input
Event output with array parameters - Used in parent component
v-model
Bind
("todo", { // ... props: ["text", "value"], // <-- Note that done has been changed to value data() { return { isDone: // <-- Note It has been changed to }; }, methods: { toggle(e) { = !; this.$emit("input", ); // <-- Note that the event name has changed } } });
<!-- Other codes in #app --><todo :text="" v-model=""></todo>
.sync implements other data binding
The introduction of Vue 2.2.0 was mentioned earlierv-model
characteristic. For some reason, its input properties arevalue
, but the output event is calledinput
。v-model
、value
、input
These three names are literally inconsistent. Although this seems a bit weird, this is not the point. Is the point that a control can only bind one property in two directions?
Vue 2.3.0 has been introduced.sync
Modifications are used to modifyv-bind
(Right now:
), making it a two-way binding. This is also syntactic sugar, added.sync
The modified data binding will look likev-model
Also, the event handling function is automatically registered to assign values to the bound data. This approach also requires that the subcomponent trigger a specific event. However, the name of this event is somewhat related to the binding attribute name, and it is added before the binding attribute nameupdate:
Prefix.
for example<sub :="any" />
Put the child component'ssome
Properties and parent componentany
Data binding is required to pass in subcomponents$emit("update:some", value)
to trigger the change.
In the above example, usev-model
The binding always feels a bit awkward becausev-model
The literal meaning of this is to bind a numeric value in two directions, indicating whether it is not completeddone
It is actually a state, not a value. So we modify it again and still usedone
This property name (notvalue
),pass.sync
to achieve two-way binding.
("todo", { // ... props: ["text", "done"], // <-- Restore to done data() { return { isDone: // <-- Restore to done }; }, methods: { toggle(e) { = !; this.$emit("update:done", ); // <-- Event name: update:done } } });
<!-- Other codes in #app --><!-- Notice v-model Become :,Don't forget the colon --> <todo :text="" :=""></todo>
Revealing Vue two-way binding
Through the above description, I think everyone should have understood that Vue's two-way binding is actually done by ordinary one-way binding and event combination, but throughv-model
and.sync
The default processing function is registered to update the data. There is such a paragraph in the Vue source code
// @file: src/compiler/parser/ if () { addHandler( el, `update:${camelize(name)}`, genAssignmentCode(value, `$event`) ) }
As can be seen from this code,.sync
When binding on two-way, the compiler will add aupdate:${camelize(name)}
event handler function to assign data (genAssignmentCode
literally means code that generates assignments).
Outlook
Currently, Vue's two-way binding also needs to trigger events to realize data backhaul. This is still a certain difference from many expected assignment back passes. There are two main reasons for this gap
- Need to return data through events
- Properties (prop) cannot be assigned
In the current version of Vue, simplification can be achieved by defining computed properties, such as
computed: { isDone: { get() { return ; }, set(value) { this.$emit("update:done", value); } } }
To be honest, it is quite brain-consuming to define more variable names with the same meaning and different names. Hopefully, Vue can reduce this process through certain technical means in future versions, such as adding Prop declarations.sync
Options, just declaresync: true
All of them can be assigned directly and automatically triggeredupdate:xxx
event.
Of course, as a framework, when solving a problem, we must also consider the impact on other features and the scalability of the framework. Therefore, we will wait and see how the two-way binding will evolve in the end.
Interested friends can use itOnline HTML/CSS/JavaScript code running tool:http://tools./code/HtmlJsRunTest the above code running effect.
I hope this article will be helpful to everyone's programming.