引言

恭喜你,已经深入理解了 Nuxt.js SSR 的核心数据获取与 SEO 优化!现在,是时候将你的技能提升到一个新的层次了。在构建复杂、功能丰富的现代 Web 应用时,仅仅掌握基础的页面渲染和数据获取是远远不够的。

本篇博客将带你探索 Nuxt.js 的进阶领域。我们将学习如何在应用的不同部分之间高效地共享和管理状态,如何利用 Nuxt.js 强大的服务器端能力构建自己的 API 路由,以及如何运用中间件、插件和模块等高级功能来扩展和定制你的应用。准备好迎接挑战,让你的 Nuxt.js 应用更加强大和灵活吧!

完整项目地址: manual-nuxt-app (vip)


1. 状态管理:useState 与 Pinia

在复杂的单页应用中,组件之间经常需要共享数据(如用户登录状态、购物车内容、应用主题等)。Nuxt.js 提供了两种主要的状态管理方案。

1.1 useState:Nuxt.js 内置的轻量级状态

useState 是 Nuxt.js 提供的一个简单而强大的组合式函数,用于创建跨组件共享的响应式状态

  • 特点

    • 轻量级:无需安装额外库,开箱即用。
    • SSR 友好:状态可以在服务端创建,并随 HTML 一起发送到客户端,实现状态的“水合 (Hydration)”。
    • 基于键名 (Key):每个状态通过一个唯一的字符串键名来标识。这是 useState 的核心机制。相同键名的状态在应用的不同部分会被视为同一个状态实例。
    • 作用域:默认是全局的。但可以通过在 composables/ 目录下创建自定义组合式函数来封装和组织。
  • 如何使用

    • 在任何组件或组合式函数中调用 useState(key, () => initialValue)
    • 返回一个 Ref,可以像普通响应式数据一样读写。
  • 示例:管理用户登录状态

<!-- composables/useAuth.js -->
// 将状态逻辑封装在组合式函数中,便于复用
export const useAuth = () => {
  // 使用键名 'user' 创建状态
  const user = useState('user', () => null)
  const isLoggedIn = computed(() => !!user.value)

  // 登录方法
  const login = async (credentials) => {
    try {
      // 假设有一个登录 API
      const userData = await $fetch('/api/login', {
        method: 'POST',
        body: credentials
      })
      user.value = userData // 更新状态
    } catch (error) {
      console.error('Login failed:', error)
    }
  }

  // 登出方法
  const logout = () => {
    user.value = null
  }

  return { user, isLoggedIn, login, logout }
}
<!-- components/LoginForm.vue -->
<script setup>
import { useAuth } from '~/composables/useAuth'

const { login } = useAuth()
const form = reactive({ email: '', password: '' })

const handleSubmit = async () => {
  await login(form)
  // 登录成功后,其他使用 useAuth() 的组件会立即感知到 user 状态的变化
}
</script>
<!-- components/UserProfile.vue -->
<script setup>
import { useAuth } from '~/composables/useAuth'

const { user, isLoggedIn, logout } = useAuth()
// user 状态会自动与 LoginForm 中的状态同步
</script>

注意useState 适合管理应用级的、相对简单的共享状态。对于非常复杂的状态逻辑,Pinia 仍然是更强大的选择。

1.2 Pinia:官方推荐的完整状态管理库

Pinia 是 Vue 3 的官方推荐状态管理库,在 Nuxt.js 中集成得非常好。

  • 特点

    • 完整的状态管理:提供 stategetters (计算属性), actions (方法) 的清晰结构。
    • TypeScript 友好:类型推断极佳。
    • 模块化:可以创建多个独立的 store (如 userStorecartStore)。
    • Devtools 支持:在浏览器开发工具中提供强大的调试功能(状态快照、时间旅行等)。
  • 在 Nuxt.js 中使用 Pinia

    1. 安装模块:Nuxt.js 通过模块 (modules) 来集成 Pinia。
      npm install @pinia/nuxt
    2. 配置 nuxt.config.ts
      export default defineNuxtConfig({
        modules: [
          '@pinia/nuxt',
        ],
        // 可选:配置 Pinia
        pinia: {
          autoImports: ['defineStore', 'acceptHMRUpdate'],
        },
      })
    3. 创建 Store:在 stores/ 目录下创建 .ts 文件。
      // stores/user.ts
      import { defineStore } from 'pinia'
      
      export const useUserStore = defineStore('user', {
        state: () => ({
          user: null,
          token: null,
        }),
        getters: {
          isLoggedIn: (state) => !!state.user,
        },
        actions: {
          async login(credentials) {
            try {
              const data = await $fetch('/api/login', {
                method: 'POST',
                body: credentials
              })
              this.user = data.user
              this.token = data.token
              // 通常会将 token 存入 cookie 或 localStorage
            } catch (error) {
              throw error
            }
          },
          logout() {
            this.user = null
            this.token = null
          }
        }
      })
    4. 在组件中使用
      <script setup>
      import { useUserStore } from '~/stores/user'
      
      const userStore = useUserStore()
      
      const handleLogin = async () => {
        await userStore.login({ email: '...', password: '...' })
      }
      </script>

