Detailed description of Vue event driver and dependency tracking, detailed description of vue event tracking
A previous analysis on Vue data binding principles needs to be reviewed recently and sent to the essay by the way.
In the previous implementation of an Mvvmsetter
To observemodel
.viewModel
Bindmodel
. Whenmodel
Change, update allviewModel
To render the new value to the interface. At the same time, throughv-model
All boundinput
And passaddEventListener
Event will be updatedmodel
To complete two-way binding.
But that program is not only used to understanddefineProperty
And others are worthless.
- No compilation node.
- No processing expression dependency.
Here I will solve the problem of expression dependency. I will introduce the vue template compilation in the next section.
Define getter & setter for Data
class Observer { constructor(data) { this._data = data; this.walk(this._data); } walk(data) { Object.keys(data).forEach((key) => { this.defineRective(data, key, data[key]) }) }; defineRective(vm, key, value) { var self = this; if (value && typeof value === "object") { this.walk(value); } Object.defineProperty(vm, key, { get: function() { return value; }, set: function(newVal) { if (value != newVal) { if (newVal && typeof newVal === "object") { self.walk(newVal); } value = newVal; } } }) }}module.exports = Observer;
In this way, each attribute is addedgetter
Andsetter
When the attribute is an object, it is recursively added.
Once the attribute value is obtained or assigned a value to the attributeget
Orset
Whenset
, That ismodel
You can publish a message to notify allviewModel
Update.
DefineRective (vm, key, value) {// stores the dependency expression of this attribute in the closure. Var dep = new Dep (); var self = this; if (value & typeof value = "object") {this. walk (value);} Object. defineProperty (vm, key, {get: function () {return value;}, set: function (newVal) {if (value! = NewVal) {if (newVal & typeof newVal = "object") {self. walk (newVal);} value = newVal; // notify all viewmodels to update dep. notify ();}}})}
How to defineDep
What about ??
Class Dep {constructor () {// dependency list this. dependences = [];} // Add dependency addDep (watcher) {if (watcher) {this. dependences. push (watcher) ;}}// notify all dependencies to update notify () {this. dependences. forEach (watcher) => {watcher. update () ;}} module. exports = Dep;
Each dependency here isWatcher
.
See how to defineWatcher
EachWatcher
There will be a uniqueid
It has an expression and a callback function.
For example, expression a +b
; Will be accessed during get computinga
Andb
Because JavaScript is a single thread, only one JavaScript code is executed at any time.Dep.target
As a global variable to indicate the currentWatcher
And thencompute
Accessa
,b
, Triggera
Andb
Ofgetter
In getter Dep.target
Add as dependency.
Oncea
Andb
Ofset
Trigger, callupdate
Function to update the dependent value.
Var uid = 0; class Watcher {constructor (viewModel, exp, callback) {this. viewModel = viewModel; this. id = uid ++; this. exp = exp; this. callback = callback; this. oldValue = ""; this. update ();} get () {Dep.tar get = this; var res = this. compute (this. viewModel, this. exp); Dep.tar get = null; return res;} update () {var newValue = this. get (); if (this. oldValue = newValue) {return;} // update Dom in callback this. callback (newValue, this. oldValue); this. oldValue = newValue;} compute (viewModel, exp) {var res = replaceWith (viewModel, exp); return res ;}} module. exports = Watcher;
Because the current expression needs to be executed under the current model, replaceWith function is used to replace.
Add dependency through get
Object.defineProperty(vm, key, { get: function() { var watcher = Dep.target; if (watcher && !dep.dependences[watcher.id]) { dep.addDep(watcher); } return value; }, set: function(newVal) { if (value != newVal) { if (newVal && typeof newVal === "object") { self.walk(newVal); } value = newVal; dep.notify(); } }})
This method of adding dependencies is too clever.
Here I drew a picture to describe it.
Finally, let's test the code.
const Observer = require('./Observer.js');const Watcher = require('./watcher.js');var data = { a: 10, b: { c: 5, d: { e: 20, } }}var observe = new Observer(data);var watcher = new Watcher(data, "a+b.c", function(newValue, oldValue) { console.log("new value is " + newValue); console.log("oldValue is " + oldValue);});console.log("\r\n");console.log("a has changed to 50,then the expr should has value 55");data.a = 50;console.log("\r\n");console.log("b.c has changed to 50,then the expr should has value 122");data.b.c = 72;;console.log("\r\n");console.log("b.c has reseted an object,then the expr should has value 80");data.b = { c: 30 }
OK
The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.