vue源码之数据响应式原理

vue源码之数据响应式原理

使用Object.defineProperty

var obj = {}
Object.defineProperty(obj, 'a', {
  get() {
    console.log('你试图访问obj的a属性');
    return 7;
  },
  set(newVal) {
    console.log('你试图修改obj的a属性', newVal);
  }
})

console.log(obj.a);  // 7 
obj.a = 10;  
console.log(obj.a);  // 7 


// getter和setter 需要一个临时变量来周转这个get和set值,写法如下:
var obj = {}
var temp;
Object.defineProperty(obj, 'a', {
  get() {
    console.log('你试图访问obj的a属性');
    return temp;
  },
  set(newVal) {
    console.log('你试图修改obj的a属性', newVal);
    temp = newVal
  }
})

console.log(obj.a); // undefined
obj.a = 10;
console.log(obj.a); // 10 


// 封装 defineReactive 闭包环境 内外两个环境
function defineReactive(data, key, val) {
  Object.defineProperty(data, key, {
    // 可枚举
    enumerable: true,
    // 可以配置 delete
    configurable: true,
    // getter
    get() {
      console.log('你试图访问obj的' + key + '属性');
      return val;
    },
    // setter
    set(newVal) {
      console.log('你试图修改obj的' + key + '属性', newVal);
      if (val == newVal) {
        return;
      }
      val = newVal
    }
  })
}

var obj = {}
defineReactive(obj, 'a', 10);

console.log(obj.a); //10
obj.a = 11;
console.log(obj.a); // 11

递归侦测对象全部属性

新建defineReactive.js文件

import observe from './observe';

// 封装 defineReactive 闭包环境 内外两个环境
export default function defineReactive(data, key, val) {
  console.log('我是defineReactive', data, key);
  if (arguments.length == 2) {
    val = data[key]
  }
  // 子元素要进行observe 至此形成了递归,这个递归不是函数自己,而是多个循环调用
  let childrenOb = observe(val);


  Object.defineProperty(data, key, {
    // 可枚举
    enumerable: true,
    // 可以配置 delete
    configurable: true,
    // getter
    get() {
      console.log('你试图访问' + key + '属性');
      return val;
    },
    // setter
    set(newVal) {
      console.log('你试图修改' + key + '属性', newVal);
      if (val == newVal) {
        return;
      }
      val = newVal;
      // 当设置了新值,这个新值也要被observe  
      childrenOb =  observe(newVal);
    }
  })
}

新建Observer.js文件

import { def } from './utils';
import defineReactive from './defineReactive';
import { arrayMethods } from './array';
import observe from './observe';

// Observer类  将一个正常的object转换为每个层级的属性都是被响应式(可以被侦测的)object
export default class Observer {
  constructor(value) {
    // 给实例 this 这里的this 构造函数的this不是表示类本身,而是表示实例
    // 给实例添加一个__ob__属性,值是这次的 new 的实例
    def(value, '__ob__', this, false);
    console.log('我是Observer', value);

    // 检查是否为数组还是对象
    if (Array.isArray(value)) {
      // 如果是数组, 将数组的原型指向 arrayMethods
      Object.setPrototypeOf(value, arrayMethods);
      // 让数组变的observe
      this.ObserveArray(value)
    } else {
      this.walk(value);
    }
  }
  // 遍历
  walk(value) {
    for (let k in value) {
      defineReactive(value, k)
    }
  }
  // 数组的特殊遍历
  ObserveArray(arr) {
    for (let i = 0, l = arr.length; i < l; i++) {
      // 逐项进行observe
      observe(arr[i])
    }
  }
}

新建observe.js文件

import { def } from './utils';
import defineReactive from './defineReactive';
import observe from './observe';

// Observer类  将一个正常的object转换为每个层级的属性都是被响应式(可以被侦测的)object
export default class Observer {
  constructor(value) {
    // 给实例 this 这里的this 构造函数的this不是表示类本身,而是表示实例
    // 给实例添加一个__ob__属性,值是这次的 new 的实例
    def(value, '__ob__', this, false);
    console.log('我是Observer', value);
    this.walk(value);
  }
  // 遍历
  walk(value){
    for(let k in value){
      defineReactive(value,k)
    }
  }
}

