vue.js是采纳数据劫持联合发布者-订阅者模式的形式,通过Object.defineProperty()或者Proxy来劫持各个属性的setter,getter,在数据变动时公布音讯给订阅者,触发相应的监听回调。
Vue2.0整体思路
外围:通过Object.defineProperty()来实现对属性的劫持,达到监听数据变动的目标
Vue遍历data对象所有的属性,并应用 Object.defineProperty 把这些属性全副转为 getter/setter,在属性被拜访和批改时告诉变更。
通过如下代码实现对属性的监听:
var data = {name: 'Pat'};
observe(data);
data.name = 'PatWu'; // 哈哈哈,监听到值变动了 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,
get: function() {
console.log('监听到了取值: ', val);
return val; }, set: function(newVal) {
console.log('监听到了值变动: ', val, ' --> ', newVal);
val = newVal; } });}
在渲染过程中会对 vm 上的数据拜访,这个时候会触发了据对象的 getter。那么每个对象值的 getter 都持有一个 dep,在触发 getter 的时候会调用 dep.depend() 办法,
从而实现依赖的收集:
// ...省略
function defineReactive(data, key, val) {
observe(val);
var dep = new Dep(); // 实例化依赖收集对象
Object.defineProperty(data, key, {
get: function() {
// 依赖收集 dep.depend(); // Dep.target.addDep(this) return val; } // ... 省略 });}
Dep.target 渲染过程中曾经被赋值为渲染 watcher,那么就执行到 addDep 办法, 接着把以后的 watcher 订阅到这个数据持有的 dep 的 subs 中,
这个目标是为后续数据变动时候能告诉到哪些 subs 做筹备。
// watcher.js
function addDep(dep) {
// ...省略 dep.addSub(this)}
收集依赖并监听到变动之后就是怎么告诉订阅者了,数据变动触发数据对象的setter,setter中会执行dep.notify(),再调用订阅者的update办法实现数据更新渲染。
// ... 省略
function defineReactive(data, key, val) {
observe(val);
var dep = new Dep(); // 实例化依赖收集对象
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(); // 触发更新渲染
}); }};
Vue3.0实现形式
Vue 3.0与Vue 2.0的区别仅是数据劫持的形式由Object.defineProperty更改为Proxy代理,其余代码不变。
function observer(data) {
const that = this; for(var key in data){ that.deps[key] = []; //初始化所有订阅者对象{msg: [订阅者], info: []} } let handler = { get(target, property) {
return target[property]; }, set(target, key, value) {
let res = Reflect.set(target, key, value);
var watchers = that.deps[key]; watchers.map(item => {
item.update();
}); return res; } } // 通过Proxy实现对数据的劫持 this.$data = new Proxy(data, handler);
}
Object.defineProperty 和 Proxy的优缺点:
1.Object.defineProperty的毛病
不能监听数组:因为数组没有getter和setter,因为数组长度不确定,如果太长性能累赘太大
只能监听属性,而不是整个对象,须要遍历循环属性
只能监听属性变动,不能监听属性的删减
2.proxy的益处
能够监听数组
监听整个对象不是属性
13种来截办法,弱小很多
返回新对象而不是间接批改原对象;
3.proxy的毛病
兼容性不好,而且无奈用polyfill磨平;
发表回复