What did the V-Model do
Vue provides us with some very convenient built-in instructions, of which the most used is probably the V-model instruction, but it may be used very often but few people have a deep understanding of how the official implementation of this bi-directional binding instruction. I used my spare time to dig into the source code to analyze Vue@2x, from the template to the completion of the DOM element rendering.
We have defined the following template in our sample code, which basically adds the V-model directive to the input tag. As we enter data in the input field, the contents of our underlying divs are updated synchronously. There are two core aspects of Vue, one is two-way binding of data and the other is data-driven view.
- When we enter data, the V-Model directive is triggered to update the instance property text
- The instance attribute text is a responsive data after Vue initialization during initialization. As long as the data changes, it will drive the re-rendering of the component.
How does the V-Model enter data in the input field and then synchronously update component instance properties?
<-- template --! > <div class="app-wrap"> <input type="text" v-model="text"> <div class="slot">v-model: {{text}}</div> </div> <-- javascript --! > const vm = new Vue({ el: '#app', data: { text: 'Vue' } });Copy the code
After the template is compiled, the ast content is as follows:
{attrsList: [] attrsMap: {class: "app - wrap"} children: [{attrs: [{...}], attrsList: (2) ({...}, {...}), attrsMap: {type: "text", v-model: "text" }, directives: [{ arg: null, end: 98, isDynamicArg: false, modifiers: undefined, name: "model", rawName: "v-model", start: 84, value: "text" }], hasBindings: true plain: false start: 65 static: false staticRoot: false tag: "input" type: 1 }, {type: 3, text: " ", start: 99, end: 106, static: true}, {type: 1, tag: "Div ", attrsList: Array(0), attrsMap: {...}, rawAttrsMap: {...},...}], parent: undefined plain: false {... }} start: 0 static: false staticClass: ""app-wrap"" staticRoot: false tag: "div" type: 1 }Copy the code
To invoke the compiler/codegen/index. In js genDirectives processing instruction
generate -> genElement -> genData -> genDirectives
The genDirectives function in the state-. Directives looks for the policies defined internally in the system according to the directives name and processes the directives with the corresponding policies if they exist. The code is shown below:
const gen: DirectiveFunction = state.directives[dir.name]
Copy the code
/ / dir. Name = = = ‘model’ – > strategy pattern, defined in SRC/platforms/web/compiler directives / * related processing strategy
/ / when the dir. Name = = = model – > SRC/platforms/web/compiler directives/model. Js
/ / if the tag = input && type = text | textarea will add a change to the element (modifier = lazy) | input events
The final render function generated by generate function after codeGen stage is as follows
"with(this) { return _c( 'div', {staticClass:"app-wrap"}, [ _c( 'input', { directives:[{ name:"model", rawName:"v-model", value:(text), expression:"text" }], attrs: {"type":"text"}, domProps:{"value":(text)}, on:{ "input": function($event) { if($event.target.composing) return; text=$event.target.value } } } ), _v(" "), _c( 'div', {staticClass:"slot"}, [_v("v-model:"+_s(text))] ) ] ) }"Copy the code
In this example code, we add the V-model directive to the input tag, so we end up adding a value attribute to the input tag, Value v-model= key in “key” and adds a change (V-model.lazy) or input event to the input to listen for event changes.
When Vue executes render and mounts the real DOM, it binds the DOM to the corresponding properties and events.
When the element is created, will define the hook on the elements through the SRC/core/vdom/patch. The js invokeCreateHooks function performs under defined in platforms/runtime/directives/model. The hook under the js.