对于原始值(Primitive values),按照值传递,而非按引用传递。
JavaScript 中的 Proxy 无法提供对原始值的代理,因此想要将原始值变成响应式数据,必须对其做一层包裹,即 Vue.js 中 ref
源码实现
export function ref(value?: unknown) {
return createRef(value, false)
}
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)
}
}
}
function trackRefValue(ref: RefBase<any>) {
if (shouldTrack && activeEffect) {
ref = toRaw(ref)
if (__DEV__) {
trackEffects(ref.dep || (ref.dep = createDep()), {
target: ref,
type: TrackOpTypes.GET,
key: 'value'
})
} else {
trackEffects(ref.dep || (ref.dep = createDep()))
}
}
}
export const toReactive = (value) => isObject(value) ? reactive(value) : value
export function toRaw(observed) {
const raw = observed && observed['__v_raw']
return raw ? toRaw(raw) : observed
}
ref() API 返回的是一个包装对象(value reference)?JavaScript 中分基本数据类型和引用数据类型,基本数据类型是通过值传递(非引用传递)。如果在一个函数中返回一个字符串变量,接收到这个字符串的代码只会获得一个值,是无法追踪原始变量后续的变化的。
而对于在 template 中可以使用,没有使用 .value。因为包装对象被暴露给模版渲染上下文,或是被嵌套在另一个响应式对象中的时候,它会被自动展开 (unwrap) 为内部的值,。理解为:做了拆箱操作——包装对象的自动展开(Ref Unwrapping)
Ref
import { ref } from 'vue'
let foo = 0
let bar = ref(0)
foo = 1
bar = 1 // ts-error
PROS
CONS
.valueReactive
import { reactive } from 'vue'
const foo = { prop: 0 }
const bar = reactive({ prop: 0 })
foo.prop = 1
bar.prop = 1
PROS
.value)CONS
watchconst counter = ref(0)
watch(counter, count => {
console.log(count) // same as Icounter.value'
})
<template>
<button @click="counter += 1">
Counter is {{ counter }}
</button>
</template>
import { ref, reactive } from 'vue'
const foo = ref('bar')
const data = reactive({ foo, id: 10 })
data.foo // 'bar'