跳到主要内容

Nuxt.js 上手学习笔记(一):项目结构、自动路由与 API 搭建

·2 分钟

记录初次上手 Nuxt.js 的学习过程,包括项目目录结构、组件自动注册、server和api 路由约定以及开发中踩到的坑

Nuxt.js server/api 路由 Vue3 SSR

Nuxt.js (day1) 的学习 #

最近看到一个新技术就折腾一下,前面看到 qiankun,昨天刷 L 站看到了 Nuxt.js,这不就开始上手边看文档边写项目了。 目前就根据我自己起的项目来做个小总结。

1. 项目结构 #

目前跟普通的 Vue 的区别就是多了一个 server,专门用于放与后端 db、api 相关的内容,其他的没太多变化。

目录 用途
app.vue 根组件,所有页面的入口
pages/ 文件即路由,index.vue/about.vue/about
components/ 自动注册的 Vue 组件,无需 import
layouts/ 页面布局模板,如统一的 header/footer
composables/ 自动导入的组合式函数,封装可复用逻辑
plugins/ 应用启动时执行的插件,如注册第三方库
middleware/ 路由守卫,页面跳转前执行,如权限验证
server/ 服务端代码,server/api/ 创建后端接口
public/ 静态资源,直接通过 URL 访问,不经过构建
assets/ 需要构建处理的资源,如 CSS、图片(会被 Vite 处理)

2. 组件自动注册 #

Nuxt.js 中的组件创建后无需手动引入,直接使用即可。比如在 components/ 下新建了 example.vue,在 pages/ 的其他 vue 文件中直接 <example /> 就能用。

3. 外部依赖配置 #

外部依赖直接在 nuxt.config.ts 中配置即可,比如引入 element-plus

export default defineNuxtConfig({
  compatibilityDate: '2025-07-15',
  devtools: { enabled: false },
  css: ['~/assets/css/main.css'],
  modules: ['@element-plus/nuxt'],
  nitro: {
    experimental: {
      wasm: true
    },
    externals: {
      external: ['better-sqlite3']
    }
  },
  runtimeConfig: {
    jwtSecret: process.env.JWT_SECRET || 'dev-secret-change-in-production'
  }
})

4. server/api 路由约定 #

这个项目使用的是 SQLite,无需配置额外环境,简洁方便。 Nuxt 写接口有一个很特别的地方:接口路径与文件路径直接对应。比如新增接口放在 server/api/notes/index.post.ts,在 Vue 文件中调用就是:

const note = await $fetch('/api/notes', { method: 'POST', body: {} })

如果需要携带 id 参数,文件名写成 [id].delete.ts 即可。

路由映射规则 #

server/routes/server/api/ 都是 Nuxt/Nitro 的特殊目录,框架自动处理路由映射,目录名本身不会出现在 URL 里

类比 pages/ 目录:

pages/about.vue  → /about  (不是 /pages/about)
pages/user.vue   → /user   (不是 /pages/user)

同理:

server/api/notes.get.ts    → /api/notes  (api 是约定前缀)
server/routes/notes.get.ts → /notes      (routes 不加前缀)

这是 Nitro 框架的约定:api/ 自动加 /api 前缀,routes/ 直接映射,两个目录名都不会出现在 URL 里。

自定义目录名 #

如果想自定义目录名,需要在 nuxt.config.ts 中通过 nitro.scanDirs 配置,但这样做比较麻烦,需要手动定义每个路由。所以一般直接用 api/routes/ 即可。

// nuxt.config.ts
nitro: {
  scanDirs: ['server/fast']
}
// server/fast/notes.get.ts 的 URL 还是 /notes,不会带 /fast 前缀
// 如果想要 /fast/notes,需要在文件里手动定义路由:
export default defineEventHandler({
  route: '/fast/notes',
  handler: (event) => {
    return { data: 'hello' }
  }
})

踩坑记录 #

开发时遇到一个奇怪的报错:

Error: IPC connection closed
 ⁃ at Socket.onClose (node_modules/.pnpm/@nuxt+vite-builder@4.4.2_.../vite-node.mjs:140:101)

因为确实没有找到错误的头绪,于是询问 GPT 后发现是某个 Vue 文件的语法问题:<script setup> 没有加 lang="ts",导致 catch (e: any) 这种 TypeScript 写法无法被 Vue 编译器识别。没有 lang="ts" 就不能在 script 中使用 TypeScript 语法,需要注意。

最后 day1 小结 #

目前学习进度到路由和简单 API 的搭建,后续会继续基于当前项目做进一步学习。