vue.js动态数据绑定学习笔记

2020-06-13 10:42:10易采站长站整理

// 在上例中会匹配 '{{user.name}}' 及 '{{user.age}}'
if (self.isTextNode(node) && reg.test(text)) {
// 解析 textContent,RegExp.$1 为匹配到的内容,在上例中为 'user.name' 及 'user.age'
self.compileText(node, RegExp.$1);
}

// 递归
if (node.childNodes && node.childNodes.length) {
self.compileElement(node);
}
});
};

Compile.prototype.compileText = function(node, exp) {
// this.$vm 即为 Hue 实例,exp 为正则匹配到的内容,即 'user.name' 或 'user.age'
compileUtil.text(node, this.$vm, exp);
};

let compileUtil = {
text: function(node, vm, exp) {
this.bind(node, vm, exp, 'text');
},

bind: function(node, vm, exp, dir) {
// 获取更新视图的回调函数
let updaterFn = updater[dir + 'Updater'];

// 先调用一次 updaterFn,更新视图
updaterFn && updaterFn(node, this._getVMVal(vm, exp));

// 添加 Watcher 订阅
new Watcher(vm, exp, function(value, oldValue) {
updaterFn && updaterFn(node, value, oldValue);
});
},

// 根据 exp,获得其值,在上例中即 'vm.user.name' 或 'vm.user.age'
_getVMVal: function(vm, exp) {
let val = vm;
exp = exp.trim().split('.');
exp.forEach(function(k) {
val = val[k];
});
return val;
}
};

let updater = {
// Watcher 订阅的回调函数
// 在此即更新 node.textContent,即 update view
textUpdater: function(node, value) {
node.textContent = typeof value === 'undefined'
? ''
: value;
}
};

正如代码中所看到的,Compile 在解析到 {{xxx}} 后便添加了 xxx 属性的订阅,即 new Watcher(vm, exp, callback)。理解了这一步后,接下来就需要了解怎么实现相关属性的订阅了。先从 Observer 开始谈起。

Observer

从最简单的情况来考虑,即不考虑数组元素的变化。暂时先不考虑 Dep 与 Observer 的联系。先看看 Observer 构造函数:


function Observer(data) {
this.data = data;
this.walk(data);
}

Observer.prototype.walk = function(data) {
const keys = Object.keys(data);
// 遍历 data 的所有属性
for (let i = 0; i < keys.length; i++) {
// 调用 defineReactive 添加 getter 和 setter
defineReactive(data, keys[i], data[keys[i]]);
}
};

接下来通过 Object.defineProperty 方法给所有属性添加 getter 和 setter,就达到了我们的目的。属性有可能也是对象,因此需要对属性值进行递归调用。


function defineReactive(obj, key, val) {
// 对属性值递归,对应属性值为对象的情况
let childObj = observe(val);

Object.defineProperty(obj, key, {