Nuxt4阶段六:工程化与进阶 —— 模块、中间件、插件、TS 与测试

Nuxt4阶段六:工程化与进阶 ------ 模块、中间件、插件、TS 与测试

学习目标 :会用 Nuxt Modules、路由中间件、插件;理解 Nuxt 4 TypeScript 分层;能写基础测试与统一错误处理。

预计时间 :1--2 周

前置 :完成阶段一至五。

本阶段产出:带登录守卫的后台、一个实用插件、基础测试用例。


1. Nuxt Modules:扩展能力的官方方式

模块能改构建、加目录约定、注册组件与 composables。

1.1 安装与注册

bash 复制代码
pnpm add @nuxt/ui
# 或 Tailwind:pnpm add @nuxtjs/tailwindcss
pnpm add @vueuse/nuxt
pnpm add @nuxtjs/color-mode
ts 复制代码
// nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    '@nuxt/ui',
    '@vueuse/nuxt',
    '@nuxtjs/color-mode',
  ],
  colorMode: {
    classSuffix: '',
  },
})

浏览生态:https://nuxt.com/modules

1.2 初学者建议组合

模块 用途
@nuxt/ui 或 Tailwind UI / 样式
@vueuse/nuxt 超全的 composables
@nuxtjs/color-mode 亮暗色
@pinia/nuxt 状态(阶段三)
@nuxt/image 图片(阶段五)
@nuxt/test-utils 测试
@nuxtjs/i18n 国际化(阶段八可用)

不必一次装全,按需添加。


2. 路由中间件(页面级鉴权)

放在 app/middleware/

2.1 匿名中间件(局部使用)

app/middleware/auth.ts

ts 复制代码
export default defineNuxtRouteMiddleware(async (to) => {
  const { data: user } = await useFetch('/api/auth/me', {
    key: 'auth-me',
    // 避免重复导航时刷屏错误,可按需调整
  })

  if (!user.value) {
    return navigateTo({
      path: '/login',
      query: { redirect: to.fullPath },
    })
  }
})

页面启用:

ts 复制代码
definePageMeta({
  middleware: 'auth',
  layout: 'admin',
})

2.2 全局中间件

文件名加 .global.ts,例如 app/middleware/analytics.global.ts

ts 复制代码
export default defineNuxtRouteMiddleware((to) => {
  if (import.meta.client) {
    console.log('[nav]', to.fullPath)
  }
})

每个路由都会跑------保持逻辑轻量。

2.3 中间件执行顺序

  1. 全局中间件(文件名排序)
  2. 布局/页面定义的中间件
  3. 页面渲染

2.4 登录页示例

app/pages/login.vue

html 复制代码
<script setup lang="ts">
definePageMeta({ layout: false })

const route = useRoute()
const username = ref('admin')
const password = ref('123456')
const errorMsg = ref('')

async function onSubmit() {
  errorMsg.value = ''
  try {
    await $fetch('/api/auth/login', {
      method: 'POST',
      body: { username: username.value, password: password.value },
    })
    await navigateTo((route.query.redirect as string) || '/admin')
  } catch (e: any) {
    errorMsg.value = e?.data?.message || '登录失败'
  }
}
</script>

<template>
  <form @submit.prevent="onSubmit">
    <h1>登录</h1>
    <input v-model="username" placeholder="用户名" />
    <input v-model="password" type="password" placeholder="密码" />
    <p v-if="errorMsg">{{ errorMsg }}</p>
    <button type="submit">登录</button>
  </form>
</template>

/adminmiddleware: 'auth',未登录会被踢回 /login


3. 插件 Plugins

用于初始化第三方库、注入全局方法。

3.1 客户端插件

app/plugins/hello.client.ts(仅浏览器):

ts 复制代码
export default defineNuxtPlugin(() => {
  return {
    provide: {
      hello: (name: string) => `Hello, ${name}!`,
    },
  }
})

使用:

ts 复制代码
const { $hello } = useNuxtApp()
console.log($hello('Nuxt'))

3.2 服务端插件

app/plugins/server-init.server.ts:仅 SSR/服务端运行。

3.3 通用插件

.client / .server 后缀则两端都跑------注意不要碰 window

3.4 Vue 插件示例(假想)

ts 复制代码
export default defineNuxtPlugin((nuxtApp) => {
  // nuxtApp.vueApp.use(SomeVuePlugin)
})

排序可用文件名数字前缀:01.foo.ts02.bar.ts


4. TypeScript 与 Nuxt 4 项目分离

Nuxt 4 为不同上下文准备了更清晰的类型边界:

