vue学习笔记之二:object的变化侦测Watcher

同样的,展示我们可以跑起来的测试代码。当然这是翻看各个类后,按我自己理解整理出来的。原文有export default都取消了,方便一个文件测试。新增一个文件vuetest02.js,将以下代码拷贝

class Dep {
    constructor() {
        this.subs = [];
    }
    addSub(sub) {
        this.subs.push(sub);
    }
    // removeSub(sub) {
    //     remove(this.subs, sub);
    // }
    depend() {
        if (window.target) {
            this.addSub(window.target)
        }
    }
    notify() {
        const subs = this.subs.slice();
        for (let i = 0; i < subs.length; i++) {
            subs[i].update();
        }
    }
}
const bailRE = /[^w.$]/

function parsePath(path){
    if (bailRE.test(path)) {
        return;
    }
    const segments = path.split('.');
    return function (obj) {
        if (!obj) return;
        //console.log(obj);
        for (let i = 0; i < segments.length; i++) {
            //console.log(segments[i]);
            obj = obj[segments[i]];
        }
        return obj;
    }
}

var window = {};//模拟页面全局属性

class Watcher {
    constructor(vm, expOrFn, cb) {
        this.vm = vm;
        // 执行this.getter(),就可以获取到data.a.b.c的内容
        this.getter = parsePath(expOrFn);
        this.cb = cb;        
        this.value = this.get();
        console.log("this.value..." + this.value);
    }
    get() {
        window.target = this;
        let value = this.getter.call(this.vm, this.vm);
        window.target = undefined;
        return value;
    }
    update() {
        const oldValue = this.value;
        this.value = this.get();
        this.cb.call(this.vm, this.value, oldValue);
    }
}

class Observer {
    constructor(value) {
        this.value = value;
        if (!Array.isArray(value)) {
            this.walk(value);
        }
    }
    walk(obj) {
        const keys = obj.keys(obj);
        for (let i = 0; i < keys.length; i++) {
            defineReactive(obj, keys[i], obj.keys[i])
        }
    }
}

function defineReactive(data, key, val) {
    // 新增,递归子属性
    if (typeof val == 'object') {
        new Observer(val);
    }
    let dep = new Dep();
    Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get: function () {
            dep.depend();
            return val;
        },
        set: function (newVal) {
            if (val === newVal) {
                return;
            }
            val = newVal;
            dep.notify();            
        }
    })
}

//与01测试一样的数据准备。
var base = {};
defineReactive(base, "name", "kevin");

//准备一个修改DOM的方法。就是说只要属性变化了,就帮我调用这个方法
function updateVm(vm, newVal, oldValue) {
    console.log(`将控件的外观按新值变化...newVal=${newVal},oldValue=${oldValue}`);
}

let watch = new Watcher(base, "name", updateVm);
base.name = "witty";
base.name = "witty2";
base.name = "witty3";
console.log("测试完成。")

  在命令窗口运行:node vuetest02.js

你会发现有两个问题,以下是测试的截图

 第一个问题,updateVm为什么newVal是一个旧值,其实是我并不了解call的方法是什么含义

Function.call(obj,[param1[,param2[,…[,paramN]]]])
obj:这个对象将代替Function类里this对象
params:这个是一个参数列表

  原来第一个参数实际上是代表this,于是调用updateVm如下:

//准备一个修改DOM的方法。就是说只要属性变化了,就帮我调用这个方法
function updateVm(newVal, oldValue) {
    console.log(`this=${this}`);
    console.log(`将控件的外观按新值变化...newVal=${newVal},oldValue=${oldValue}`);
}

  

第二个问题,是书本上的代码就有这个问题存在,其实每get一次都会加一次“依赖收集”,解决也很容易,把window.target=this这一句从get方法中删除,改放在构造函数调用get的语句之前就行了。

原文地址:https://www.cnblogs.com/kevin-Y/p/12483398.html