问题描述
最近产品提了一个问题,有一个页面在生产环境下500错误,必现,但是在本地开发环境下没有问题,以及测试环境都没有复现该问题。
问题分析
-
本地开发环境没有问题,说明大概率是数据问题导致,前端代码出现未捕获的异常导致的(node项目做了DCDN会缓存页面, 所以异常的页面要返回标准状态码,而不是自定义状态页)。
-
本地连接生产环境的接口,复现问题, 该页面存在兜底逻辑, 响应数据为空,去掉一部分条件进行第一次重试, 如果响应还是空,那么就只保留一个条件第二次重试。
-
通过日志报错信息,发现是
nuxt instance unavailable错误, 说明是nuxt实例不可用。查看整个错误堆栈信息,调用链如下,
也就是在第二次尝试的时候,调用了useNuxtApp方法, 但是nuxt实例不可用。通过查询nuxt3 官网得知。
在Composition API中上下文是隐性传递的,Vue在组件的生命周期中设置当前组件的临时实例(nuxtApp的临时实例)到全局变量中,并在同一个时刻取消设置。
而且在服务端渲染的时候,有多个用户请求,nuxtApp 运行在同一个全局上下文中,因此nuxt和vue会立即取消设置全局实例,以避免在两个用户或组件之间泄漏共享引用。
所以问题就出现在这里了,第二次重试的时候,nuxt实例已经被取消了,所以useNuxtApp获取不到,会报错nuxt instance unavailable。
解决方案
官方提供的方案是显示的传递nuxt实例vue 3.3.2+不推荐
社区提供的方案不推荐
官方建议的方案推荐
问题总结
vue 本身不建议使用顶层await, 导致await之后的代码无法获取实例上下文, 本质原因是setup本身是个同步执行的函数,内部在执行完毕后会清空实例数据。实验性质中会讲setup转换为async setup,同时对于使用await时会使用withAsyncContext对其进行包裹来重新获取上下文。
async setup() 必须与 Suspense 组合使用,该特性目前仍处于实验阶段。我们计划在未来的版本中完成该特性并编写文档——但如果你现在就感兴趣,可以参考其测试来了解其工作方式。
nuxt中本质是是同一个问题导致的,nuxt3中提供了runWithContext方法,可以在异步函数中获取nuxt实例上下文。但是也是不推荐使用的。
最好的方法是将await放到生命周期函数中,或者使用useAsyncData来获取数据,这样就不会出现nuxt实例不可用的问题。如本次的问题,useAsyncData 本身就是支持异步函数,而不单纯的像useFetch一样作为请求用的。所以我们重试的逻辑应该是放大一个异步函数中完成,并最终返回结果数据。
