事情要从上周改叨叨那条”开始折腾 Nuxt”的时候说起。本以为写完那条就能慢慢把博客搬过去,结果一脚踩进去,到现在还没爬出来。

Vue 不熟这件事

首先要承认,我对 Vue 的接触大概停留在两年前写过几个小 demo 的水平。refreactivecomputed 这些概念知道,但 Nuxt 那一套组合式 API + 自动导入 + 文件路由叠起来,每次写都要回头查文档。

最难受的是写法相似但语义不同。比如 defineProps 看着像 Astro.props,但响应式是默认开启的,于是我在父组件里改了 prop,子组件死活不更新,debug 了半小时才发现是要用 computed 包一层。

样式一迁移就崩

Astro 的 <style> 是 scoped 的,类名会加 hash;Nuxt 3 的 <style scoped> 行为类似,但默认注入的顺序和层叠规则不一样

于是我看到的现象是:

  • 同样的类名,在 Astro 里层级正确,到 Nuxt 里被全局 CSS 覆盖
  • 字号、间距看起来”差不多”,但排版明显松紧不一致
  • 媒体查询断点对了,可视宽度却错位几像素

更离谱的是毛玻璃(backdrop-filter),在 Astro 里一切正常,到 Nuxt 里要加 position: relative 和一个空 z-index 层才肯渲染——查了 issue 才确认是 SSR 注水顺序的锅。

400 / 500 错误连番来

getCollection 换成 Nuxt Content 之后,第一次跑 pnpm dev 报了一个 500,错误信息只有一句 Cannot read properties of undefined。翻了两小时日志,发现是 defineCollection 里漏了 source: 'log',content 根本没被注册。

紧接着是 400。改完路径之后 useAsyncData 拉到的永远是 null,但接口本身在;最后定位到是 queryContentwhere 条件字段名拼错——pubDate 写成 pubdate,由于 schema 没显式声明,没在校验阶段暴露,直接带病上线。

再后来是部署到 Cloudflare Pages 时,server bundle 体积超限,构建 500。这个我承认是图省事把整站打成了一个函数。

慢慢摸到的几个小经验

记下来给以后同样冲动的自己:

  • 迁移前先把现有站点的 设计 token(颜色、字号、间距)抽成 CSS 变量,能省一半返工
  • 路由别一下子全切,先搬一个最简单的页面打通 pages/ + layouts/ + app.vue 三件套
  • useHeaduseSeoMeta 二选一,别混用,搜索引擎那边抓到的 og 标签会重复
  • 出错先看 /.nuxt/types 生成的类型文件,比看 dev server 日志直观
  • defineProps 时多加 withDefaults,默认值在 SSR 注水阶段能救命

说真的,比想象中恶心多了

写完上面那段经验时,以为事情差不多摸到门了,结果这两天又开始反复”撞墙”。越用越觉得,Nuxt 不是一个”写起来爽”的框架,而是一个要求你时刻保持敬畏的框架。

最让人崩溃的,是它那一套”看起来省心、实际上埋雷”的自动导入。

  • .vue 文件里随手写一个 useFoo()没人告诉你它从哪来
  • 想跳转到定义?不好意思,这个函数是构建时合成的,编辑器只能给你一个空泛的 unknown
  • 团队来了新人,排查 bug 时第一句话总是:“这个 useXxx 哪儿的?”

更绝的是各种同名 API

  • $fetch / useFetch / useAsyncData / useFetch 的 server 版……四个 API 长得像,用法微妙地不一样,记错一个就 SSR 报 Cannot read properties of undefined
  • useState 是全局的,但只在客户端持久化;想跨请求共享,请再引入 Pinia
  • definePageMeta 里能写 middleware、能写 layout、能写 validate、还能写 keepalive——字段多了之后没人记得每个键是干嘛的

还有 hydration 不匹配。生产环境第一次构建跑得好好的,改了一行 prop 之后页面”抖”了一下,控制台报了一句 Hydration node mismatch,错误位置指向第 47 行的某个 div。点过去一看,那是个空 div,两边的内容根本是一致的——只是 SSR 渲染顺序和 CSR 渲染顺序差了几毫秒。

最让人想骂街的是 dev server 的缓存

  • 改了 nuxt.config.ts 里一个 plugin 的顺序,刷新页面没反应
  • 改了 app.vue 里的全局样式,刷新页面还是旧的
  • pnpm dev 重启一次,三十秒过去了,你盯着黑屏发呆

写 Astro 时从来没这种感觉——Astro 的 dev server 启动快、改了就变、报错的位置很准。Nuxt 不是不行,是它把”约定”做得很厚,厚度上来之后,bug 也跟着厚

暂时停一停

这周末决定先回到 Astro 继续更叨叨和随笔,Nuxt 的分支留着慢慢迭代。

不为别的——只是想让自己写得动。折腾工具的快乐不该盖过写东西本身。留个尾巴,等哪天摸熟了,再回来交一份完整的迁移笔记。

评论系统开发中,敬请期待