返回文章索引
TRANSMISSION / MINT6 MIN READ

Nuxt 4 的架构减法

一个内容型站点不需要复杂前端状态。让路由、服务端渲染和内容查询各自回到最擅长的位置。

#Nuxt#Vue#架构
复杂模块逐步收敛为清晰精简的 Nuxt 应用核心
用架构减法换取清晰边界

很多 Nuxt 项目变复杂,并不是因为业务真的复杂,而是因为我们太早把所有可能性都放进了架构。

个人博客是一个很好的校准器:页面数量有限、内容结构稳定、交互集中在少数区域。它提醒我们,框架最有价值的部分往往不是增加能力,而是删除不必要的选择

从默认 SSR 开始

技术文章需要被搜索引擎理解,也需要在弱网下尽早出现正文。Nuxt 默认的服务端渲染刚好符合这个目标。

export default defineNuxtConfig({
  modules: ['@nuxt/content'],
  nitro: {
    preset: 'cloudflare',
  },
})

这里没有把整个站改成 SPA,也没有为每个页面单独发明数据加载方式。页面在服务端完成第一次查询和渲染,客户端接管后继续使用同一套路由。

如果某一小块必须依赖浏览器 API,再局部使用 ClientOnly。不要为了一个客户端组件牺牲整页的语义和首屏。

内容查询留在页面边界

首页、文章列表和文章详情需要的数据粒度不同。查询应该贴近消费它的页面:

const { data: posts } = await useAsyncData('latest-posts', () => {
  return queryCollection('posts')
    .where('draft', '=', false)
    .order('publishedAt', 'DESC')
    .limit(6)
    .all()
})

这样做有几个直接好处:

  • 数据参与 SSR,不需要 onMounted 后再闪烁加载;
  • 查询键明确,Nuxt 可以在 hydration 时复用 payload;
  • 页面知道自己需要什么,不必经过一层通用 store;
  • 草稿过滤与排序规则出现在真正相关的位置。

只有当多个页面共享了同一段复杂业务状态时,才值得抽成 composable 或 store。

组件只抽取稳定重复

“看起来可能复用”不是组件边界。真正稳定的重复通常同时包含结构、语义和行为。

这个博客里,文章卡片是合适的组件:它在首页和索引页重复出现,拥有固定的元数据、标题、摘要、标签和跳转行为。

相反,首页 Hero 即使很大,也未必需要拆成五个小组件。它只在一个页面出现,内部元素共同完成一段视觉叙事。过早拆分只会让样式关系跨文件跳转。

组件不是文件整理工具,而是可独立理解和复用的 UI 契约。

服务端 API 保持窄接口

登录与留言属于运行时数据,放在 server/api。每个 endpoint 只做一件事:

POST /api/auth/register
POST /api/auth/login
POST /api/auth/logout
GET  /api/auth/session
GET  /api/comments/:post
POST /api/comments/:post

窄接口使授权规则更容易检查。读取留言是公开的,创建留言需要会话;退出只销毁当前令牌,不需要触碰用户资料。

如果一个 endpoint 同时处理读取、写入、登录和格式转换,它就已经失去了清晰边界。

把状态放回正确的位置

Vue 的响应式状态很方便,但不是所有状态都应该进入 JavaScript:

状态最合适的位置
当前文章路由
搜索词页面 ref
标签筛选URL query + 页面状态
登录用户SSR 安全的 useState
文章正文Nuxt Content
会话真实性服务端 D1

当状态位置正确时,大量“同步逻辑”会自然消失。我们不再需要监听路由去修复 store,也不需要用 localStorage 假装登录状态可靠。

减法不是功能更少

好的架构减法不会让用户少得到什么。相反,它把复杂度预算留给真正有感知的部分:

  • 更清晰的排版;
  • 更快的首屏;
  • 更可靠的登录;
  • 更舒服的移动端阅读;
  • 更容易坚持的 Markdown 写作流程。

Nuxt 4 提供了足够多的能力。项目成熟的标志,不是把它们全部用上,而是知道哪些能力可以暂时不用。

END OF SIGNAL
READER CHANNEL

留言

00
还没有留言。成为第一个发出回应的人。