Vue3 Options API中Provide/Inject注入值undefined问题排查
问题背景
我使用Vue3的Options API开发,在provider.vue中通过provide传递reactRefClsDigitizePolygonInstance对象,且该组件的watch钩子日志已确认reactRefClsDigitizePolygonInstance.value已赋值并存在有效内容。但在injector.vue中注入该值后,切换按钮触发isToggleBtnClearDigitizedCheckState变化时,调用this.injClsDigitizePolygonInteractionsMapInstance.value.clear()抛出错误:
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'value')
即this.injClsDigitizePolygonInteractionsMapInstance为undefined。
相关代码片段
provider.vue
export default { data() { return { reactRefClsDigitizePolygonInstance: { value: undefined, }, }; }, provide() { return { [InjectionKeys.PROVIDE_KEY_CLASS_DIGITIZE_POLYGON_INTERACTIONS_INSTANCE.description]: this.reactRefClsDigitizePolygonInstance, }; }, watch: { 'injMapInstance.value'(newVal, oldVal) { console.log({msg:verboseTag + ' watched injMapInstance is called where oldVal: ' + oldVal + ', and newVal: ' + newVal}); if (oldVal == undefined && newVal != undefined) { if (this.injMapInstance) { if (this.injMapInstance.value) { if (newVal != undefined && oldVal != undefined && newVal != oldVal) { const msg = JSON.stringify({msg:WTFTag + 'value of injMapInstance.value is defined and changed from:' + oldVal + ' to: ' + newVal}); throw new Error(msg); } if (clsDigitizePolygonInteractions) { clsDigitizePolygonInteractions.setMapInstance(this.injMapInstance.value); this.polygonDigitizationProgressStatus = clsDigitizePolygonInteractions.listenToDigitizationProgressStatus(); this.reactRefClsDigitizePolygonInstance.value = clsDigitizePolygonInteractions; // 日志显示this.reactRefClsDigitizePolygonInstance.value有值 const msg = JSON.stringify({msg:WTFTag + 'reactRefClsDigitizePolygonInstance: ' + JSON.stringify(this.reactRefClsDigitizePolygonInteractions.value, null, 4)}); console.log(debugTag, '###msg:', msg) } } } } }, } }
injector.vue
export default { inject: { injClsDigitizePolygonInteractionsMapInstance: { from: InjectionKeys.PROVIDE_KEY_CLASS_DIGITIZE_POLYGON_INTERACTIONS_INSTANCE.description }, }, watch: { isToggleBtnClearDigitizedCheckState(newVal, oldVal) { this.injClsDigitizePolygonInteractionsMapInstance.value.clear(); // 此处触发报错 }, } }
报错信息
vue:69 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'value') at Proxy.isToggleBtnClearDigitizedCheckState (ToggleButtonClearDigit
问题原因分析
- 注入键不匹配:
provide和inject使用的键(InjectionKeys.PROVIDE_KEY_CLASS_DIGITIZE_POLYGON_INTERACTIONS_INSTANCE.description)可能存在字符串拼写、大小写差异,或动态值不一致的情况,导致无法匹配到提供的值。 - 组件层级错误:
injector.vue必须是provider.vue的直接或间接后代组件,若两者不在同一组件树分支,或provider.vue未作为injector.vue的祖先渲染,注入会失败。 - inject未设置默认值:当注入失败时,未配置默认值会导致
injClsDigitizePolygonInteractionsMapInstance直接为undefined,调用其属性时触发报错。 - 响应式绑定问题:虽然修改了
reactRefClsDigitizePolygonInstance.value,但如果provide的对象本身不是响应式的,极端情况下子组件可能无法感知到对象的存在(不过此处是对象引用传递,该概率较低)。
修复方案
1. 校验注入键一致性
在provider.vue和injector.vue的created钩子中分别打印注入键的值,确认两者完全一致:
// provider.vue created() { console.log('provide key:', InjectionKeys.PROVIDE_KEY_CLASS_DIGITIZE_POLYGON_INTERACTIONS_INSTANCE.description); } // injector.vue created() { console.log('inject key:', InjectionKeys.PROVIDE_KEY_CLASS_DIGITIZE_POLYGON_INTERACTIONS_INSTANCE.description); }
若不一致,修正键值确保两者完全匹配。
2. 确认组件层级关系
检查项目的组件树结构,确保injector.vue是provider.vue的后代组件。若需要全局注入,可在项目入口文件中使用app.provide全局提供该值:
// main.js import { createApp } from 'vue'; import App from './App.vue'; import { InjectionKeys } from './path/to/InjectionKeys'; import clsDigitizePolygonInteractions from './path/to/your-module'; const app = createApp(App); app.provide( InjectionKeys.PROVIDE_KEY_CLASS_DIGITIZE_POLYGON_INTERACTIONS_INSTANCE.description, { value: clsDigitizePolygonInteractions } // 或根据实际情况初始化 ); app.mount('#app');
3. 为inject添加默认值与空值校验
修改injector.vue的配置,添加默认值,并在调用方法前做空值判断,避免直接访问undefined的属性:
export default { inject: { injClsDigitizePolygonInteractionsMapInstance: { from: InjectionKeys.PROVIDE_KEY_CLASS_DIGITIZE_POLYGON_INTERACTIONS_INSTANCE.description, default: () => ({ value: null }) // 返回空对象避免undefined }, }, watch: { isToggleBtnClearDigitizedCheckState(newVal, oldVal) { // 可选链操作符多层校验 if (this.injClsDigitizePolygonInteractionsMapInstance?.value?.clear) { this.injClsDigitizePolygonInteractionsMapInstance.value.clear(); } else { console.warn('数字化实例未初始化或不存在clear方法'); } }, } }
4. 使用Vue3响应式API优化provide对象
将reactRefClsDigitizePolygonInstance改为用ref创建,确保响应式绑定更符合Vue3规范:
// provider.vue import { ref } from 'vue'; export default { setup() { // 用ref创建响应式对象 const reactRefClsDigitizePolygonInstance = ref(undefined); return { reactRefClsDigitizePolygonInstance }; }, provide() { return { [InjectionKeys.PROVIDE_KEY_CLASS_DIGITIZE_POLYGON_INTERACTIONS_INSTANCE.description]: this.reactRefClsDigitizePolygonInstance, }; }, watch: { 'injMapInstance.value'(newVal, oldVal) { // ...其他逻辑不变 if (clsDigitizePolygonInteractions) { // 直接赋值给ref的value this.reactRefClsDigitizePolygonInstance.value = clsDigitizePolygonInteractions; } }, } }
此时injector.vue中调用时,同样用可选链做校验:
this.injClsDigitizePolygonInteractionsMapInstance?.value?.clear();
内容的提问来源于stack exchange,提问作者user22470297

