【Vue源码】数据响应式原理 - 依赖收集 - defineReactive - Observer - Dep - Watcher

我们在使用Vue时,只需要修改数据,视图就会自动更新,这就是数据响应
今天来学习Vue实现数据响应式的原理~

源码链接 https://gitee.com/ykang2020/vue_learn

0. 使用Vue响应式时的学习笔记

  1. Vue会监视data中所有层次的数据
  2. 如何监测对象中的数据?
    通过setter实现监视,且要在new Vue 时就传入要监测的数据
    ① 对象中后追加的属性,Vue默认不做响应式处理
    ② 如需给后添加的属性做响应式,需要使用如下API
Vue.set(target, propertyName/index, value)
vm.$set(target, propertyName/index, value)
  1. 如何检测数组中的数据?
    通过包裹数组更新元素的方法实现,本质就是做了两件事
    ① 调用原生数组对应的方法对数组进行更新
    ② 重新解析模板,进而更新页面
  2. 在Vue修改数组中的某个元素一定要用如下方法
    ① API: push() pop() shift() unshift() splice() sort() reverse()
    ② Vue.set() 或 vm.$set

注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象添加属性


在学习Vue源码之前,先来看看
前置知识 Object.defineProperty() 可以参考之前的博文
【JS】JavaScript对象属性-属性类型-数据属性-访问器属性-Object.defineProperty()方法-get方法-set方法

gettersetter方法可以对数据进行监听,访问和设置都会被监听捕获
读取数据的时候会触发getter,而修改数据的时候会触发setter

1. 定义 defineReactive 函数

1.1 Why (临时变量)

我们要进行数据劫持,先想到的就是Object.defineProperty()中给属性添加gettersetter方法,但是这么做有点问题~

defineProperty() 方法需要临时的全局变量周转gettersetter

我们来看下面这个例子

let obj = {
   };
let temp;

Object.defineProperty(obj, "a", {
   
  get() {
   
    console.log("getter试图访问obj的a属性");
    return temp;
  },
  set(newValue) {
   
    console.log("setter试图改变obj的a属性", newValue);
    temp = newValue;
  },
});

console.log(obj.a); 
obj.a = 5
console.log(obj.a);

在这里插入图片描述

1.2 How (闭包)

所以我们就自己定义一个函数,对defineProperty进行封装,来实现数据劫持

使用defineReactive 函数不需要设置临时变量了,而是用闭包

function defineReactive(data, key, value) {
   
  Object.defineProperty(data, key, {
   
    // 可枚举 可以for-in
    enumerable: true,
    // 可被配置,比如可以被delete
    configurable: true,
    // getter
    get() {
   
      console.log(`getter试图访问obj的${
     key}属性`);
      return value;
    },
    // setter
    set(newValue) {
   
      console.log(`setter试图改变obj的${
     key}属性`, newValue);
      if (value === newValue) return;
      value = newValue;
    },
  });
}

let obj = {
   };
// 初始化
defineReactive(obj, "a", 10);
console.log(obj.a);

obj.a = 5;
console.log(obj.a);

在这里插入图片描述

2. 对象的响应式处理——递归侦测对象全部属性 object

2.1 Why (嵌套)

上面定义的defineProperty()函数,不能监听到对象嵌套的形式

也就是对象嵌套对象

function defineReactive(data, key, value) {
   
  if (arguments.length === 2) {
   
    value = data[key];
  }
  Object.defineProperty(data, key, {
   
    // 可枚举 可以for-in
    enumerable: true,
    // 可被配置,比如可以被delete
    configurable: true,
    // getter
    get() {
   
      console.log(`getter试图访问obj的${
     key}属性`);
      return value;
    },
    // setter
    set(newValue) {
   
      console.log(`setter试图改变obj的${
     key}属性`, newValue);
      if (value === newValue) return;
      value = newValue;
    },
  });
}

let obj = {
   
  b: {
   
    c: {
   
      d: 4,
    },
  },
};
// 初始化
defineReactive(obj, "b");
console.log(obj.b.c.d);

这里显示没有监听到内部(obj.b.c.d)
在这里插入图片描述

2.2 How(递归)

所以我们要创建一个Observer类 ——> 将一个正常的object转换为每个层级的属性都是响应式(可以被侦测)的object

遍历对象

在这里插入图片描述

observe.js

监听 value
尝试创建Observer实例,如果value已经是响应式数据,就不需要再创建Observer实例,直接返回已经创建的Observer实例即可,避免重复侦测value变化的问题

import Observer from "./Observer";
/**
 * 监听 value
 * @param {*} value 
 * @returns 
 */
export default function observe(value) {
   
  // 如果value不是对象,就什么都不做
  if (typeof value != "object") return;
  let ob;
  if (typeof value.__ob__ !== "undefined") {
   
    ob = value.__ob__;
  } else {
   
    ob = new</
  • 21
    点赞
  • 83
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值