承接着上文 Vue 构造函数,完成 init.js 中 initState 对数据劫持的初始化方法
init.js
import proxyData from './proxy';
import observe from './observe';
function initState (vm) {
var options = vm.$options;
if (options.data) {
initData(vm); // 针对 data
}
}
function initData (vm) {
var data = vm.$options.data;
// 不直接使用用户 options 提供的 data
// 如果是 options.data 是 function 就用 vm 为 this 来执行,否则就使用 options.data,|| {} 是保证如果 options 中没有 data 就是一个空的对象
// 最后挂在 vm._data 上
data = vm._data = typeof data === 'function' ? data.call(vm) : data || {};
// 第一个数据劫持:数据代理,将 vm._data 挂到 vm 上
for(var key in data) {
proxyData(vm, '_data', key);
}
// 要对 data 和 data 内的对象和数组进行观察
observe(data);
}
export {
initState
}
proxy.js 用于数据代理
function proxyData (vm, target, key) {
Object.defineProperty(vm, key, {
get () {
return vm[target][key];
},
set (newValue) {
vm[target][key] = newValue;
}
})
}
export default proxyData;
observe.js 观察对象
import Observer from './observer';
function observe (data) {
if (typeof data !== 'object' || data === null) return ;
return new Observer(data);
}
export default observe;
observeArr.js 观察数组
import Observer from './observer';
function observeArr (arr) {
for (var i = 0; i < arr.length; i++) {
observe(arr[i]);
}
}
export default observeArr;
observer.js 观察者类
处理对象与数组的方法是不一样的
- {} defineProperty
- 让对象的每一个属性实现响应式
- [] 自己去写方法
- 数组要更改原数组的方法,对于修改数组的方法也要自己重写
- 先建立 array.js 来处理关于数组
- 把 data 的原型改为修改过的 arrMethods
- 递归观察数组 ```javascript import defineReactiveData from ‘./reactive’; import { arrMethods } from ‘./array’; import observeArr from ‘./observeArr’;
- 数组要更改原数组的方法,对于修改数组的方法也要自己重写
function Observer (data) { if (Array.isArray(data)) { data.protot = arrMethods; observeArr(data); } else { this.walk(data); } }
Observer.prototype.walk = function (data) { var keys = Object.keys(data);
for (var i = 0; i < keys.length; i ++) { var key = keys[i], value = data[key];
// 用于定义响应数据
defineReactiveData(data, key, value); // 写在 reactive.js
} }
export default Observer;
<a name="h7okJ"></a>
# array.js 专门处理数组
- 把专门修改数组的方法记录在 [config.js](#RPMUv)
- 使用 Object.create 以原数组为原型创建新的对象 arrMethods
- 为 arrMethods 重写每一个会修改数组的方法
- 执行原数组的方法
- 对要追加数组的这个新数组进行观察 [observeArr](#MHKZV)
```javascript
import { ARR_METHODS } from './config';
import observeArr from './observeArr';
var originArrMethods = Array.prototype,
arrMethods = Object.create(originArrMethods); // 使用 Object.create 以原数组为原型创建新的对象
ARR_METHODS.map(function(m) {
// 重写每一个会修改数组的方法
arrMethods[m] = function () {
var args = Array.prototype.slice.call(arguments), // 使用数组的 slice 把类数组的 argumenmts 转为数组
rt = originArrMethods[m].apply(this, args); // 执行原数组方法的行为
// 不仅执行原数组方法的行为, 对要追加数组的这个新数组进行观察
var newArr;
switch (m) {
case 'push':
case 'unshift':
newArr = args;
break;
case 'splice':
newArr = args.slice(2);
break;
default;
break;
}
newArr && observeArr(newArr);
return rt;
}
});
export {
arrMethods
}
reactive.js 专门处理响应式
value 有可能还是一个对象或数组,要再递归观察
- 需要递归 observe
- 设置对象的 newValue 也可能是对象或数组,也要递归观察
```javascript
function defineReactiveData (data, key, value) {
// 因为这个 value 有可能还是一个对象或数组所以要递归观察
observe(value);
Object.defineProperty(data, key, {
}, set(newValue) {get() {
return value;
} }); }if (newValue === value) return;
observe(newValue);
value = newValue;
export default defineReactiveData;
<a name="RPMUv"></a>
# config.js 相关配置
```javascript
var ARR_METHODS = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
];
export {
ARR_METHODS
}