//observe.js function observe(value, vm) { // 判断值是否是对象,只有对象才需要变成响应式 if (!value || typeof value !== "object") { return; } return new Observer(value); } function Observer(data) { // 存储data this.data = data; // 开始干活 this.walk(data); } walk: function (data) { var me = this; // 遍历第一层属性 Object.keys(data).forEach(function (key) { // me.convert(key, data[key]);--无用 // 将数据重新定义成响应式数据 me.defineReactive(data, key, data[key]); }); }, convert: function (key, val) { this.defineReactive(this.data, key, val); }, //定义响应式数据 defineReactive: function (data, key, val) { // 创建dep对象 // 所有响应式数据(data)都会有一个唯一的dep对象 // dep对象会通过闭包的方式保存响应式数据的getter和setter中 var dep = new Dep(); // 因为遍历只能遍历第一层属性,如果属性值是对象,还需要再进行响应式处理 // 进行隐式递归调用, 将data上所有属性都会变成响应式属性 var childObj = observe(val); // 将data上数据定义成响应式 Object.defineProperty(data, key, { enumerable: true, // 可枚举 configurable: false, // 不能再define get: function () { // 判断 Dep.target --> 当前有没有要处理的watcher,在创建watcher对象的时候,来执行get方法 if (Dep.target) { // 建立 dep 和 watcher 的联系 dep.depend(); } return val; }, set: function (newVal) { if (newVal === val) { return; } // 更新值 val = newVal; // 新的值是object的话,又要改成响应式数据 childObj = observe(newVal); // 通知订阅者 // 通过watcher更新用户界面 dep.notify(); }, }); }, |
//Dep对象 var uid = 0; function Dep() { this.id = uid++; this.subs = []; } Dep.prototype = { addSub: function (sub) { this.subs.push(sub); }, depend: function () { // 给watcher添加dep // watcher.addDep(dep) Dep.target.addDep(this); }, removeSub: function (sub) { var index = this.subs.indexOf(sub); if (index != -1) { this.subs.splice(index, 1); } }, notify: function () { // 遍历所有watcher this.subs.forEach(function (sub) { // 调用watcher的更新方法 sub.update(); }); }, }; Dep.target = null; |
watcher.js function Watcher(vm, expOrFn, cb) { //cb—updaterFn ,更新页面的函数 this.cb = cb; this.vm = vm; // expOrFn—获取vm表达式对应值的函数 this.expOrFn = expOrFn; this.depIds = {}; if (typeof expOrFn === "function") { this.getter = expOrFn; } else { this.getter = this.parseGetter(expOrFn.trim()); } //get方法内部调用this.getter,获取表达式对应的值 this.value = this.get(); } get: function () { //当前watcher对象作为Dep.target的值,先创建实现响应式数据,创建Dep对象,然后模板辨析,创建watcher对象 Dep.target = this; var value = this.getter.call(this.vm, this.vm); Dep.target = null; return value; }, 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; //取值,执行get方法,创建dep对象 obj = obj[exps[i]]; } return obj; }; }, addDep: function (dep) { // 1. 每次调用run()的时候会触发相应属性的getter // getter里面会触发dep.depend(),继而触发这里的addDep // 2. 假如相应属性的dep.id已经在当前watcher的depIds里,说明不是一个新的属性,仅仅是改变了其值而已 // 则不需要将当前watcher添加到该属性的dep里 // 3. 假如相应属性是新的属性,则将当前watcher添加到新属性的dep里 // 如通过 vm.child = {name: 'a'} 改变了 child.name 的值,child.name 就是个新属性 // 则需要将当前watcher(child.name)加入到新的 child.name 的dep里 // 因为此时 child.name 是个新值,之前的 setter、dep 都已经失效,如果不把 watcher 加入到新的 child.name 的dep中 // 通过 child.name = xxx 赋值的时候,对应的 watcher 就收不到通知,等于失效了 // 4. 每个子属性的watcher在添加到子属性的dep的同时,也会添加到父属性的dep // 监听子属性的同时监听父属性的变更,这样,父属性改变时,子属性的watcher也能收到通知进行update // 这一步是在 this.get() --> this.getVMVal() 里面完成,forEach时会从父级开始取值,间接调用了它的getter // 触发了addDep(), 在整个forEach过程,当前wacher都会加入到每个父级过程属性的dep // 例如:当前watcher的是'child.child.name', 那么child, child.child, child.child.name这三个属性的dep都会加入当前watcher // 判断有没有保存过dep(通过id判断) if (!this.depIds.hasOwnProperty(dep.id)) { // 给dep保存watcher // 为什么给dep保存watcher? // 因为watcher有更新用户界面的方法,当dep对应的响应式数据发生变化, // 就能通过dep找到所有的watcher,从而更新用户界面 dep.addSub(this); // 给watcher保存dep // 为什么给watcher保存dep? // 防止dep保存多次同一个watcher this.depIds[dep.id] = dep; // 为什么dep保存watcher用subs数组?而watcher保存dep用depIds对象? // this.depIds = { 0: dep0, 1: dep1 } 对象查找属性比数组遍历查找值快的多 // this.depIds = [0, 1] 就需要遍历,相当于要遍历所有元素 } }, |
更新页面的操作 update: function () { this.run(); }, run: function () { // 读取属性最新的值 var value = this.get(); // 读取属性上一次的值 var oldVal = this.value; // 如果值相等,就不更新 if (value !== oldVal) { // 将最新的值保存起来 this.value = value; // 调用更新用户界面的方法cb去更新 this.cb.call(this.vm, value, oldVal); } }, |