vue3中ref和reactive的区别


最近在学vue3,把学习过程进行一个记录。

关于最基本的ref和reactive,网上的文章很多,这里自己做一个总结。

使用区别

二者都是用于定义响应式数据,表面上的区别比较简单:

  1. reactive定义引用数据类型,ref 定义基本类型
  2. reactive定义的变量直接使用,ref 定义的变量使用时需要.value
  3. 模板中均可直接使用,vue帮我们判断了是reactive还是ref定义的(通过__v_isRef属性),从而自动添加了.value。

例如:

// 定义
let count = ref(0);
let obj = reactive({a: 1, b: 2});
// 使用
count.value = 1;
menu  = {a: 1, b: 2, c: 3};

return {
	count,
	...toRefs(menu)
}

本质区别

源码细节较多,本篇只分析关于ref和reactive的核心代码
首先找到ref函数,调用了createRef函数,而createRef返回了RefImpl类

...
export function ref(value?: unknown) {
  return createRef(value, false)	// 创建ref
}

function createRef(rawValue: unknown, shallow: boolean) {
  if (isRef(rawValue)) {
    return rawValue
  }
  return new RefImpl(rawValue, shallow)	// 返回一个RefImpl对象
}

再来看下RefImpl类,是不是似曾相识的感觉,没错就是Object.defineProperty,这里做的事情就是收集依赖,触发依赖,只是换了个写法而已。

class RefImpl<T> {
  private _value: T	// 用来保存加工后实现响应化的值
  private _rawValue: T	// 用来保存当前未经加工过的值

  public dep?: Dep = undefined	// 用来收集依赖,这是一个 Set类型
  public readonly __v_isRef = true	// 用来标识该值是否经过 ref 加工

  constructor(value: T, public readonly __v_isShallow: boolean) {
  	// __v_isShallow 默认没有传,故默认为undefined,这里分别调用toRaw、toReactive
    this._rawValue = __v_isShallow ? value : toRaw(value)
    this._value = __v_isShallow ? value : toReactive(value)
  }

  get value() {
    trackRefValue(this)
    return this._value
  }

  set value(newVal) {
    const useDirectValue =
      this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
    newVal = useDirectValue ? newVal : toRaw(newVal)
    if (hasChanged(newVal, this._rawValue)) {
      this._rawValue = newVal
      this._value = useDirectValue ? newVal : toReactive(newVal)
      triggerRefValue(this, newVal)
    }
  }
}

接下来看看toReactive函数,可以看到,如果传入的参数是一个对象的话,返回值将会调用 reactive 方法来进行包裹,reactive 最终会通过 Proxy 来实现响应式

export const toReactive = <T extends unknown>(value: T): T =>
  isObject(value) ? reactive(value) : value

结论

  1. ref创建基本数据类型时,是通过vue2中类似与Object.defineProperty的做法实现响应
  2. ref创建对象时,内部调用的是reactive

也就是说,vue3中ref实际的对reactive的二次封装增强,reactive能做的ref一定能做。所以个人在开发中习惯性都写ref了。

Logo

前往低代码交流专区

更多推荐