案例
<script src="../../dist/vue.global.js"></script>
<div id="demo">
<h1>counts:{{ counts }}</h1>
<button @click="changeCounts">更改counts</button>
</div>
<script>
const { createApp, ref } = Vue;
createApp({
setup() {
const counts = ref(0)
const changeCounts = () => {
counts.value++
}
return {
counts,
changeCounts
}
},
}).mount('#demo')
</script>
调用ref函数发生了什么?返回了什么?
export function ref(value?: unknown) {
return createRef(value, false)
}
export function isRef(r: any): r is Ref {
return !!(r && r.__v_isRef === true)
}
function createRef(rawValue: unknown, shallow: boolean) {
if (isRef(rawValue)) {
return rawValue
}
return new RefImpl(rawValue, shallow)
}
从上面👆代码我们可以看出来,当我们执行const counts = ref(0)时,ref(0)其本质就是返回了一个RefImpl的构造函数
在createRef中,会先去判断传入的rawValue是否已经是一个ref类型的值,如果是的话就不需要再进行包装了
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)
// 存储被ref包装的值
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, DirtyLevels.Dirty, newVal)
}
}
}
- 当我们读取
counts的值时,会触发get函数,开始收集依赖并将值返回 - 当我们更改
counts的值时,会触发set函数,会判断是否浅代理,或者传入的值是浅代理的,或者传入的值是只读的,那么直接用传入的值,否则的话就找出最原始的值来,也就是某一个值包装成响应式对象后,我取出一开始进行包装的值也就是原始值 - 使用
hasChanged判断传入的值和原始值是否不一致,不一致则进行更改,并触发依赖