Vue3 响应式系统
响应式原理
Vue3 使用 Proxy 实现响应式,相比 Vue2 的 Object.defineProperty 有以下优势:
- 可以监听数组索引和长度的变化
- 可以监听对象属性的添加和删除
- 更好的性能
核心 API
ref
import { ref } from 'vue'
const count = ref(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
// 在模板中自动解包
<template>
<div>{{ count }}</div>
</template>
reactive
import { reactive } from 'vue'
const state = reactive({
count: 0,
nested: {
value: 1
}
})
state.count++
state.nested.value++
shallowRef
import { shallowRef } from 'vue'
const state = shallowRef({ count: 0 })
// 不会触发更新
state.value.count++
// 会触发更新
state.value = { count: 1 }
shallowReactive
import { shallowReactive } from 'vue'
const state = shallowReactive({
count: 0,
nested: { value: 1 }
})
// 会触发更新
state.count++
// 不会触发更新
state.nested.value++
toRef / toRefs
import { reactive, toRef, toRefs } from 'vue'
const state = reactive({
foo: 1,
bar: 2
})
// 创建单个 ref
const fooRef = toRef(state, 'foo')
fooRef.value++ // state.foo 也会更新
// 解构保持响应性
const { foo, bar } = toRefs(state)
unref
import { ref, unref } from 'vue'
const count = ref(0)
console.log(unref(count)) // 0
const num = 1
console.log(unref(num)) // 1
isRef / isReactive / isProxy
import { ref, reactive, isRef, isReactive, isProxy } from 'vue'
const count = ref(0)
const state = reactive({})
console.log(isRef(count)) // true
console.log(isReactive(state)) // true
console.log(isProxy(count)) // false
console.log(isProxy(state)) // true
响应式工具
toRaw
import { reactive, toRaw } from 'vue'
const state = reactive({ count: 0 })
const raw = toRaw(state)
console.log(raw === state) // false
raw.count++ // 不会触发更新
markRaw
import { reactive, markRaw } from 'vue'
const obj = markRaw({ count: 0 })
const state = reactive({ obj })
// obj 不会被转换为响应式
state.obj.count++ // 不会触发更新
effectScope
import { effectScope, ref, watchEffect } from 'vue'
const scope = effectScope()
scope.run(() => {
const count = ref(0)
watchEffect(() => {
console.log(count.value)
})
})
// 停止所有副作用
scope.stop()
高级用法
customRef
import { customRef } from 'vue'
function useDebouncedRef(value, delay = 200) {
let timeout
return customRef((track, trigger) => {
return {
get() {
track()
return value
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger()
}, delay)
}
}
})
}
const text = useDebouncedRef('hello')
triggerRef
import { shallowRef, triggerRef } from 'vue'
const state = shallowRef({ count: 0 })
state.value.count++
triggerRef(state) // 手动触发更新