何时使用 useState vs Pinia

  • 使用 useState:简单的共享状态(如主题、模态框开关)、快速原型开发。
  • 使用 Pinia:复杂的状态逻辑、需要 actions 进行异步操作、需要 getters 进行复杂计算、需要 Devtools 调试、大型应用。

2. 构建 API:server/api/ 目录

Nuxt.js 不仅仅是一个前端框架!它的服务器端能力允许你直接在项目中编写后端 API 路由,实现真正的“全栈”开发。这得益于其底层的 Nitro 引擎。

  • server/ 目录:这是存放服务器端代码的地方。

  • server/api/ 目录:存放 API 路由处理函数。文件结构决定路由。

    • server/api/hello.ts -> GET /api/hello
    • server/api/users/[id].ts -> GET /api/users/123PUT /api/users/123 等。
    • server/api/auth/login.post.ts -> 专门处理 POST /api/auth/login 请求。
  • 创建 API 路由: 每个文件导出一个处理函数 (Handler)。这个函数接收 event 对象(包含请求信息)并返回响应。

  • 示例:创建一个简单的用户 API

// server/api/users/[id].ts
import { defineEventHandler } from 'h3' // H3 是 Nitro 的 HTTP 工具库

// 模拟数据库
const users = [
  { id: 1, name: 'Alice', email: 'alice@example.com' },
  { id: 2, name: 'Bob', email: 'bob@example.com' }
]

export default defineEventHandler((event) => {
  // 从 URL 参数中获取 id
  const { id } = event.context.params

  // 处理 GET 请求
  if (event.method === 'GET') {
    const user = users.find(u => u.id === parseInt(id))
    if (user) {
      return { user } // 返回 JSON 响应
    } else {
      // 设置状态码并返回错误
      setResponseStatus(event, 404)
      return { error: 'User not found' }
    }
  }

  // 处理 PUT 请求 (更新)
  if (event.method === 'PUT') {
    const body = await readBody(event) // 读取请求体
    const userIndex = users.findIndex(u => u.id === parseInt(id))
    if (userIndex !== -1) {
      users[userIndex] = { ...users[userIndex], ...body }
      return { user: users[userIndex] }
    } else {
      setResponseStatus(event, 404)
      return { error: 'User not found' }
    }
  }

  // 处理其他方法
  setResponseStatus(event, 405)
  return { error: 'Method Not Allowed' }
})
  • 在前端调用
    <!-- pages/user/[id].vue -->
    <script setup>
    const route = useRoute()
    const { data: user } = await useFetch(`/api/users/${route.params.id}`)
    </script>

优势

  • 一体化开发:前后端代码在同一项目中,便于维护和部署。
  • 无缝集成:可以直接访问 Nuxt.js 的配置、模块和共享的类型定义。
  • Serverless 友好:Nitro 生成的构建产物可以轻松部署到 Vercel、Netlify、AWS Lambda 等 Serverless 平台。

3. 扩展功能:中间件、插件与模块

Nuxt.js 提供了强大的扩展机制,让你可以深入定制应用的行为。

3.1 中间件 (Middleware)

中间件允许你在路由导航过程中执行逻辑。它在页面渲染之前运行。

  • 类型

    • 全局中间件:在 middleware/ 目录下创建的 .ts 文件,会自动应用于所有路由。
    • 命名中间件:在 middleware/ 目录下创建的文件(如 auth.ts),可以在页面中通过 definePageMeta 显式调用。
    • 内联中间件:直接在页面组件的 <script setup> 中定义。
  • 用途

    • 身份验证和授权:检查用户是否登录,是否有权限访问某个页面。
    • 重定向:根据条件重定向用户(如未登录用户跳转到登录页)。
    • 数据预取:在进入页面前获取一些通用数据。
    • 日志记录:记录路由访问。
  • 示例:创建一个认证中间件

