Nuxt 3 开发避坑¶
开发个人主页时用了 Nuxt 3。虽然有些坑,但它还是很好用的。文档:https://nuxt.com/docs/getting-started/introduction。
解决初始化报错¶
执行初始化命令后
npx nuxi@latest init <project-name>
显示下面的报错。
Error: Failed to download template from registry: Failed to download https://raw.githubusercontent.com/nuxt/starter/templates/templates/v3.json: TypeError: fetch failed
在 GitHub 上找到了相关的 Issues:support http proxy when using nuxi init
with node >= 18。其中有人总结了原因:
The main issue is native fetch API provided by newer Node.js versions (18+), does not support HTTP agents for proxy support.
简单概括就是它不走代理。目前有 4 种解决方法:
- 改 hosts 文件。这个我不喜欢,跳过。
-
换源。有人把相关的文件 clone 到了 gitee 上,设置环境变量
set NUXI_INIT_REGISTRY="https://gitee.com/hzgotb/nuxt-starter/raw/templates/templates"
之后再初始化就可以了。
-
官方目前提供了一个支持代理的版本,但是 nightly 版,还不是正式版。
npx nuxi-nightly@latest init <project-name>
-
使用 Clash 的 TUN 模式,不用系统代理。
Font Awesome¶
配置¶
按它的文档做就行:https://docs.fontawesome.com/web/use-with/vue/use-with#nuxt。建议把 Integrations/Vue 里的文档全看一下。
需要先装两个核心的包。
npm i --save @fortawesome/fontawesome-svg-core
npm i --save @fortawesome/vue-fontawesome@latest-3
常用的免费 Icon Packages,可以全部安装。
npm i --save @fortawesome/free-solid-svg-icons
npm i --save @fortawesome/free-regular-svg-icons
npm i --save @fortawesome/free-brands-svg-icons
在写它的 plugin 文件时要注意
// For Nuxt 3
import { library, config } from '@fortawesome/fontawesome-svg-core'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import { fas } from '@fortawesome/free-solid-svg-icons'
// This is important, we are going to let Nuxt worry about the CSS
config.autoAddCss = false
// You can add your icons directly in this plugin. See other examples for how you
// can add other styles or just individual icons.
library.add(fas)
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.component('font-awesome-icon', FontAwesomeIcon, {})
})
// Modify the `nuxt.config.ts` file by adding to the `export default defineNuxtConfig()`
export default defineNuxtConfig({
css: [
'@fortawesome/fontawesome-svg-core/styles.css'
]
})
上面一半代码是写在 plugins/fontawesome.js
里的。下面 export default defineNuxtConfig({...})
里的代码是加在 nuxt.config.ts
里的。
另外不建议像上面一样,直接把一个 IconPack
加进 library
里。最好是只加需要用到的 IconDefinition
,这样能显著减小 build 以后的 js 文件大小。
Hydration Mismatch¶
相关的 Issues:Nuxt 3: Hydration mismatch when using SSR。开 SSR 时,浏览器控制台会警告 Hydration Mismatch,命令行会提示 Could not find one or more icon(s)
。
SSR 还是建议开的,参考:https://nuxt.com/docs/getting-started/deployment#static-hosting。
最简单粗暴的解决方式是用 <ClientOnly>
标签把 <font-awesome-icon>
包起来,这样服务端就不渲染了,自然就没有 Mismatch 了。(
另一种更好的解决方法来自 Discussions:Using FontAwesome in Nuxt 3。虽然他们讨论的是另一个问题,但也能解决我这里的问题。根据 Answer 里的第一条回复,把引入的所有 Font Awesome 包都写进 nuxt.config.ts
的 build.transpile
中。
export default defineNuxtConfig({
build: {
transpile: [
'@fortawesome/vue-fontawesome',
'@fortawesome/fontawesome-svg-core',
'@fortawesome/free-brands-svg-icons',
'@fortawesome/free-regular-svg-icons',
'@fortawesome/free-solid-svg-icons',
]
}
})
If you're curious, like I was, about why we need to transpile @fortawesome/vue-fontawesome in order to avoid this SSR hydration mismatch, it's because the module field in the package.json file from @fortawesome/vue-fontawesome specifies index.es.js as the entry point (link) but package.json doesn't specify
"type": "module"
so Node treats this file as a CommonJS module. But index.es.js actually uses ESM syntax, so Node fails to load it and thus the<FontAwesomeIcon>
component fails to render on the server since it's not defined.The Nuxt documentation does an excellent job documenting this problem:
https://nuxt.com/docs/guide/concepts/esm#what-kinds-of-problems-can-there-be
So until @fortawesome/vue-fontawesome is updated to correct this issue, simply configure Nuxt to transpile @fortawesome/vue-fontawesome:
1build: { transpile: [ '@fortawesome/vue-fontawesome', ], },
设置页面 head 信息¶
文档 https://nuxt.com/docs/getting-started/seo-meta#usehead 里推荐用 usehead
来实现。它是 Unhead 提供的,现在已经被 Nuxt 内置了,直接用就行。
AppConfig¶
useAppConfig()
的代码提示¶
文档 https://nuxt.com/docs/guide/directory-structure/app-config#typing-app-config 中提到 Nuxt 会自动生成 app.config.ts
的类型信息。
实际使用时,以 VSCode 为例,需要重启编辑器才能生效。
AppConfig 的限制¶
useAppConfig()
返回的是配置对象的 Reactive Proxy,所以和 Vue 的 reactive()
有一样的限制。具体可以参考 Vue 的文档 reactive()
的局限性。
纯静态部署到 Vercel¶
把 Nuxt 3 项目部署到 Vercel 时,默认是部署了一个叫 __nitro
的 Serverless Function。用户访问时,__nitro
就会被调用,然后返回对应的页面。
实际用了几天后,我发现每隔一段时间访问网站时,请求的耗时会显著增加。背后的原因,大概是每过一段时间 __nitro
就会把 cache 给失效掉,然后重新渲染整个页面,所以耗时比较长。对于我的没什么功能的小网站来说,这些步骤完全是多余的,还给用户带来了负面的体验,所以我决定直接静态化部署网站。
Vercel 的文档 中提到了用 nuxt generate
和 nuxt build
(默认)部署时的不同做法,但是后面一种我自己试的时候失败了,所以就用了前一种。流程很简单:
-
修改
nuxt.config.ts
export default defineNuxtConfig({ nitro: { static: true, }, });
-
在 Vercel 上去项目 Settings 面板重写
Build Command
为npm run generate
(或nuxt generate
)