Vue3嵌套ref问题:无法访问/赋值内部ref的value属性?
Vue 3嵌套Ref初始值为undefined/null时无法访问.value的问题
问题现象
当外层是指向对象的ref,内部嵌套的ref初始值为undefined或null时,尝试访问内部ref的.value属性会报错;但内部ref初始值为对象、数字等非空值时可正常操作,单独的ref(外层直接是undefined/null)也能正常赋值。
示例代码:
const { ref } = Vue; let options = { setup: function () { const outer = ref({ innerUndefined: ref(undefined), innerNull: ref(null), innerObject: ref({}), innerZero: ref(0) }); const outerUndefined = ref(undefined); const outerNull = ref(null); // 尝试设置innerUndefined.value失败 console.log('Attempting to set innerUndefined.value'); try { outer.value.innerUndefined.value = { 'test': 1 }; } catch (error) { console.log('FAIL: ' + error.toString()); } // 尝试设置innerNull.value失败 console.log('Attempting to set innerNull.value'); try { outer.value.innerNull.value = { 'test': 2 }; } catch (error) { console.log('FAIL: ' + error.toString()); } // 设置innerObject.value成功 console.log('Attempting to set innerObject.value'); outer.value.innerObject.value = { 'test': 3 }; console.log('SUCCESS'); // 设置innerZero.value成功 console.log('Attempting to set innerZero.value'); outer.value.innerZero.value = { 'test': 4 }; console.log('SUCCESS'); // 设置outerUndefined.value成功 console.log('Attempting to set outerUndefined.value'); outerUndefined.value = { 'test': 5 }; console.log('SUCCESS'); // 设置outerNull.value成功 console.log('Attempting to set outerNull.value'); outerNull.value = { 'test': 6 }; console.log('SUCCESS'); return {}; } }; Vue.createApp(options).mount('#app');
<script src="https://unpkg.com/vue@3.2.47/dist/vue.global.js"></script> <div id="app">See the console</div>
原因分析
这是Vue 3的ref自动解包机制导致的:
- 当
ref作为响应式对象(外层ref的.value会被Vue转为响应式对象)的属性时,Vue会自动对其解包,直接返回ref的.value值。 - 对于初始值为
undefined/null的内部ref,解包后得到的就是undefined/null,而非ref实例,自然没有.value属性,赋值时会报错。 - 初始值为对象、数字的内部
ref,解包后虽返回对应值,但Vue对这类类型的响应式处理逻辑会保留关联,允许通过.value反向操作原始ref;单独的ref不存在嵌套在响应式对象中的场景,不会触发自动解包,直接操作的是ref实例本身。
解决方案
方案1:提前保存内部ref到独立变量
将内部ref提前赋值给独立变量,绕过外层响应式对象的自动解包,直接通过变量操作:
const { ref } = Vue; let options = { setup: function () { // 提前定义并保存内部ref const innerUndefined = ref(undefined); const innerNull = ref(null); const innerObject = ref({}); const innerZero = ref(0); const outer = ref({ innerUndefined, innerNull, innerObject, innerZero }); // 直接通过变量操作,成功赋值 console.log('Attempting to set innerUndefined.value via variable'); innerUndefined.value = { 'test': 1 }; console.log('SUCCESS'); console.log('Attempting to set innerNull.value via variable'); innerNull.value = { 'test': 2 }; console.log('SUCCESS'); return {}; } }; Vue.createApp(options).mount('#app');
方案2:使用markRaw阻止自动解包
用markRaw标记内部ref,让Vue不对其进行自动解包,外层响应式对象的属性会保留ref实例:
const { ref, markRaw } = Vue; let options = { setup: function () { const outer = ref({ innerUndefined: markRaw(ref(undefined)), innerNull: markRaw(ref(null)), innerObject: ref({}), innerZero: ref(0) }); // 正常访问.value赋值 console.log('Attempting to set innerUndefined.value with markRaw'); outer.value.innerUndefined.value = { 'test': 1 }; console.log('SUCCESS'); console.log('Attempting to set innerNull.value with markRaw'); outer.value.innerNull.value = { 'test': 2 }; console.log('SUCCESS'); return {}; } }; Vue.createApp(options).mount('#app');
方案3:外层改用shallowRef
如果不需要外层对象的深度响应式,使用shallowRef,它不会对内部对象进行响应式转换,内部ref不会被自动解包:
const { ref, shallowRef } = Vue; let options = { setup: function () { const outer = shallowRef({ innerUndefined: ref(undefined), innerNull: ref(null), innerObject: ref({}), innerZero: ref(0) }); // 正常访问.value赋值 console.log('Attempting to set innerUndefined.value with shallowRef'); outer.value.innerUndefined.value = { 'test': 1 }; console.log('SUCCESS'); console.log('Attempting to set innerNull.value with shallowRef'); outer.value.innerNull.value = { 'test': 2 }; console.log('SUCCESS'); return {}; } }; Vue.createApp(options).mount('#app');
内容的提问来源于stack exchange,提问作者Agendum
相关产品推荐
相关产品推荐