上下文 大致范围
app 页面、组件、composables、插件
server server/**
shared shared/**
node / builder 配置与构建相关

好处:不会在前端代码里「误用」仅服务端 API 还不报错(或反过来)。

实践建议:

  1. 公共类型放 shared/shared/types
  2. runtimeConfig 补类型(可通过配置自动推导,或扩展接口)
  3. 打开严格模式,逐步修类型错误

实验性 Typed Routes :让 navigateTo('/blog/1') 等有路径提示。可在文档中开启 experimental.typedPages(以当前版本文档为准)。


5. 错误处理工程化

5.1 全局 error.vue

见阶段五;确保存在并样式可读。

5.2 组件内

ts 复制代码
const { error } = await useFetch('/api/posts')

5.3 主动抛错

ts 复制代码
throw createError({ statusCode: 403, statusMessage: 'Forbidden' })

5.4 钩子(了解)

ts 复制代码
// plugins
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.hook('vue:error', (err) => {
    console.error('vue:error', err)
  })
})

6. 测试入门

bash 复制代码
pnpm add -D @nuxt/test-utils vitest @vue/test-utils happy-dom

nuxt.config.ts 可按 @nuxt/test-utils 文档添加配置。示例 vitest.config.ts

ts 复制代码
import { defineVitestConfig } from '@nuxt/test-utils/config'

export default defineVitestConfig({
  test: {
    environment: 'nuxt',
  },
})

组件测试示例:

ts 复制代码
// tests/PostCard.spec.ts
import { describe, it, expect } from 'vitest'
import { mountSuspended } from '@nuxt/test-utils/runtime'
import BlogPostCard from '~/components/blog/PostCard.vue'

describe('BlogPostCard', () => {
  it('renders title', async () => {
    const wrapper = await mountSuspended(BlogPostCard, {
      props: { id: '1', title: 'Hello', excerpt: 'World' },
    })
    expect(wrapper.text()).toContain('Hello')
  })
})

API 测试可对 handler 做单元测试,或用 $fetchsetup 后的测试服务器(见官方 Testing 指南)。

package.json

json 复制代码
{
  "scripts": {
    "test": "vitest run",
    "test:watch": "vitest"
  }
}

7. 代码组织最佳实践(中型项目)

text 复制代码
app/
  components/
    blog/          # 按领域分
    ui/            # 通用 UI
  composables/
    useAuth.ts
    useApi.ts
  middleware/
  pages/
  layouts/
  plugins/
server/
  api/
    auth/
    posts/
    todos/
  utils/
  middleware/
shared/
  types/
  utils/           # 纯函数、无 Node/浏览器依赖

原则:

  • 页面保持瘦:数据获取 + 拼装组件
  • 可复用逻辑进 composables
  • 可复用 UI 进 components
  • 服务端 IO 不进 composables(除非包 $fetch

8. 环境与质量工具(可选但推荐)

bash 复制代码
pnpm add -D eslint @nuxt/eslint
# 按模块文档初始化

Prettier、Husky、lint-staged 可按团队习惯加。学习阶段有 ESLint 即可。


9. 暗色模式小练习

若装了 @nuxtjs/color-mode

html 复制代码
<script setup lang="ts">
const colorMode = useColorMode()
</script>

<template>
  <button @click="colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'">
    当前:{{ colorMode.value }}
  </button>
</template>

CSS:

css 复制代码
:root { color-scheme: light; }
.dark {
  color-scheme: dark;
  background: #111;
  color: #eee;
}

10. 本阶段验收清单

  1. 至少安装并配置 2 个 Nuxt 模块
  2. auth 中间件保护 /admin,未登录跳转 /login
  3. 写过一个 .client 插件并 provide 方法
  4. 存在友好的 error.vue
  5. (加分)Vitest + 至少一个组件测试通过
  6. (加分)shared/types 被 app 与 server 同时引用
  7. (加分)接入 color-mode

11. 常见坑

中间件里错误使用 useFetch 导致循环

登录页不要挂 auth 中间件;失败时注意别重定向到自己。

插件里直接碰 window

.client 后缀或 import.meta.client 判断。

测试环境与运行时不一致

优先用官方 @nuxt/test-utilsenvironment: 'nuxt'

全局中间件太重

每个导航都跑,别在里面狂打 API。


12. 阶段总结

你已经掌握:

  • Modules 扩展方式
  • 路由中间件鉴权流
  • 插件注入与客户端/服务端拆分
  • TS 分层意识、测试与错误页

下一篇:阶段七 ------ 构建、环境变量、Vercel/Docker/Node 部署与简易 CI。


参考链接

相关推荐
jieyucx2 小时前
Nuxt4阶段五:渲染模式与性能优化
性能优化·vue·nuxt
csdn2015_10 小时前
nodejs安装
node.js·vue
前端双越老师1 天前
如何以前端视角(非0基础)学 Java ?
java·node.js·全栈
杨充1 天前
6.设计原则的全景图
设计模式·开源·全栈
杨充1 天前
7.SOLID原则案例汇
设计模式·开源·全栈
Kel1 天前
Node.js 没那么复杂
人工智能·node.js·全栈
索西引擎2 天前
【React】Redux 中间件机制:副作用处理与数据流增强的形式化分析
前端·react.js·中间件
糖果店的幽灵2 天前
【DeepAgents 从入门到精通】Context Management 上下文管理
java·人工智能·后端·spring·中间件·langgraph·deepagents