This is the 23rd day of my participation in Gwen Challenge
One, foreword
In the previous chapter, the relationship between DEP and Watcher in the process of dependency collection was mainly introduced:
Dep. Target is used to record the Watcher instance before the get method in the Watcher class is about to trigger a view update. In object.defineProperty's get method, deP is associated with watcher by having the deP of the data remember the render Watcher, and the view will only be updated if the data involved in the view rendering changesCopy the code
This article continues to rely on the collected view update section
Second, implement view update logic
1. Check weight watcher
Question: What happens if the same data is used more than once in a view?
According to current logic, when the same data is used multiple times in a view, the same Watcher is saved multiple times in the DEP
<div id="app">
<li>{{name}}</li>
<li>{{name}}</li>
<li>{{name}}</li>
</div>
Copy the code
In the deP of name, three of the same render Watcher will be saved
So watcher needs to do a replay check
Add an ID to the Watcher instance. Each time the new Watcher id increases, the new Watcher id will be used as a flag to check the Watcher instanceCopy the code
let id = 0;
class Watcher {
constructor(vm, fn, cb, options){
this.vm = vm;
this.fn = fn;
this.cb = cb;
this.options = options;
this.id = id++; // Watcher unique tag
this.getter = fn;
this.get();
}
get(){
Dep.target = this;
this.getter();
Dep.target = null; }}export default Watcher
Copy the code
2. Make Watcher remember deP, too
Similarly, watcher needs to remember the DEP as well
let id = 0;
class Dep {
constructor(){
this.id = id++;
this.subs = [];
}
// Make Watcher remember deP and deP remember Watcher
depend(){
AddDep: makes the current watcher remember the dep
Dep.target.addDep(this);
}
// let deP remember watcher - be called in watcher
addSub(watcher){
this.subs.push(watcher);
}
}
Dep.target = null; // Static property to record the current watcher
export default Dep;
Copy the code
Why is it implemented this way?
If you want to remember each other, in Watcher you need to do a duplicate check on deP. The deP also does a duplicate check on watcher; After associating deP with Watcher in this way, you only need to judge onceCopy the code
import Dep from "./dep";
let id = 0;
class Watcher {
constructor(vm, fn, cb, options){
this.vm = vm;
this.fn = fn;
this.cb = cb;
this.options = options;
this.id = id++;
this.depsId = new Set(a);// The unique ID used to save the deP instance for the current watcher
this.deps = []; // For the current watcher to save the DEP instance
this.getter = fn;
this.get();
}
addDep(dep){
let did = dep.id;
/ / dep to check again
if(!this.depsId.has(did)){
// Make Watcher remember deP
this.depsId.add(did);
this.deps.push(dep);
// Let deP remember Watcher
dep.addSub(this); }}get(){
Dep.target = this;
this.getter();
Dep.target = null; }}export default Watcher;
Copy the code
This implementation keeps DEP and Watcher in a relative relationship:
If you have deP in Watcher; So there must be watcher in deP; If there is no DEP in Watcher; There must be no Watcher in deP; Therefore, deP and Watcher can be checked only once.Copy the code
3. Data changes, triggering view updates
The set method that goes into Object.defineProperty when the view is updated needs to inform all watcher collections in the DEP to execute the view update method in the set methodCopy the code
// src/observe/index.js#defineReactive
function defineReactive(obj, key, value) {
observe(value);
let dep = new Dep(); // Add a deP for each attribute
Object.defineProperty(obj, key, {
get() {
if(Dep.target){
dep.depend();
}
return value;
},
set(newValue) {
if (newValue === value) return
observe(newValue);
value = newValue;
dep.notify(); // Notify all watcher collected in the current DEP to perform view updates in turn}})}Copy the code
4. Add notify to Dep:
let id = 0;
class Dep {
constructor(){
this.id = id++;
this.subs = [];
}
depend(){
Dep.target.addDep(this);
}
addSub(watcher){
this.subs.push(watcher);
}
// All the watcher collected in deP executes the update method update in sequence
notify(){
this.subs.forEach(watcher= > watcher.update())
}
}
Dep.target = null;
export default Dep;
Copy the code
5. Add update method to Watcher
import Dep from "./dep";
let id = 0;
class Watcher {
constructor(vm, fn, cb, options){
this.vm = vm;
this.fn = fn;
this.cb = cb;
this.options = options;
this.id = id++;
this.depsId = new Set(a);this.deps = [];
this.getter = fn;
this.get();
}
addDep(dep){
let did = dep.id;
if(!this.depsId.has(did)){
this.depsId.add(did);
this.deps.push(dep);
dep.addSub(this); }}get(){
Dep.target = this;
this.getter();
Dep.target = null;
}
// Execute the view rendering logic
update(){
this.get(); }}export default Watcher;
Copy the code
Problem of 6.
Frequently updating the same data multiple times causes the view to be re-rendered frequently
let vm = new Vue({
el: '#app'.data() {
return { name: "Brave" , age: 123}}}); vm.name ="Brave Wang";
vm.name = "Brave";
vm.name = "Brave Wang";
vm.name = "Brave";
vm.name = "Brave Wang";
vm.name = "Brave";
Copy the code
The value of name changes 6 times, but in the end there is no change. Brave is still Brave, so we need to change to the mechanism of asynchronous updateCopy the code
Three, the end
This article introduces the view update part of Vue dependency collection, mainly covering the following points:
View initialization:
- The render method takes the value and goes to the Get method of Object.defineProperty
- The get method adds the DEP to the data and records the current render watcher
- Record mode: Watcher checks the replay and remembers deP, and DEP remembers Watcher
When data is updated:
- When the data is changed, the set method of Object.defineProperty is entered
- In the set method, make all the watcher collected in the DEP perform the view render operation watcher.get()
- The current render watcher is recorded through dep.target before the view renders (before the this.getter method executes)
- Repeat the view initialization process
Next: Vue asynchronous update
Maintenance logs:
20210802: Modify the abstract