Vue3响应式系统的底层实现机制
Vue3的响应式系统基于ES6 Proxy实现,相比Vue2的Object.defineProperty方案,解决了无法检测属性新增/删除、数组索引直接设值等缺陷。核心流程是:数据读取时收集依赖(track),数据修改时触发更新(trigger)。
reactive函数为对象创建Proxy代理:
// 简化版reactive实现原理
const reactiveMap = new WeakMap()
function reactive(target) {
if (typeof target !== "object" || target === null) return target
if (reactiveMap.has(target)) return reactiveMap.get(target)
const proxy = new Proxy(target, {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver)
track(target, key) // 依赖收集
if (typeof result === "object" && result !== null) {
return reactive(result) // 深层响应式
}
return result
},
set(target, key, value, receiver) {
const oldValue = target[key]
const result = Reflect.set(target, key, value, receiver)
if (oldValue !== value) {
trigger(target, key) // 触发更新
}
return result
}
})
reactiveMap.set(target, proxy)
return proxy
}
ref的内部实现使用class封装,通过get/set访问器实现依赖追踪:
class RefImpl {
private _value
private _rawValue
public dep = undefined
constructor(value) {
this._rawValue = value
this._value = isObject(value) ? reactive(value) : value
}
get value() {
trackRefValue(this) // 收集依赖
return this._value
}
set value(newVal) {
if (hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal
this._value = isObject(newVal) ? reactive(newVal) : newVal
triggerRefValue(this) // 触发更新
}
}
}
effect与依赖收集追踪系统
effect是响应式系统的调度核心。computed、watch、组件渲染函数最终都通过effect实现:
let activeEffect = null
const effectStack = []
function effect(fn, options = {}) {
const effectFn = () => {
activeEffect = effectFn
effectStack.push(effectFn)
const result = fn() // 执行函数,触发响应式对象的get,收集依赖
effectStack.pop()
activeEffect = effectStack[effectStack.length - 1]
return result
}
effectFn.deps = [] // 存储该effect依赖的所有依赖集合
effectFn.scheduler = options.scheduler
if (!options.lazy) effectFn()
return effectFn
}
// 依赖收集
const targetMap = new WeakMap()
function track(target, key) {
if (!activeEffect) return
let depsMap = targetMap.get(target)
if (!depsMap) targetMap.set(target, (depsMap = new Map()))
let dep = depsMap.get(key)
if (!dep) depsMap.set(key, (dep = new Set()))
dep.add(activeEffect)
activeEffect.deps.push(dep)
}
// 触发更新
function trigger(target, key) {
const depsMap = targetMap.get(target)
if (!depsMap) return
const dep = depsMap.get(key)
if (!dep) return
const effectsToRun = new Set(dep)
effectsToRun.forEach(effectFn => {
if (effectFn.scheduler) {
effectFn.scheduler(effectFn) // computed/watch使用调度器
} else {
effectFn() // 直接执行
}
})
}
自定义Hook设计模式与最佳实践
组合式API的核心优势是逻辑复用。自定义Hook将相关状态和方法封装为独立模块,替代Mixins的命名冲突和来源不明确问题。
网络请求Hook:
import { ref, shallowRef } from "vue"
export function useFetch(url, options = {}) {
const data = shallowRef(null)
const error = ref(null)
const loading = ref(false)
let controller = null
async function execute(reqUrl = url, reqOptions = options) {
if (controller) controller.abort() // 取消上次请求
controller = new AbortController()
loading.value = true
error.value = null
try {
const response = await fetch(reqUrl, {
...reqOptions,
signal: controller.signal
})
if (!response.ok) throw new Error(`HTTP ${response.status}`)
data.value = await response.json()
} catch (e) {
if (e.name !== "AbortError") {
error.value = e
}
} finally {
loading.value = false
controller = null
}
}
function cancel() {
if (controller) controller.abort()
}
if (options.immediate !== false) execute()
return { data, error, loading, execute, cancel }
}
防抖搜索Hook:
import { ref, watch, onUnmounted } from "vue"
export function useDebounceSearch(delay = 300) {
const keyword = ref("")
const results = ref([])
const searching = ref(false)
let timer = null
watch(keyword, (val) => {
clearTimeout(timer)
if (!val.trim()) {
results.value = []
return
}
searching.value = true
timer = setTimeout(async () => {
try {
const response = await fetch(`/api/search?q=${encodeURIComponent(val)}`)
results.value = await response.json()
} catch {
results.value = []
} finally {
searching.value = false
}
}, delay)
})
onUnmounted(() => clearTimeout(timer))
return { keyword, results, searching }
}
表单验证Hook:
import { reactive, computed } from "vue"
export function useForm(schema) {
const form = reactive(
Object.fromEntries(
Object.keys(schema).map(k => [k, ""])
)
)
const errors = reactive(
Object.fromEntries(
Object.keys(schema).map(k => [k, ""])
)
)
const isValid = computed(() =>
Object.keys(schema).every(key => !errors[key])
)
function validate() {
let valid = true
for (const [key, rules] of Object.entries(schema)) {
errors[key] = ""
for (const rule of rules) {
const msg = rule(form[key], form)
if (msg) {
errors[key] = msg
valid = false
break
}
}
}
return valid
}
function reset() {
Object.keys(form).forEach(k => form[k] = "")
Object.keys(errors).forEach(k => errors[k] = "")
}
return { form, errors, isValid, validate, reset }
}
// 使用示例
const { form, errors, isValid, validate } = useForm({
username: [
v => v ? "" : "用户名必填",
v => v.length >= 3 ? "" : "用户名至少3个字符"
],
email: [
v => v ? "" : "邮箱必填",
v => /@/.test(v) ? "" : "邮箱格式不正确"
]
})
响应式性能优化策略
响应式系统不是零成本的。大规模数据对象、频繁更新的场景需要针对性优化:
– shallowRef/shallowReactive:只在顶层属性建立响应式,深层属性变化不触发更新,适合大型不可变数据
– markRaw:标记对象跳过响应式转换,传入reactive时不会被代理
– triggerRef:手动触发shallowRef的更新通知,配合shallowRef实现精细控制
– computed缓存:只有依赖变化时才重新计算,避免重复计算开销
– v-once:静态内容只渲染一次,后续更新跳过
合理使用这些API,在保持响应式能力的同时将性能损耗控制在可接受范围内。自定义Hook的设计原则是单一职责、显式返回、清理副作用,让每个Hook成为一个可独立测试和复用的逻辑单元。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/vue3-zu-he-shi-api-xiang-ying-shi-yuan-li-shen-du-jie-xi-yu/