本文共 4202 字,大约阅读时间需要 14 分钟。
Vue 3 中,针对基础类型和引用类型数据的响应性实现方式存在显著差异。本文将从 Ref 实现原理、代码解析以及实际应用场景等多个方面展开全面探讨。
Ref 是 Vue 3 提供的一种强式响应式简写语法,其核心实现目标是可以只通过 .value
属性修改业务逻辑,强化开发体验。在实现上,ref
与 _v_isRef
标志位的判断逻辑plays大角色。
RefImpl 是 Ref 实现的核心类,主要属性包括 _rawValue
和 _value
,分别代表原始值和被包装的值。以下是关键代码段:
class RefImpl { constructor(_rawValue, _shallow = false) { this._rawValue = _rawValue; this._shallow = _shallow; // shallowRef 标志位 this.__v_isRef = true; this._value = _shallow ? _rawValue : convert(_rawValue); } get value() { track(toRaw(this), 'get', 'value'); return this._value; } set value(newVal) { if (hasChanged(toRaw(newVal), this._rawValue)) { this._rawValue = newVal; this._value = this._shallow ? newVal : convert(newVal); trigger(toRaw(this), 'set', 'value', newVal); } }}
track
函数,触发响应式更新。convert
转换。值得注意的是,基础类型的 Ref 与 Reactive 没必然联系,引用类型的 Ref 则会_embryos_一个 Reactive
实例:
// 基础类型 const refCount = ref(0) // backingValue 直接已一个数值 // 引用类型 const refObject = ref({ value: 0 }) // backingValue 是一个包含 value 属性的对象// 在 value 属性上挂解重新应性 // 其背后为一个 Reactve 实例 {// value: 0 // }
ShallowRef 实现仅对 .value 属性进行响应式追踪,深层结构则完全不响应变化:
// 浅层 Ref const srefCount = shallowRef(0)// 全深层引用类型 const srefObject = shallowRef({ value: 0 })// 嵌套对象 const srefObjectMore = shallowRef({ value: { a: 'jyk' } })
当涉及到嵌套对象的响应式修改时,通常需要手动调用 triggerRef
:
// 修改嵌套对象的属性 const setObjectProp = () => { srefObjectMore.value.value.a = new Date().getTime() triggerRef(srefObjectMore)}// 修改嵌套对象的 .value const setObjectMore = () => { srefObjectMore.value = { value: { a: new Date().getTime() } } triggerRef(srefObjectMore)}
toRef
和 toRefs
作为辅助函数,主要用于将 Reactve 实例中的特定属性转为 Ref 类型,同时保持响应式连接。两者均依赖于 ObjectRefImpl
内部实现:
// 单个属性 Ref const refName = toRef(person, 'name')// 多个属性 Ref const personToRefs = toRefs(person)
function toRef(object, key) { const objKey = object[key]; return isRef(objKey) ? objKey : new ObjectRefImpl(object, key);}// ObjectRefImpl 实现结构class ObjectRefImpl { constructor(_object, _key) { this._object = _object; this._key = _key; this.__v_isRef = true; } get value() { return this._object[this._key]; } set value(newVal) { this._object[this._key] = newVal; }}
function toRefs(object) { if (!isProxy(object)) { console.warn('pegp'); } const ret = isArray(object) ? new Array(object.length) : {}; for (const key in object) { if (hasOwn(object, key)) { ret[key] = toRef(object, key); } } return ret;}
延迟响应控制台是一个实际场景,常见于搜索输入等交互:
const useDebouncedRef = (value, delay = 200) => { let timeout; return customRef((track, trigger) => ({ get() { track() return value; }, set(newVal) { clearTimeout(timeout); timeout = setTimeout(() => { value = newVal; trigger(); }, delay); } }));}
const myComputed = (_get, _set) => { return customRef((track, trigger) => ({ get() { track(); if (typeof _get === 'function') { return _get(); } else { console.warn('未配置 get 方法') } }, set(newVal) { if (typeof _set === '-function') { _set(newVal) trigger(); } else { console.warn('未配置 set 方法') } } }));}
// 简单计算属性const myCom = myComputed(() => refCount.value + 1)// 完整计算属性const setRef = () => { // 直接修改 Ref 的 .value refCount.value += 1}
Vue 原生计算属性实现通过 computed
factory函数构建,比 Ref 更强大灵活:
function computed(getterOrOptions) { let getter; let setter; if (isFuncton(getterOrOptions)) { getter = getterOrOptions; setter = () => { console.warn('不可读写') } } else { getter = getterOrOptions.get; setter = getterOrOptions.set; } return new ComputedRefImpl(getter, setter, isFunction(getterOrOptions) || (!getterOrOptions.set));}
Vue 3 的 Ref 和 Reactve 几乎完全不同 redraw机制。Ref 对简单类型实现原生响应式,而对复杂对象,只有通过引用对象的属性变化才能触发。
在实际应用中, mastering RefAndReactive 的关系规则是核心技能之一。
通过以上理解,我们可以更合理地选择 Ref 与 Reactve 的结合方式,从而充分发挥 Vue.js 的响应式能力。
转载地址:http://wltyk.baihongyu.com/