1、执行下面代码
在packages\vue\examples\composition\test.html文件下
案例二:原始值类型 1
<script src="../../dist/vue.global.js"></script>
<div id="demo">
<h1>{{count}}</h1>
</div>
<script>
const { createApp, ref } = Vue
var app = createApp({
setup() {
var count = ref(1)
setTimeout(() => {
count.value++
}, 1000)
return {
count
}
}
})
app.mount('#demo')
</script>
案例二:为了检测ref只负责原始值类型值,至于对象则转接给reactive对象
<script src="../../dist/vue.global.js"></script>
<div id="demo">
<h1>{{obj}}</h1>
</div>
<script>
const { createApp, ref } = Vue
var app = createApp({
setup() {
var obj = ref({ a: 1 })
setInterval(() => {
obj.value.a++
}, 1000)
return {
obj
}
}
})
app.mount('#demo')
</script>
2、打断点
在packages\reactivity\src\ref.ts文件下,搜索return createRef(value, false)
function ref(value) {
return createRef(value, false)
}
function createRef(rawValue, shallow) {
if (isRef(rawValue)) {
return rawValue
}
return new RefImpl(rawValue, shallow)
}
function isRef(r){
return r && r.__v_isRef === true
}
class RefImpl{
public dep = undefined
public readonly __v_isRef = true // new RefImpl() 会给实例对象加上属性 __v_isRef 为 true
constructor(value, public readonly __v_isShallow) {
this._rawValue = toRaw(value) //
this._value = toReactive(value) // 如果是对象,则使用Reactive做处理,这时走的是reactive(value)的逻辑了
}
get value() {
trackRefValue(this)
return this._value
}
set value(newVal) {
newVal = toRaw(newVal)
if (hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal
this._value = toReactive(newVal)
triggerRefValue(this, newVal)
}
}
}
const isObject = (val) => val !== null && typeof val === 'object'
const toReactive = ( T ) => isObject(value) ? reactive(value) : value
function toRaw(observed){
const raw = observed && observed[ReactiveFlags.RAW]
return raw ? toRaw(raw) : observed
}
3、调用栈分析
- 在执行setup函数时候,会处理ref函数,将数据处理为类的实例,当访问到了实例的value属性,就会触发相应的函数trackRefValue和triggerRefValue
- 其中trackRefValue是收集依赖,并不是所有的触发get函数都会真的去收集依赖,只有在render中读取的才会收集
- 在setup函数内部访问到的就不会触发依赖
- triggerRefValue是触发依赖,等到设置了新的值时,触发该函数,进一步触发副作用的schedule函数
- 进而触发componentUpdateFn,在内部重新触发patch,就这样更新视图
代码流程图