借助es5的Object.defineProperty实现vue数据劫持原理
首先画一个html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script src="./watcher.js"></script>
<script src="./observer.js"></script>
<script src="./mvvm.js"></script>
<script>
const vm = new MVVM({
el: '#app',
data: {
country: 'china',
content: 'family',
family: {
count: 4
}
},
computed: {
ageTotal() {
return this.family.father.age + this.family.mother.age + this.family.children.age
}
},
watch: {
"family.count": function(nV, oV){
console.log(nV);
}
}
});
vm.family.count = 100;
</script>
</body>
</html>
看一下mvvm.js 的内容
function MVVM (options){
this.$options = options;
var data = this._data = this.$options.data;
let me = this;
// 将this.aaa代理到this._data.aaa
Object.keys(data).forEach(key => {
me._proxyData(key);
});
// 初始化计算属性
this._initComputed();
// 利用递归进行数据劫持
observe(data, this);
// 初始化watcher
this._initWatcher();
}
MVVM.prototype = {
_proxyData: function(key ,setter, getter){
let me = this;
setter = setter ||
Object.defineProperty(me, key, {
configurable: false,
enumerable: true,
get: function proxyGetter(){
return me._data[key];
},
set: function proxySetter(newVal){
me._data[key] = newVal;
}
});
},
_initComputed: function () {
let me = this;
let computed = this.$options.computed;
if (typeof computed === 'object') {
Object.keys(computed).forEach(key => {
Object.defineProperty(me, key, {
get: typeof computed[key] === 'function' ? computed[key] : computed[key].get,
set: function (){}
});
});
}
},
_initWatcher: function () {
let me = this;
let watcher = this.$options.watch;
if (typeof watcher === 'object') {
Object.keys(watcher).forEach(key => {
new Watcher(me, key, watcher[key]);
});
}
}
}
看一下observer的内容
function Observer(data){
this.data = data;
this.walk(data);
}
Observer.prototype = {
walk: function(data){
let me = this;
Object.keys(data).forEach(key => {
me.convert(key, data[key]);
});
},
convert: function (key, val) {
this.defineReactive(this.data, key, val);
},
defineReactive: function (data, key, val) {
let dep = new Dep();
let childObj = observe(val);
Object.defineProperty(data, key, {
enumerable: true,
configurable: false,
get: function() {
// 使用watch aaa.bbb时候,会触发两次get,且两次实例的dep是不同的,watch队列会被push进两个dep,如果继续使用this.add.bbb也会持续触发get,但是wather队列不会继续添加该dep
if (Dep.target) {
dep.depend();
}
return val;
},
set: function(newVal) {
if (newVal === val) {
return;
}
val = newVal;
childObj = observe(newVal);
dep.notify();
}
});
}
};
function observe(value, vm){
if (!value || typeof value !== 'object') {
return
}
return new Observer(value);
}
var uid = 0;
Dep.target = null;
function Dep () {
this.id = uid++;
this.subs = [];
};
Dep.prototype = {
depend: function () {
Dep.target.addDep(this);
},
addSub: function (sub) {
// 当使用watch a.b时候getter会触发两次,watcher会被push到当前sub中,sub是当前的watcher
this.subs.push(sub);
},
notify: function(){
this.subs.forEach(sub => {
sub.update();
});
}
};
看一下watcher的内容
function Watcher(vm, expOrFn, cb){
this.cb = cb;
this.vm = vm;
this.expOrFn = expOrFn;
this.depIds = {};
if (typeof expOrFn === 'function') {
this.getter = expOrFn;
} else {
this.getter = this.parseGetter(expOrFn);
}
this.value = this.get();
};
Watcher.prototype = {
parseGetter: function(exp) {
if (/[^\w.$]/.test(exp)) return;
var exps = exp.split('.');
return function(obj) {
for (var i = 0, len = exps.length; i < len; i++) {
if (!obj) return;
obj = obj[exps[i]];
}
return obj;
}
},
get: function () {
Dep.target = this;
let value = this.getter.call(this.vm, this.vm);
Dep.target = null
return value;;
},
addDep: function (dep) {
if (!this.depIds.hasOwnProperty(dep.id)) {
dep.addSub(this); // 当使用watch aaa.bbb时候,会触发两次get,因此该函数会执行两次,dep.id是不同的,然后不同的dep会执行addSub,把当前的watcher放到当前dep的subs队列中
this.depIds[dep.id] = dep;
}
},
update: function () {
this.run();
},
run: function () {
let value = this.get();
let oldVal = this.value;
if (value !== oldVal) {
this.value = value;
this.cb.call(this.vm, value, oldVal);
}
}
};
大功告成!!!
网友评论