新建utils.js文件

// utils 用于遍历的工具函数
export const  def = function (obj, key, value, enumerable) {
  Object.defineProperty(obj, key, {
    value,
    enumerable,
    writable: true,
    configurable: true
  })
}


index.js文件

import defineReactive from './defineReactive';
import Observer from './Observer';
import observe from './observe';

var obj = {
  a: {
    b: {
      m: {
        n: 7
      }
    }
  },
  c:1
};

observe(obj)
// obj.c = 10;
console.log(obj.a.b.m.n);

在这里插入图片描述

数组的响应式处理

数组响应式的方法有七个改写的 :pup 、pop、shift、unshift、splice、 reverse 、 sort ;

import { def } from './utils';

// 数组响应式的方法有七个改写的 :pup 、pop、shift、unshift、splice、 reverse 、 sort ;
// 改写 七 个方法 基于 Array.prototype

// 问:数组的响应式原理怎么实现的 ? 
// 答:以 Array.prototype为原型,创建了arrayMethods 对象,然后Object.setPrototypeOf(o,arrayMethods)
// 强制让数组 的 __proto__指向了Array.prototype 这样可以触发新创建的函数

const arrayPrototype = Array.prototype;
// 以 Array.prototype为原型,创建了arrayMethods 对象 并暴露
export const arrayMethods = Object.create(arrayPrototype);
// console.log(arrayPrototype);

// 要被改写的7个方法
const methodsNeedChange = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'reverse',
  'sort'
];

methodsNeedChange.forEach(methodName => {
  // console.log(11);
  // 备份原的方法
  const original = arrayPrototype[methodName];
  // 定义新的方法
  def(arrayMethods, methodName, function () {
    console.log('数组方法');
    // 恢复原来的功能
    const result = original.apply(this, arguments);
    // 把类数组对象变为数组
    const args = [...arguments];
    const ob = this.__ob__;
    // 有三种方法push 、unshift、splice 能够插入新项,现在要把插入的也要变为observe
    let inserted = [];
    switch(methodName){
      case 'push' :
      case 'unshift':
        inserted = args;
        break;
      case 'splice':
        inserted = args.silce(2)
        break;
    }
    // 判断有没有要插入的项,让新项也变为响应的
    if(inserted){
      ob.ObserveArray(inserted)
    }
    return result;
  }, false)
})

index.js

import observe from './observe';

var obj = {
  a: {
    b: {
      m: {
        n: 7
      }
    }
  },
  c:1,
  g:[11,22,33,44,55]
};

observe(obj)
obj.g.push(66);
console.log(obj.g);

在这里插入图片描述

依赖收集

用到数据的地方,叫做依赖

1、vue1.x ,细粒度依赖,用到数据的DOM都是依赖;
2、vue2.x ,中等细粒度依赖,用到数据的组件都是依赖;

在getter中收集依赖,在setter中触发依赖;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Watcher.js

import Dep from "./Dep";
var uid = 0;
export default class Watcher {
  constructor(target, expression, callback) {
    console.log('我是Watcher 类的构造器');
    this.id = uid++;
    this.target = target;
    this.getter = parsePath(expression);
    this.callback = callback;
    this.value = this.get();
  }
  update() {
    this.run()
  }
  get() {
    // 进入依赖收集,让全局的Eep.target设置为Watcher本身,那么进入依赖收集
    Dep.target = this;
    const obj = this.target;
    var value;
    // 只要能找,一直寻找
    try {
      value = this.getter(obj);
    } finally {
      // 退出依赖
      Dep.target = null;
    }
    return value;
  }
  run() {
    this.getAndInvoke(this.callback);
  }
  getAndInvoke(cb) {
    const value = this.get();
    if (value !== this.value || typeof value == 'object') {
      const oldValue = this.value;
      this.value = value;
      cb.call(this.target, value, oldValue);
    }
  }
};