// middleware/auth.ts
export default defineNuxtRouteMiddleware((to, from) => {
  // 获取用户状态 (假设使用了 useState 或 Pinia)
  const user = useState('user').value

  // 如果目标页面需要认证 (通过 meta 标记)
  if (to.meta.requiresAuth && !user) {
    // 重定向到登录页
    return navigateTo('/login')
  }

  // 如果用户已登录,但访问的是登录页,重定向到首页
  if (to.path === '/login' && user) {
    return navigateTo('/')
  }

  // 允许导航
  // return true 或什么都不返回
})
<!-- pages/dashboard.vue -->
<script setup>
// 应用命名中间件 'auth'
definePageMeta({
  middleware: 'auth',
  // 或者使用数组应用多个中间件
  // middleware: ['auth', 'some-other-middleware']
  requiresAuth: true // 可以传递自定义元信息
})
</script>
3.2 插件 (Plugins)

插件允许你在应用初始化时扩展或修改 Vue 应用实例、Nuxt 实例或添加全局可用的功能。

  • 位置plugins/ 目录。

  • 执行时机:在 Vue 应用创建之后,页面渲染之前执行。

  • 用途

    • 注册全局组件、指令、混入 (mixin)。
    • 添加全局方法或属性到 app.config.globalProperties (Vue 2 风格) 或通过 provide/inject (Vue 3 推荐)。
    • 初始化第三方库(如 Vue Router, Pinia, UI 库)。
    • 添加全局错误处理器。
  • 示例:注册一个全局组件插件

// plugins/global-components.ts
import { defineNuxtPlugin } from '#app'
import MyButton from '~/components/MyButton.vue'
import MyModal from '~/components/MyModal.vue'

export default defineNuxtPlugin((nuxtApp) => {
  // 使用 nuxtApp.vueApp 访问 Vue 应用实例
  nuxtApp.vueApp.component('MyButton', MyButton)
  nuxtApp.vueApp.component('MyModal', MyModal)
  // 现在可以在任何地方使用 <MyButton /> 和 <MyModal />
})
  • 示例:添加一个全局方法
// plugins/api-client.ts
import { defineNuxtPlugin } from '#app'
import { $fetch } from 'ohmyfetch' // Nuxt 内置的 fetch

export default defineNuxtPlugin(() => {
  return {
    provide: {
      // 将 $api 提供给所有组件
      api: $fetch.create({
        baseURL: 'https://my-api.com',
        // 可以设置默认 headers, token 等
        headers: {
          Authorization: `Bearer ${useCookie('token').value}`
        }
      })
    }
  }
})
<!-- 在组件中使用 -->
<script setup>
// 通过 inject 使用
const $api = inject('api')
const data = await $api('/users')
</script>
3.3 模块 (Modules)

模块是 Nuxt.js 生态系统的核心。它们是可复用的插件包,可以扩展 Nuxt.js 的核心功能。

  • 作用

    • 集成第三方库:如 @nuxtjs/tailwindcss@nuxtjs/strapi@nuxtjs/supabase
    • 添加新功能:如 @nuxtjs/robots (生成 robots.txt), @nuxtjs/sitemap (生成站点地图)。
    • 修改 Nuxt 配置:模块可以修改 nuxt.config、添加构建配置、注册插件和中间件等。
  • 使用模块

    1. 安装模块:npm install @nuxtjs/tailwindcss
    2. 在 nuxt.config.ts 的 modules 数组中添加:
      export default defineNuxtConfig({
        modules: [
          '@nuxtjs/tailwindcss',
          '@nuxtjs/robots', // 例如,添加 robots.txt 支持
        ],
        // 模块的特定配置
        robots: {
          rules: {
            UserAgent: '*',
            Disallow: '/admin/'
          }
        }
      })
    3. 模块会自动完成剩下的工作(如配置 Tailwind、生成 robots.txt 文件)。
  • 查找模块:Nuxt.js 官方模块目录 (https://modules.nuxtjs.org/) 是寻找和发现模块的最佳地方。


总结

通过本篇博客,你已经掌握了 Nuxt.js 的进阶功能,让你的应用开发能力更上一层楼:

  • 你学会了使用 useState 进行轻量级状态管理,并理解了其与 Pinia 的适用场景。
  • 你掌握了如何利用 server/api/ 目录构建自己的后端 API,体验了 Nuxt.js 全栈开发的魅力。
  • 你深入理解了中间件、插件和模块这三大扩展机制:
    • 中间件用于控制路由导航。
    • 插件用于扩展应用实例和添加全局功能。
    • 模块是集成第三方库和扩展 Nuxt 功能的标准化方式。
Logo

电影级数字人,免显卡端渲染SDK,十行代码即可调用,工业级demo免费开源下载!

更多推荐