vue中双向绑定的原理及拓展

首先通过控制台输出一个定义在vue初始化数据上的对象是什么
代码:
var vm = new Vue({
    data: {
        obj: {
            a: 1
        }
    },
    created: function () {
        console.log(this.obj);
    }
});

结果:

 

a属性中有这两个方法

vue通过Object.defineProperty()来实现数据劫持

它有三个参数:

  第一个是参数所在对象;

  第二个是你要操作的属性;

  第三个是被操作的属性的特性

    这个参数的格式为{}

    一般是两个

     其中get为访问属性时使用,set为修改属性时使用

var obj = {}
var name = '';
Object.defineProperty(obj, 'name', {
  set: function (value) {
    name = value;
    console.log('name改变为' + value);
  },
  get: function () {
    return '《' + name + '》'
  }
})
 
obj.name = 'mypro';  // name值为mypro
console.log(obj.name);  // 《mypro》

//其实只用Object.defineProperty()已经可以实现双向绑定,缺点是效率非常低

观察者模式,它使双向绑定更有效率,它是一对多的一种模式,

在vue中,你改了某个data的数据,这是"一";

"多",就是页面上凡是用了这个数据的地方都更新

这就是页面上的很多地方,都观察这个data,

这就是一对多,所以应当用观察者模式。

思路整理

1、实现Observer

2、实现Compile

3、实现Watcher

4、实现MVVM

实现数据绑定的做法有⼤致如下⼏种:

发布者-订阅者模式(backbone.js)(框架过老不建议使用)

脏值检查(angular.js) (使用轮询)

数据劫持(vue.js)

就是通过Object.defineProperty(),去操作数据的get、set

要实现mvvm的双向绑定,就必须要实现以下⼏点:

1、实现⼀个数据监听器Observer,能够对数据对象的所有属性进⾏监听,如有变动可拿到最新值并通知订阅 者

var data = {name: 'kindeng'};
observe(data);
data.name = 'dmq'; // 哈哈哈,监听到值变化了 kindeng --> dmq

function observe(data) {
    if (!data || typeof data !== 'object') {
        return;
    }
    // 取出所有属性遍历
    Object.keys(data).forEach(function(key) {
        defineReactive(data, key, data[key]);
    });
};

function defineReactive(data, key, val) {
    observe(val); // 监听子属性
    Object.defineProperty(data, key, {
        enumerable: true, // 可枚举
        configurable: false, // 不能再define
        get: function() {
            return val;
        },
        set: function(newVal) {
            console.log('哈哈哈,监听到值变化了 ', val, ' --> ', newVal);
            val = newVal;
        }
    });
}


​

现在可以监听每个数据变化,现在需要通知订阅者

function defineReactive(data, key, val) {
    var dep = new Dep();
    observe(val); // 监听子属性

    Object.defineProperty(data, key, {
        // ... 省略
        set: function(newVal) {
            if (val === newVal) return;
            console.log('哈哈哈,监听到值变化了 ', val, ' --> ', newVal);
            val = newVal;
            dep.notify(); // 通知所有订阅者
        }
    });
}

function Dep() {
    this.subs = [];
}
Dep.prototype = {
    addSub: function(sub) {
        this.subs.push(sub);
    },
    notify: function() {
        this.subs.forEach(function(sub) {
            sub.update();
        });
    }
};
// Observer.js
// ...省略
Object.defineProperty(data, key, {
    get: function() {
        // 由于需要在闭包内添加watcher,所以通过Dep定义一个全局target属性,暂存watcher, 添加完移除
        Dep.target && dep.addSub(Dep.target);
        return val;
    }
    // ... 省略
});

// Watcher.js
Watcher.prototype = {
    get: function(key) {
        Dep.target = this;
        this.value = data[key];    // 这里会触发属性的getter,从而添加订阅者
        Dep.target = null;
    }
}

 

2、实现⼀个指令解析器Compile,对每个元素节点的指令进⾏扫描和解析,根据指令模板替换数据,以及绑定 相应的更新函数

😂(我没怎么去理解),应该就是相当于解析模版命令,将变量替换为数据然后渲染视图。

3、实现⼀个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执⾏指令 绑定的相应回调函数,从⽽更新视图

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;
    }
};
// 这里再次列出Observer和Dep,方便理解
Object.defineProperty(data, key, {
    get: function() {
        // 由于需要在闭包内添加watcher,所以可以在Dep定义一个全局target属性,暂存watcher, 添加完移除
        Dep.target && dep.addDep(Dep.target);
        return val;
    }
    // ... 省略
});
Dep.prototype = {
    notify: function() {
        this.subs.forEach(function(sub) {
            sub.update(); // 调用订阅者的update方法,通知变化
        });
    }
};

4、mvvm⼊⼝函数,整合以上三者

这个是文章的原作者https://segmentfault.com/a/1190000006599500?utm_source=tag-newest,我就稍微加了些自己的理解和觉得重要的知识点。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值