function parsePath(str) {
  var segments = str.split('.');
  return (obj) => {
    for (let i = 0; i < segments.length; i++) {
      if (!obj) return;
      obj = obj[segments[i]]
    }
    return obj;
  }
}

Dep.js

var uid = 0;
export default class Dep {
  constructor() {
    console.log('我是Dep类的构造器');
    this.id = uid++;
    // 用数组存储自己的订阅者,subs是 subscribes 订阅者的意思
    // 这个数组里面放的是 wather实例 是发布者
    this.subs = [];
  }
  // 添加订阅
  addSubs(sub) {
    this.subs.push(sub)
  }
  // 移除更新
  // 添加依赖
  depend(){
    // Dep.target 自己指定的全局的位置,用window.target 也可以,只要是唯一的就可以
    if(Dep.target){
      this.addSubs(Dep.target)
    }
  }
  // 通知更新
  notify() {
    // 浅克隆
    const subs = this.subs.slice()
    // 遍历
    for (let i = 0; i < subs.length; i++) {
      subs[i].update();
    }
  }
};

defineReactive.js 引入 Dep new Dep()

import observe from './observe';
import Dep from './Dep';

// 封装 defineReactive 闭包环境 内外两个环境
export default function defineReactive(data, key, val) {
  const dep = new Dep();
  console.log('我是defineReactive', data, key);
  if (arguments.length == 2) {
    val = data[key]
  }
  // 子元素要进行observe 至此形成了递归,这个递归不是函数自己,而是多个类循环调用
  let childrenOb = observe(val);

  Object.defineProperty(data, key, {
    // 可枚举
    enumerable: true,
    // 可以配置 delete
    configurable: true,
    // getter
    get() {
      console.log('你试图访问' + key + '属性');
      // 如果处于依赖收集阶段
      if(Dep.target){
        dep.depend();
        if(childrenOb){
          childrenOb.dep.depend()
        }
      }
      return val;
    },
    // setter
    set(newVal) {
      console.log('你试图修改' + key + '属性', newVal);
      if (val == newVal) {
        return;
      }
      val = newVal;
      // 当设置了新值,这个新值也要被observe  
      childrenOb =  observe(newVal);
      // 发布订阅者 通知dep
      dep.notify();
    }
  })
}

Observer.js 引入 Dep new Dep()

import { def } from './utils';
import defineReactive from './defineReactive';
import {arrayMethods} from './array';
import observe from './observe';
import Dep from './Dep';

// Observer类  将一个正常的object转换为每个层级的属性都是被响应式(可以被侦测的)object
export default class Observer {
  constructor(value) {
    // 每个 Observer 的实例身上都有一个 dep
    this.dep = new Dep();
    // 给实例 this 这里的this 构造函数的this不是表示类本身,而是表示实例
    // 给实例添加一个__ob__属性,值是这次的 new 的实例
    def(value, '__ob__', this, false);
    console.log('我是Observer', value);

    // 检查是否为数组还是对象
    if (Array.isArray(value)) {
      // 如果是数组, 将数组的原型指向 arrayMethods
      Object.setPrototypeOf(value, arrayMethods);
      // 让数组变的observe
      this.ObserveArray(value)
    } else {
      this.walk(value);
    }
  }
  // 遍历
  walk(value) {
    for (let k in value) {
      defineReactive(value, k)
    }
  }
  // 数组的特殊遍历
  ObserveArray(arr) {
    for (let i = 0, l = arr.length; i < l; i++) {
      // 逐项进行observe
      observe(arr[i])
    }
  }
}

index.js触发Watcher

import observe from './observe';
import Watcher from './Watcher';

var obj = {
  a: {
    b: {
      m: {
        n: 7
      }
    }
  },
  c: 1,
  g: [11, 22, 33, 44, 55]
};

observe(obj)
// obj.c++;
// obj.a.b = 10;
// console.log(obj.a.b.m.n);
// obj.g.push(66);
// console.log(obj.g);
// console.log(obj);

new Watcher(obj, 'a.b.m.n', (val) => {
  console.log('监控a.b.m.n', val);
});
obj.a.b.m.n = 100;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值