small meatballs To see how the Vue response type
After a few months with the big guys in the Vue project, the Vue is starting from 0.
Compared with the previously used regular and angular of the response of the dirty check mechanism, found that the Vue response is hit-type. When using angular or regular, the discovery updates a value, the framework checks for a number of unrelated values, picks out which data is dirty, can update the view, and so on. In the case of Vue computed, it is found that Vue will only examine the corresponding methods of the changed data, and other unrelated data-related methods will not be disturbed.
This article also takes the Vue computed computation attribute realization example, the Revelation Vue response realization. Directory
Look at the Vue. How to respond to a list of chestnuts principle Analysis 1 mixin 2 INITDATAVM 3 DEP message subscriber 4 Thiswatch Summary
1. Give me a chestnut.
When using Vue, computed is often used to dynamically get the value of the property.
Give me a chestnut:
<div id= "Example" >
<input v-model= "Message1"/> <input v-model=
"Message2"/>
<p >computed reversed Message: "{{reversedmessage}}" </p>
</div>
var vm = new Vue ({
el: ' #example ',
data: {
message1: ' Hello ',
message2: ' World '
},
computed: { c6/>//The Getter
reversedmessage:function () {//' this ' of the computed property points to the
VM instance
console.log (' modified ');
Return This.message1.split ("). Reverse (). Join (")}}
"
The above code displays two input boxes on the page, one line showing the text.
Left input box Two-way binding this.message1 variable, right input box two-way binding this.message2 variable, The bottom of the display box is bound this.reversedmessage variable, and is obtained by the computed property, defined as This.message1.split ("). Reverse (). Join (') The flip of the input box string on the left.
Run found the left of the input box changes, the following display text immediately get a response, update the current left input box text Flip, and the console print out changed. No matter how the input box on the right changes, the following display box and console are unchanged.
We found that Vue's computational attributes automatically distinguish who is changing to update dependent values. When this property changes, the function can sniff the change and automatically rerun it. 2. Principle Analysis
We know that the response principle of Vue is to use the getter and setter methods in the Object.defineproperty to collect the dependency in the getter method and respond to the notification in the setter method.
We boldly speculate that the author, in the design of computed, when a property definition is computed, a responsive dependency is associated between the data b of the computed property and the dependent data A, implementing a computed method that relies on A's data b in response to notification when the setter of dependency a is triggered.
Below through the source to uncover it ~
2.1 mixin
Vue properties, injected through mixin
// ...
Initmixin (Vue)
statemixin (Vue) //Injection Watcher Eventmixin
(Vue)
lifecyclemixin (Vue)
rendermixin (Vue)
// ...
Suppose that the official chestnuts:
var watchexamplevm = new Vue ({
el: ' #watch-example ',
data: {
question: ',
answer: ' I cannot give you n answer until ask a question! '
},
Watch: {
//If ' question ' changes, this function will run
question:function (new Question, oldquestion) {
this.answer = ' Waiting for your to stop typing ... '
this.getanswer ()
}
}
});
After the Vue is injected into the watcher, it can have watch function when the VM. $options Watch method call.
After Vue has injected Init, Vue has the init feature. BlaBla 2.2 initdata (VM)
Now Vue has the init feature.
In the official Vue lifecycle diagram, the new Vue () is followed by the observe data process, so let's take a look at what this phase does.
Hand painting InitData Process diagram
First, Vue transfers vm. $options. Data to the VM.
Then, the data and its attributes are transformed in a responsive style.
The source code is as follows:
Vue.prototype._initdata = function () {this. $options =options;
The data under the options is transferred to the VM under let data = This._data=this. $options. Data;
Iterate through all of the data and add a response-type Object.keys (data). ForEach (Key=>this._proxy (key));
Iterate through all of the data and its properties and add a response-type observe (data,this);
}//Vue's response principle, not to mention vue.prototype._proxy = function (key) {var self = this;
Object.defineproperty (self, key, {configurable:true, enumerable:true, Get:function Proxygetter () {
return Self._data[key];
}, Set:function Proxysetter (val) {Self._data[key] = val; }}}//Traverse all data and its properties, add response export function observe (value, VM) {if (!value | | | typeof value!== ' object ') { Return
var ob;
if (Hasown (value, ' __ob__ ') && value.__ob__ instanceof Observer) {ob = value.__ob__; else if (Shouldconvert && IsArray (value) | | isplainobject (value)) && object.isextensible (value) &am p;&!value._isvue){ob = new Observer (value);
Ensure that each property has a __ob__ attribute, it is a Objserver object, which is the response of this thing added} if (ob && vm) {OB.ADDVM (VM);
return OB;
}
Look at the implementation of the Observer class
Export default class observer{
Constructor (value) {
this.value = value
this.walk (value)
}
/ /recursive ... Allow each property to be observe
Walk (value) {
Object.keys (value). ForEach (Key=>this.convert (Key,value[key))
}
convert (Key, Val) {
definereactive (This.value, Key, Val)
}
}
// This function does not understand dep.target it doesn't matter, after saying, first know is to create the response
export function definereactive (obj, key, Val) {
var dep = new DEP ()
var Childob = Observe (val)
object.defineproperty (obj, key, {
enumerable:true,
configurable:true, Get
: () =>{
if (dep.target) {
dep.addsub (dep.target)
} return
val
},
set: newval=> {
var value = val
if (newval = = value) {return
}
val = newval
Childob = Observe (newval)
dep.notify ()
}
)
}
Export function observe (value, VM) {
if (!value | | typeof value!== ' object ') {
return
} return
new Observer (value)
}
To put it bluntly, adding a observer instance to each attribute is designed to implement adding getter and setter to each property to guarantee a response. 2.3 DEP: Message subscriber
Have you ever personally practiced JavaScript's message Notification subscription design pattern? DEP is designed to be a message subscriber.
Export Default class Dep {
constructor () {
this.subs = []
}
//Event Subscription
Addsub (sub) {
This.subs.push (sub)
}
//Event notification
notify () {
This.subs.forEach (sub=>sub.update ())
}
}
A simple event notification subscription model.
Before you look at the Definereactive function:
This function does not understand dep.target it's okay, or it's back.
export function definereactive (obj, key, Val) {
var dep = new dep ()
var childob = Observe (val)
object.defineproperty (obj, key, {
enumerable:true,
configurable:true, get
: () =>{
if (dep.target) {
dep.addsub (dep.target) //Subscription
} return
val
},
set:newval=> {
var value = val
if (newval = = value) {return
}
val = newval
Childob = Observe ( newval)
dep.notify () //Publish
}
}
The hand drew a picture:
Each data and its attributes have a observer that gives the data response, which relies on the global DEP message Subscriber, the getter to subscribe to the message, the instance queue is stored by the DEP instance, the setter is the time to trigger the message, the watcher instance in the queue is notified, Run the event that the user wants.
Now that the Observer property of each data and its properties is implemented successfully and the instance of DEP is subscribed to, then watcher joins the DEP instance queue specifically. 2.4 this. $watch
Hand painting this. $watch process diagram (including the InitData process in the previous section)
Implementation of the Wacher class:
Export default class Watcher {
constructor (VM, EXPORFN, CB) {
THIS.CB = cb
THIS.VM = VM
THIS.EXPORFN = exp ORFN
//2. The key place is here, the Data/property get method
This.value = This.get ()
}
Update () {
this.run () is called here.
}
Run () {
const value = This.get ()
if (value!==this.value) {
this.value = value
this.cb.call ( THIS.VM)
}
get
() {
//1. The point is: put the global variable dep.target just want the current watcher instance
Dep.target = this
const Value = This.vm._data[this.exporfn]
dep.target = null return
value
}
}
In fact, what watcher did.
The first step: Dep.target = This, for the second step, to execute the data get method, tell data, is Watcher trigger get method, not others oh.
Step Two: This.value = This.get (), a GET method that triggers data.
What does the Get method of data do? The code has been posted before.
Review:
Get: () =>{
if (dep.target) {
dep.addsub (dep.target)
} return
val
}
The Get method determines that the Dep.target has a value (that is, a watcher trigger) and relies on the collection. Add the current break-in watcher to the dependency queue for your own DEP instance.
This implements the Vue response process. Concatenate the following figure three.
3. Summary
End.
Reference:
https://segmentfault.com/a/1190000010408657
https://www.imooc.com/article/14466