Object类型
Vue2.0的响应式原理是Object.defineProperty(obj, key, {}),在getter中收集依赖,在setter中触发依赖;还有一个是观察者模式。
Observer
原始数据通 data 通过 Observer 遍历自己的 key,通过get、set函数为每个属性添加侦测。
class Observer {
constructor(value) {
this.value = value;
if(!Array.isArray(value)){
this.walk(value);
}
}
//walk会将每一个属性都转换成getter/setter的形式来侦测变化,这个方法只有在数据类型为Object时被调用
walk(obj){
for(let key in obj){
defineReactive(obj, key, obj[key]);
}
}
}
function defineReactive(data, key, val) {
//递归属性
if(typeof val==='object'){
new Observer(val)
}
let dep = new Dep();
Object.defineProperty(data, key, {
configurable: true,
enumerable: true,
get(){
//如果有用到这个key的值, 进行依赖收集
if(window.target){
dep.depend();
}
return val;
},
set(newVal){
if(val===newVal){
return;
}
val = newVal;
//如果key的值发生了变化, 通知依赖,触发响应的更新
dep.notify();
}
})
}
Dep
数据data每个属性key都有一个数组用来存放哪些地方用到了这个数据的这个属性。统一放在一个Dep实例中。Dep一方面用来存储这些依赖,另一方面属性key的值变化了(set)的时候去通知这些依赖,触发相应的更新(update)。
class Dep {
constructor() {
this.subs = [];
}
addSub(sub){
this.subs.push(sub);
}
depend(){
if(window.target){
this.subs.push(window.target);
}
}
notify(){
const subs = this.subs.slice();
for(let sub of subs){
sub.update();
}
}
}
Watcher
依赖搜集。
- 触发key值的get方法,将自己添加到对应key的依赖数组中。(这里有个技巧是先缓存到window.target上,触发完成get方法后再将window.target置为undefined)
- 要有update方法,当key的值变化时触发相应的update。
class Watcher {
constructor(vm, expOrFn, cb) {
this.vm = vm;
//执行this.getter(), 就可以读取data.a.b.c的内容
this.getter = parsePath(expOrFn);
this.cb = cb;
this.value = this.get();
}
get(){
window.target = this;
let value = this.getter.call(this.vm, this.vm);
window.target = undefined;
return value;
}
update(){
const oldValue = this.value;
this.value = this.get();
this.cb.call(this.vm, this.value, oldValue)
}
}
const bailRE = /[^\w.$]/;
function parsePath(path) {
if(bailRE.test(path)){
return;
}
const segments = path.split('.');
return function (obj) {
for(let i=0; i<segments.length;i++){
if(!obj) return;
obj = obj[segments[i]]
}
return obj;
}
}
验证
<input type="text" id="myInput">
<p></p>
</body>
<script>
let vm = {
data: {
message: 'hello'
}
};
document.getElementById("myInput").value = vm.data.message;
document.querySelector('input').oninput = function(v, a){
let x = document.getElementById("myInput").value;
vm.data.message = x;
}
document.querySelector('p').innerHTML = vm.data.message;
new Observer(vm);
new Watcher(vm, "data.message", function (newVal, val) {
document.querySelector('p').innerHTML = newVal;
});
</script>
参考:www.cnblogs.com/chenhuichao…
代码来自《深入浅出Vue.js》