跟着官网走
首先看官网案例:
xml
<script setup lang="ts">
const route = useRoute()
const { data, error } = await useFetch(`/api/movies/${route.params.slug}`)
if (!data.value) {
throw createError({ statusCode: 404, statusMessage: 'Page Not Found' })
}
</script>
思路就是将setup
中的代码通过await
同步执行,请求完接口后对data
或error
进行判断,然后就能按条件显示error.vue
页面。其中,官方也更推荐用showError
代替throw createError
。但是这样的写的问题是:1、没有对接口调用进行封装;2、对接口的错误处理也没有封装。
努力调试
通常,我希望通过api.getUserInfo(data)
这样的方式调用接口,封装方法看我另一篇文章。封装后,对于SSR时接口调用失败的处理,应该统一封装在$fetch
的onResponseError
中,如:
javascript
function fetchWrapper(url, opts) {
return $fetch(url, {
...opts,
onResponseError({ request, options, response }) {
if (import.meta.server) {
showError({ statusCode: 404, statusMessage: 'Page Not Found' })
}
},
}).catch(() => {});
}
但尝试后发现,showError
没有生效。进一步测试未做$fetch
封装的几种情况:
xml
<script setup>
const { data } = await useAsyncData(async () => {
// 能生效
showError({ statusCode: 404, statusMessage: 'Page Not Found' })
const res = await $fetch(url, {
onRequestError({ request, options, error }) {
if (import.meta.server) {
// onRequestError中能生效
showError({ statusCode: 404, statusMessage: 'Page Not Found' })
}
},
onResponseError({ request, options, response }) {
if (import.meta.server) {
// onResponseError中不生效
showError({ statusCode: 404, statusMessage: 'Page Not Found' })
}
},
})
// 不生效
showError({ statusCode: 404, statusMessage: 'Page Not Found' })
})
</script>
所以,不是封装导致的问题。总结一下,应该是请求前能生效,请求后及其回调都不生效。
大胆实践
个人猜测,这可能是Nuxt的一个小bug。我实践了一个可行的解决办法,就是建一个全局状态serverRequestError
,在接口回调中更新此状态,在组件的setup
的末尾判断一下此状态是否有值,有的话showError
。
stores/serverRequestError.js
的代码如下:
javascript
export const useServerRequestError = defineStore('serverRequestError', () => {
const code = ref(-1)
const reason = ref('')
function create({ url, statusCode, statusMessage }) {
code.value = statusCode
reason.value = `${url} ${statusCode} ${statusMessage}`
}
function clear() {
code.value = -1
reason.value = ''
}
return {
reason,
create,
clear,
}
})
// 创建error信息
export function createServerRequestError(opts) {
useServerRequestError().create(opts)}
// 检查是否有error
export function checkServerRequestError() {
if (import.meta.server) {
const store = useServerRequestError()
store.reason && showError({
statusCode: store.code || 404,
statusMessage: store.reason,
})
}
}
// 清除error
export function clearServerRequestError() {
useServerRequestError().clear()}
在onResponseError
中调用createServerRequestError()
:
vbscript
$fetch(url, {
onResponseError({ request, options, response }) {
const { status, statusText } = response;
if (import.meta.server){
useServerRequestError().create({ url: request, statusCode: status, statusMessage: statusText }) }
}
})
在需要检查的业务代码的末尾调用checkServerRequestError()
:
xml
<script setup>
const { data } = await useAsyncData(async () => {
const res = await apis.get404()
return res
})
checkServerRequestError()
</script>
在error.vue
也别忘了调用clearServerRequestError()
清除error
信息:
思考优化
感觉这个自定义的全局状态与Nuxt提供的useError
有点像,可惜尝试了很多办法,还是不知道useError
怎么用进去。
还可以优化的点,就是Nuxt提供一个在SSR时的setup
执行的结尾可执行的钩子来执行checkServerRequestError()
,遗憾的是我还没发现可用的钩子。
还可以考虑在layout
组件判断状态、或封装状态组件的方式来触发showError
,但是都还不够优雅,如果有更好的方案,请评论区留言哦~