加载中...

源码视角,Vue3为什么推荐使用ref而不是reactive


源码视角,Vue3为什么推荐使用ref而不是reactive

前言

  • 网站下方有关评论的功能接口时不时变化,头痛!!!

    了解ref的内部原理

ref 是一个函数,它接受一个内部值并返回一个响应式且可变的引用对象。这个引用对象有一个 .value 属性,该属性指向内部值。

其内部是通过 new RefImpl(value) 创建了一个新的 RefImpl 实例。这个实例包含 getter 和 setter,分别用于追踪依赖和触发更新。使用 ref 可以声明任何数据类型的响应式状态,包括对象和数组。

了解reactive的内部工作原理

reactive 是一个函数,它接受一个对象并返回该对象的响应式代理,也就是 Proxy

两者的本质区别

ref核心是返回响应式且可变的引用对象,reactive核心是返回的是响应式代理!

谈谈有关ref的RefImpl 实例优点

当你创建一个RefImpl实例时,它会包含以下几部分:

  1. 内部值:存储传递给构造函数的初始值。
  2. 依赖收集:跟踪所有依赖于该实例的效果,比如计算属性或副作用函数。通常通过依赖列表或集合实现。
  3. 触发更新:当实例的值改变时,会通知所有依赖于它的效果,让它们重新计算或执行。

省流:RefImpl 类似于发布-订阅模式的设计

谈谈reactive的局限性

1、仅对引用数据类型有效

reactive 主要适用于对象,包括数组和一些集合类型(如 MapSet)。对于基础数据类型(如 stringnumberboolean),reactive 是无效的。这意味着如果你尝试使用 reactive 来处理这些基础数据类型,将会得到一个非响应式的对象。

2、使用不当会失去响应

  1. 直接赋值对象:如果直接将一个响应式对象赋值给另一个变量,将会失去响应性。这是因为 reactive 返回的是对象本身,而不仅仅是代理。
import { reactive } from 'vue';
let state = reactive({ count: 0 });
state = { count: 1 }; // 失去响应性
  1. 直接替换响应式对象:同样,直接替换一个响应式对象也会导致失去响应性。
import { reactive } from 'vue';
let state = reactive({ count: 0 });
state = reactive({ count: 1 }); // 失去响应性
  1. 直接解构对象:在解构响应式对象时,如果直接解构对象属性,将会得到一个非响应式的变量。
const state = reactive({ count: 0 });
let { count } = state;count++; // count 仍然是 0
  1. 解决这个问题,需要使用 toRefs 函数来将响应式对象转换为 ref 对象。
import { toRefs } from 'vue';
const state = reactive({ count: 0 });
let { count } = toRefs(state);
count++; // count 现在是 1
  1. 将响应式对象的属性赋值给变量:如果将响应式对象的属性赋值给一个变量,这个变量的值将不会是响应式的。
let state = reactive({ count: 0 });
let count = state.countcount++  // count 仍然是 0console.log(state.count)

消息订阅发布模型(RefImpl

// Dep类负责收集依赖和通知更新
class Dep {
  constructor() {
    // 创建一个订阅者集合
    this.subscribers = new Set();
  }

  // depend方法用于收集依赖
  depend() {
    // 如果存在activeEffect,将其添加到订阅者集合中
    if (activeEffect) {
      this.subscribers.add(activeEffect);
    }
  }

  // notify方法用于通知更新
  notify() {
    // 遍历订阅者集合,执行每一个订阅者
    this.subscribers.forEach(effect => effect());
  }
}

let activeEffect = null;

function watchEffect(effect) {
  activeEffect = effect;
  effect();
  activeEffect = null;
}

// 定义一个RefImpl类
class RefImpl {
  // 构造函数,接收一个参数value
  constructor(value) {
    // 将参数value赋值给私有属性_value
    this._value = value;
    // 实例化一个Dep类,赋值给私有属性dep
    this.dep = new Dep();

  }

  // getter方法,获取私有属性_value的值
  get value() {
    // 调用dep的depend方法
    this.dep.depend();
    // 返回私有属性_value的值
    return this._value;
  }

  // setter方法,设置私有属性_value的值
  set value(newValue) {
    console.log('设置的新属性:', newValue);
    // 如果新值不等于私有属性_value的值
    if (newValue !== this._value) {
      // 将新值赋值给私有属性_value
      this._value = newValue;
      // 调用dep的notify方法
      this.dep.notify();
    }
  }
}

// 使用示例
let count = new RefImpl(0); 

watchEffect(() => {
  console.log(`The count is: ${count.value}`); // 订阅变化
});

count.value++; // 修改值,触发通知,重新执行watchEffect中的函数

文章作者:
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 !