美文网首页
vue中的双向数据绑定mvvm

vue中的双向数据绑定mvvm

作者: xiao_afei | 来源:发表于2021-11-15 15:42 被阅读0次

    详细文档可参考 https://github.com/DMQ/mvvm

    总体思路

    1. 入口调用observer, 对于每一个property创建一个Dep, get方法负责将watcher添加到Dep中,set方法负责通知Dep中所有的watcher进行update操作
    2. 入口接着调用compile,遍历节点,找到所有含vue指令的节点,从modal中获取数据并渲染或者添加合适的监听事件,并创建watcher,wathcher的构造函数中会调用自身get,自身get函数首先将自身赋值给Dep.target,随后调用vue中property的get方法完成将watcher添加到Dep中的操作,另外watcher中的update方法会执行从compile传入的update的回调函数

    主要分四个模块Observer, Dep, Compile, Watch

    Observer: 进行数据劫持,并且通过Dep通知watchList进行更新

    Object.defineProperty(data, key, {
        var dep = new Dep();
        get: function() {
            Dep.target && dep.addDep(Dep.target); // 供watcher创建时调用
            return val;
        }
            set: function(newVal) {
                if (val === newVal) return
                    val = newVal;
                    dep.notify(); // 通知所有订阅者
            }
    });
    

    Dep:为不同的属性创建唯一的watcherList,用来存放属性的监听者

    function Dep() {
        this.subs = [];
    }
    Dep.prototype = {
        addSub: function(sub) {
            this.subs.push(sub);
        },
        notify: function() {
            this.subs.forEach(function(sub) {
                sub.update();
            });
        }
    };
    

    Compile: 解析模板指令,绑定监听,替换数据,以及创建watcher(fragment)

    bind: function(node, vm, exp, dir) {
            var updaterFn = updater[dir + 'Updater'];
            // 第一次初始化视图
            updaterFn && updaterFn(node, vm[exp]);
            // 实例化订阅者,此操作会在对应的属性消息订阅器中添加了该订阅者watcher
            new Watcher(vm, exp, function(value, oldValue) {
                // 一旦属性值有变化,会收到通知执行此更新函数,更新视图
                updaterFn && updaterFn(node, value, oldValue);
            });
        }
    

    watcher: 向Dep中添加自己,调用complie中传入给自己的回调函数

    function Watcher(vm, exp, cb) {
        this.cb = cb;
        this.vm = vm;
        this.exp = exp;
        // 此处为了触发属性的getter,从而在dep添加自己,结合Observer更易理解
        this.value = this.get(); 
    }
    Watcher.prototype = {
        update: function() {
            this.run(); // 属性值变化收到通知
        },
        run: function() {
            var value = this.get(); // 取到最新值
            var oldVal = this.value;
            if (value !== oldVal) {
                this.value = value;
                this.cb.call(this.vm, value, oldVal); // 执行Compile中绑定的回调,更新视图
            }
        },
        get: function() {
            Dep.target = this;  // 将当前订阅者指向自己
            var value = this.vm[exp];   // 触发getter,添加自己到属性订阅器中
            Dep.target = null;  // 添加完毕,重置
            return value;
        }
    };
    

    最终入口(proxy)

    observe(data, this);
    this.$compile = new Compile(options.el || document.body, this)
    

    相关文章

      网友评论

          本文标题:vue中的双向数据绑定mvvm

          本文链接:https://www.haomeiwen.com/subject/gulqtrtx.html