承接着上文 Vue 构造函数,完成 init.js 中 initState 对数据劫持的初始化方法

init.js

  1. import proxyData from './proxy';
  2. import observe from './observe';
  3. function initState (vm) {
  4. var options = vm.$options;
  5. if (options.data) {
  6. initData(vm); // 针对 data
  7. }
  8. }
  9. function initData (vm) {
  10. var data = vm.$options.data;
  11. // 不直接使用用户 options 提供的 data
  12. // 如果是 options.data 是 function 就用 vm 为 this 来执行,否则就使用 options.data,|| {} 是保证如果 options 中没有 data 就是一个空的对象
  13. // 最后挂在 vm._data 上
  14. data = vm._data = typeof data === 'function' ? data.call(vm) : data || {};
  15. // 第一个数据劫持:数据代理,将 vm._data 挂到 vm 上
  16. for(var key in data) {
  17. proxyData(vm, '_data', key);
  18. }
  19. // 要对 data 和 data 内的对象和数组进行观察
  20. observe(data);
  21. }
  22. export {
  23. initState
  24. }

proxy.js 用于数据代理

  1. function proxyData (vm, target, key) {
  2. Object.defineProperty(vm, key, {
  3. get () {
  4. return vm[target][key];
  5. },
  6. set (newValue) {
  7. vm[target][key] = newValue;
  8. }
  9. })
  10. }
  11. export default proxyData;

observe.js 观察对象

  1. import Observer from './observer';
  2. function observe (data) {
  3. if (typeof data !== 'object' || data === null) return ;
  4. return new Observer(data);
  5. }
  6. export default observe;

observeArr.js 观察数组

  1. import Observer from './observer';
  2. function observeArr (arr) {
  3. for (var i = 0; i < arr.length; i++) {
  4. observe(arr[i]);
  5. }
  6. }
  7. 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];

  1. // 用于定义响应数据
  2. defineReactiveData(data, key, value); // 写在 reactive.js

} }

export default Observer;

  1. <a name="h7okJ"></a>
  2. # array.js 专门处理数组
  3. - 把专门修改数组的方法记录在 [config.js](#RPMUv)
  4. - 使用 Object.create 以原数组为原型创建新的对象 arrMethods
  5. - 为 arrMethods 重写每一个会修改数组的方法
  6. - 执行原数组的方法
  7. - 对要追加数组的这个新数组进行观察 [observeArr](#MHKZV)
  8. ```javascript
  9. import { ARR_METHODS } from './config';
  10. import observeArr from './observeArr';
  11. var originArrMethods = Array.prototype,
  12. arrMethods = Object.create(originArrMethods); // 使用 Object.create 以原数组为原型创建新的对象
  13. ARR_METHODS.map(function(m) {
  14. // 重写每一个会修改数组的方法
  15. arrMethods[m] = function () {
  16. var args = Array.prototype.slice.call(arguments), // 使用数组的 slice 把类数组的 argumenmts 转为数组
  17. rt = originArrMethods[m].apply(this, args); // 执行原数组方法的行为
  18. // 不仅执行原数组方法的行为, 对要追加数组的这个新数组进行观察
  19. var newArr;
  20. switch (m) {
  21. case 'push':
  22. case 'unshift':
  23. newArr = args;
  24. break;
  25. case 'splice':
  26. newArr = args.slice(2);
  27. break;
  28. default;
  29. break;
  30. }
  31. newArr && observeArr(newArr);
  32. return rt;
  33. }
  34. });
  35. export {
  36. arrMethods
  37. }

reactive.js 专门处理响应式

value 有可能还是一个对象或数组,要再递归观察

  • 需要递归 observe
  • 设置对象的 newValue 也可能是对象或数组,也要递归观察 ```javascript function defineReactiveData (data, key, value) { // 因为这个 value 有可能还是一个对象或数组所以要递归观察 observe(value); Object.defineProperty(data, key, {
    1. get() {
    2. return value;
    }, set(newValue) {
    1. if (newValue === value) return;
    2. observe(newValue);
    3. value = newValue;
    } }); }

export default defineReactiveData;

  1. <a name="RPMUv"></a>
  2. # config.js 相关配置
  3. ```javascript
  4. var ARR_METHODS = [
  5. 'push',
  6. 'pop',
  7. 'shift',
  8. 'unshift',
  9. 'splice',
  10. 'sort',
  11. 'reverse'
  12. ];
  13. export {
  14. ARR_METHODS
  15. }