vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > vue3 ref 和reactive

Vue3中关于ref和reactive的区别分析

作者:狂砍2分4篮板

这篇文章主要介绍了vue3关于ref和reactive的区别分析,文中通过示例代码介绍的非常详细,具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧

一、reactive

数据类型

reactive()可用于创造一个响应式对象,它接受一个参数,这个参数的类型是一个重点,接下来我们先看看Vue3的源码里是怎么处理的。

function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<any>,
  proxyMap: WeakMap<Target, any>
) {
  if (!isObject(target)) {
    if (__DEV__) {
      console.warn(`value cannot be made reactive: ${String(target)}`)
    }
    return target
  }
  // ...
}

Vue3主要是调用createReactiveObject函数来创建reactive对象,从源码里可以看到,该函数会首先判断reactive()传入的参数是不是一个对象,如果不是则不做任何处理,直接返回原值,同时开发环境下会在控制台输出一句警告。所以严格上来说,reactive()并不是像有些人说的不能传入基本类型的参数,它可以传,只是这种数据会失去响应式。

let name = reactive('张三')
setTimeout(() => {
  name = '李四'
  console.log(name, 'name') // 李四
}, 3000)
<template>
  <h1>{{ name }}</h1>
</template>

上面模板里仍然显示的是张三,因为name没有响应式不会触发视图更新。

原始数据与响应式数据

我们可以先定义一个原始对象,再将对象传入reactive(),此时原始对象和响应式对象会互相干扰。

let data = { name: '张三' } // 原始对象
let state = reactive(data)
setTimeout(() => {
  state.name = '李四'
  console.log(data.name) // 李四
}, 2000)

修改响应式对象state.name的值,原始对象data.name也会同步变化。

反过来也是一样,修改data.namestate.name也会同步变化,但需要注意的是:这种情况下虽然state.name发生了改变但视图并不会更新

let data = { name: '张三' }
let state = reactive(data)
setTimeout(() => {
  data.name = '李四'
  console.log(state.name) // 李四,但template里仍为张三
}, 2000)
</script>
<template>
  <h1>{{ state.name }}</h1>
</template>

所以如果你是通过定义一个对象再将对象传入reactive的话,建议始终是对响应式对象进行操作而不是原始数据。

二、ref

数据类型

export function ref(value?: unknown) {
  return createRef(value, false)
}

从源码里可以知道,ref()接受一个参数,参数可以是任意类型,这也是ref与reactive的区别之一,不管是基本类型还是引用类型ref都具备响应式,另外ref要通过.value进行取值。

let name = ref('张三') // 可以是基本类型
// 也可以是引用类型
let user = ref({
  name: '张三',
  age: 18
})
console.log(name.value)
console.log(user.value)

原始数据与响应式数据

refreactive一样,修改原始数据或响应式数据都会同步改变对方的值,且在修改原始对象时都无法触发视图更新。

ref也可能是一种reactive

function createRef(rawValue: unknown, shallow: boolean) {
  if (isRef(rawValue)) {
    return rawValue
  }
  return new RefImpl(rawValue, shallow)
}
class RefImpl<T> {
  private _value: T
  private _rawValue: T
  public dep?: Dep = undefined
  public readonly __v_isRef = true
  constructor(value: T, public readonly __v_isShallow: boolean) {
    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)
    }
  }
}

通过源码可以知道,ref是通过RefImpl这个类来创建的,而在RefImpl内部会调用toReactive函数,如果不是基本类型的数据,会将其转换成reactive

可重新赋值对象

使用reactive时,对响应式对象重新赋值是会失去响应式的。

let state = reactive({ name: '张三' })
setTimeout(() => {
  state = { name: '李四'}
  console.log(state.name) // 李四,数据变更了但视图不会更新
}, 2000)

ref则没有这种问题。

let state = ref({ name: '张三' })
setTimeout(() => {
  state.value = { name: '李四'}
  console.log(state.value.name) // 数据变更,同时视图也会更新
}, 2000)

使用watch监听ref对象需要deep

let state = ref({ name: '张三' })
setTimeout(() => {
  state.value.name = '李四'
}, 2000)
watch(state, () => {
  console.log(state.value.name)
}, { deep: true })

reactive不加deep: truewatch也会触发,而ref则需要手动加上。

小结

以上就是Vue3中关于ref和reactive的区别分析的详细内容,更多关于vue3 ref 和reactive的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文