This chapter mainly talks about how to collect and monitor the elements in data when render.
Let’s look at this line of code:
new Vue({
template:
`<div>
<span>span1:</span> {{span1}}
<span>span2:</span> {{span2}}
<div>`,
data: {
span1: 'span1',
span2: 'span2',
span3: 'span3'}});Copy the code
There is a problem with data that relies on collecting and listening methods to bind as we understood before — SPAN3 is not used in the actual template, however when the data of SPAN3 is modified (this.span3 = ‘SPAN4’), This would also trigger the setter for SPAN3 causing the render to be reexecuted, which is obviously not what we want.
Dep
When modifying the value of an object on data will trigger its setter, so the getter event will be triggered when the value is set, so we just need to render once at the initial initialization. All the data in the rendered dependent data is collected by the getter into the SUBs of the Dep. Setters only fire the subs function of the Dep when modifying the data in the data.
Define a dependency collection class Dep.
class Dep {
constructor() { this.subs = []; AddSub (sub: Watcher) {this.subs.push(sub)} /** Remove dependencies **/ removeSub (sub: Watcher) {remove (enclosing subs, sub)} Github:https://github.com/answershuto * / / / * * * to rely on the traversal notice * * /notify () {
// stabilize the subscriber list first
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
function remove (arr, item) {
if (arr.length) {
const index = arr.indexOf(item)
if (index > -1) {
return arr.splice(index, 1)
}
}
Copy the code
Watcher
The subscriber, when relying on collection, will addSub to sub. When modifying data in data, the notify of deP object will be triggered to notify all Watcher objects to modify the corresponding view.
class Watcher { constructor (vm, expOrFn, cb, options) { this.cb = cb; this.vm = vm; */ dep. target = this; / / dep. target = this; Github:https://github.com/answershuto * / / / * * trigger rendering operations rely on collecting * / this. Cb. Call (enclosing the vm); }update() { this.cb.call(this.vm); }}Copy the code
Start relying on collections
class Vue {
constructor(options) {
this._data = options.data;
observer(this._data, options.render);
letwatcher = new Watcher(this, ); }}functionDefineReactive (obj, key, val, cb) {const Dep = new Dep(); Object.defineProperty(obj, key, { enumerable:true,
configurable: true,
get: ()=>{
if(dep.target) {/* The Watcher object exists in the global dep.target */ dep.addSub(dep.target); }},set:newVal=> {/* Only functions in addSub trigger */ dep.notify(); } }) } Dep.target = null;Copy the code
Assign the observer Watcher instance to the global DEP. target, and then trigger the Render operation to collect dependencies only for objects marked by dep. target. An object with dep. target pushes an instance of Watcher into the subs, and Dep calls the Update method of the Watcher instance in the subs when the object is modified to initiate setter operations.
Detailed process can reference before I write the Vue shallow source (3) – initMixin (below), concrete is introduced in this Dep, Watcher, defineReactive method.
Thank you for your ideas. If you like, you can give me a star, Github