Vue3中关于ref和reactive的区别分析
作者:狂砍2分4篮板
一、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.name
,state.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)
原始数据与响应式数据
ref
与reactive
一样,修改原始数据或响应式数据都会同步改变对方的值,且在修改原始对象时都无法触发视图更新。
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: true
watch也会触发,而ref则需要手动加上。
小结
- 对于响应式而言,ref支持对象类型和基本类型,而reactive只支持对象类型
- 当ref是对象类型的时候,本质上也是一个reactive
- 对象类型的ref和reactive,其响应式底层原理都是Proxy
以上就是Vue3中关于ref和reactive的区别分析的详细内容,更多关于vue3 ref 和reactive的资料请关注脚本之家其它相关文章!