Nuxt 自定义 dev 模式下的 错误模板(loadingTemplate)
每天一个小知识点
在Nuxt 的 dev 里面有一个配置 是loadingTemplate,我原先以为是 开发模式下的 加载 HTML 模板,结果 好像没有我想的那么简单呢!!!
先来看看 Nuxt 官方对其的介绍吧。
loadingTemplate
Template to show a loading screen (显示一个加载场景的模板)接受一个 Function,需要返回 加载的HTML render 字符串
我一开始以为也是这样,直到我配置 了 之后 ,我发现好像没什么 卵用,好像 在开发的时候 不会渲染出来
出于好奇心,我就顺着 Nuxt 的 cli 工具 找 Nuxt 的这个 配置到底是干嘛的。
在 nuxi 的 dev.ts 下 就可以看到其是如何使用这个 loadingTemplate的呢
async _renderError(res: ServerResponse, _error?: Error) {
res.statusCode = 503
res.setHeader('Content-Type', 'text/html')
const loadingTemplate
= this.options.loadingTemplate
|| this._currentNuxt?.options.devServer.loadingTemplate
|| (
await importModule('@nuxt/ui-templates', this.options.cwd).then(
r => r.loading,
)
).catch(() => {})
|| ((params: { loading: string }) => `<h2>${params.loading}</h2>`)
res.end(
loadingTemplate({
loading: _error?.toString() || this._loadingMessage || 'Loading...',
}),
)
}
在这个地方可以清楚的看到这个地方调用了 devServer 的 loadingTemplate ,options 就是我们传递的 Nuxt 配置项,光看这个函数名称就很明显了,renderError 渲染错误?,我大概是明白了,原来 loadingTemplate 是为了 在 Nuxt 中渲染 错误 存在的呀!!,只是这个名称,真有点无语了。。
好了问题来了?怎么 触发错误了,一开始我看 里面有一个res ,那百分百是什么请求从而 引发的错误 呀,那会不会是我们在 Nuxt 中请求 的时候 出错了,这个模板就 生效了呢。
这里我尝试了两种,一种就是 在Nuxt 中 代码引发的错误,还有一种就是由Nuxt 的 拦截器 引发的 响应错误。
这是我的Nuxt 配置,其中配置了 loadingTemplate
export default defineNuxtConfig({
// 指定您的应用程序的兼容性日期。 这将影响 CSS 兼容性和 polyfills。
compatibilityDate: "2024-04-03",
// Nuxt 开发者 工具的配置项
devtools: { enabled: true },
build: {
// babel 转义
transpile: ["vuetify"],
},
modules: [
(_options, nuxt) => {
nuxt.hooks.hook("vite:extendConfig", (config) => {
// @ts-expect-error
config.plugins.push(vuetify({ autoImport: true }));
// fsfdsdfss
});
},
//...
],
devServer: {
loadingTemplate(data) {
console.log(data, 'loading template');
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
dev server error(服务错误) 22222
</body>
</html>`;
},
},
vite: {
vue: {
template: {
transformAssetUrls,
},
},
},
});
},
},
});
代码引发错误
这个比较简单,我们主要随便在 代码 里面乱写就完了。
对了 我还发现比较离谱的一点,就是 在 vue的 template 当中 你 用了 没有定义的变量,应用程序不会 出错了,但是TS 会 报警
但是页面是正常可以的
这里看到有一个警告,其实我感觉这种的话,直接报错会不会好一些呢
这样包报错的!
<template>
<div>
{{ data }}
</div>
</template>
<script setup lang="ts">
const data = await useFetch("/api/hello")
fdsfdsfs
</script>
这是 Nuxt 默认的 500 样式
当然我们的 目的是触发 503 从而看我们设置的 loadingTeamplate 生效了吗
这种 系统默认的程序错误 一般都是 500,我们怎么自定义我们的错误码呢,让Nuxt可以识别到。
这里就需要 用到 Nuxt 中提供的 创建错误的方法 createError()
createError 是nuxt 提供的 内置的工具函数,可以 在 服务端和 客户端 中抛出
在服务器端,它将触发一个全屏错误页面,您可以使用 clearError 清除该页面。
在客户端,它将引发一个非致命错误供您处理。如果需要触发全屏错误页面,则可以通过设置 fatal: true 来实现此目的。
这里我们就可以 使用 createError 来抛出 503错误,看看会不会 使用我们的 模板。
<template>
<div>
</div>
</template>
<script setup lang="ts">
throw createError({ statusCode: 503})
</script>
可以看到 并没有 生效我们的配置。
这里说一下 Nxut 这个 默认的错误 样式真的 不错,还有 404 也还挺好看的。
响应 引发 503
这里使用 middleware 对响应进行拦截,然后 返回状态码为 503
export default defineResponseMiddleware((event) => {
// 设置条件来控制是否返回 503
const isUnderMaintenance = true;
if (isUnderMaintenance) {
// 设置响应状态码为 503
event.node.res.statusCode = 503;
// 设置响应内容类型
event.node.res.setHeader('Content-Type', 'text/html');
// 设置字符集为中文
event.node.res.setHeader('charset', 'utf-8');
// 返回 503 错误内容
event.node.res.end(`<div class="loading">
<div class="loading__bar">loading.....</div>
</div>`);
}
});
发起请求,引发错误
<template>
<div>
{{ data }}
</div>
</template>
其实结果已经显而易见了,渲染的 东西肯定是我们返回的 错误内容呀,虽然这里肯定可以实现 loadingTemplate 的 作用,但是 我们 dev 的loadingTemplate 还是没有生效呀,真是离了个大普呀。\
不断的尝试,不断的试错
在 这两种方式都不信的时候 我真的就是欲哭无泪了,特别想放弃,反正用不到,但是 感觉又花了一些时间在这上面,不搞好就 感觉之前的时间不都全浪费了吗,算了,实在不行就看 Nxut 的源码吧,看一下 renderError 是在什么时候触发的。
好在我突然想到了,会不会 loadingTemplate 是跟dev 服务器相关的,也就是 说他捕获的是 服务在启动时的错误,而不是 说 服务在运行中所引发的错误,算了 都试了这么多次,也不缺这一次。
一开始的时候,我直接在 Nuxt.config.ts 中·直接 报错,结果可想而知,那么就是 启动都起不起来。所以应该是,Nuxt 请求 dev 服务器的时候,或者是 dev 服务肯定是运行起来了,产生的错误,这里我也是运行比较好,在 modules 里面 去触发错误,结果您猜怎么着,结果 成功了,真是 跌跌撞撞呀 。
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
// 指定您的应用程序的兼容性日期。 这将影响 CSS 兼容性和 polyfills。
compatibilityDate: "2024-04-03",
// Nuxt 开发者 工具的配置项
devtools: { enabled: true },
build: {
// babel 转义
transpile: ["vuetify"],
},
modules: [
(_options, nuxt) => {
nuxt.hooks.hook("vite:extendConfig", (config) => {
// @ts-expect-error
config.plugins.push(vuetify({ autoImport: true }));
// 这个地方 会 触发 程序错误。
fsfdsdfss
});
},
//...
],
devServer: {
loadingTemplate(data) {
console.log(data, 'loading template');
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
dev server error(服务错误) 22222
</body>
</html>`;
},
},
vite: {
vue: {
template: {
transformAssetUrls,
},
},
},
});
当前 改了之后需要重启 一下 Nuxt ,因为 热更新 只是 会 去 重新加载 模块,并不会 重新启动 dev 服务(也有可能是 nuxt 的缓存,把之前的响应结果缓存起来了。)
我靠,成功了,终于呀,虽然我也不知道有什么 用,但是 就感觉一个东西 从不知道 到知道(就算最后是错的),这种就很舒服。
然后这个 地方我们还可以 把 loadingTemplate 单独 抽离 出去,成为一个HTML文件,因为其本质就是 HTML 字符串嘛,那么 HTML 文件 读出来,返回去那不是一样的 渲染吗?
使用 fs 读取 HTML 文件内容
import fs from "fs";
const devServerError = fs.readFileSync("./public/dev-server-error.html");
// 转为string
const devString = devServerError.toString();
在 loadingTemplate 的时候 返回
loadingTemplate(data) {
console.log(data, 'loading template');
return devString;
},
最后 我 感觉 这个功能 有点鸡肋,可能都没什么 实质的用处,也有可能是我 了解的还不够,只是误打误撞,撞出来了,但是 经过这么一套下来,我感觉 我对于Nuxt 和 SSR 又理解了不少。