进阶篇三 Nuxt4 Nitro 引擎:Nuxt 的服务端核心

文章目录

个人网站
Nuxt 3 以后,服务端能力大幅提升,这要归功于 Nitro 引擎。它不只是个构建工具,而是一个完整的服务端框架------支持 API 路由、中间件、存储抽象、多种部署目标......今天深入了解 Nitro。

一、Nitro 是什么

Nitro 是 Nuxt 的服务端引擎,负责:

  • 服务端渲染:执行 Vue 组件的 SSR
  • API 路由 :处理 /api/* 请求
  • 中间件:请求拦截和处理
  • 存储抽象:统一的存储接口
  • 构建产物:生成可部署的服务器代码

简单说:Nuxt 前端 + Nitro 后端 = 全栈应用

二、目录结构

Nitro 相关目录:

复制代码
server/
├── api/           # API 路由
│   ├── hello.ts   # /api/hello
│   └── users/
│       └── [id].ts # /api/users/:id
├── routes/        # 服务端路由
│   └── sitemap.xml.ts  # /sitemap.xml
├── middleware/    # 服务端中间件
│   └── auth.ts
├── plugins/       # Nitro 插件
│   └── db.ts
└── utils/         # 服务端工具函数
    └── format.ts

三、API 路由

创建 API 接口非常简单:

ts 复制代码
// server/api/hello.ts
export default defineEventHandler(() => {
  return {
    message: 'Hello World'
  }
})

访问 /api/hello,返回:

json 复制代码
{ "message": "Hello World" }

动态路由

ts 复制代码
// server/api/articles/[id].ts
export default defineEventHandler((event) => {
  const id = getRouterParam(event, 'id')
  
  return {
    id,
    title: `文章 ${id}`,
    content: '...'
  }
})

访问 /api/articles/123:

json 复制代码
{ "id": "123", "title": "文章 123", "content": "..." }

请求方法

ts 复制代码
// server/api/articles.ts
export default defineEventHandler(async (event) => {
  const method = getMethod(event)
  
  switch (method) {
    case 'GET':
      return getArticles()
    
    case 'POST':
      const body = await readBody(event)
      return createArticle(body)
    
    case 'PUT':
      const updateData = await readBody(event)
      return updateArticle(updateData)
    
    case 'DELETE':
      return deleteArticle()
  }
})

更优雅的方式是拆分文件:

复制代码
server/api/articles/
├── index.get.ts    # GET /api/articles
├── index.post.ts   # POST /api/articles
└── [id]/
    ├── get.ts      # GET /api/articles/:id
    ├── put.ts      # PUT /api/articles/:id
    └── delete.ts   # DELETE /api/articles/:id
ts 复制代码
// server/api/articles/index.get.ts
export default defineEventHandler(() => {
  return getArticles()
})

// server/api/articles/index.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  return createArticle(body)
})

四、请求处理

获取请求信息

ts 复制代码
export default defineEventHandler((event) => {
  // 请求方法
  const method = getMethod(event)
  
  // 请求 URL
  const url = getRequestURL(event)
  
  // 请求头
  const headers = getHeaders(event)
  const auth = getHeader(event, 'authorization')
  
  // 查询参数
  const query = getQuery(event)
  
  // Cookie
  const cookies = parseCookies(event)
  const token = getCookie(event, 'token')
  
  // 请求体
  const body = await readBody(event)
  
  // 客户端 IP
  const ip = getRequestIP(event)
  
  return { method, url, query }
})

设置响应

ts 复制代码
export default defineEventHandler((event) => {
  // 设置状态码
  setResponseStatus(event, 201)
  
  // 设置响应头
  setResponseHeaders(event, {
    'X-Custom-Header': 'value'
  })
  
  // 设置 Cookie
  setCookie(event, 'token', 'xxx', {
    httpOnly: true,
    maxAge: 60 * 60 * 24 * 7
  })
  
  // 删除 Cookie
  deleteCookie(event, 'token')
  
  // 重定向
  return sendRedirect(event, '/login')
  
  // 返回 JSON
  return { success: true }
})

五、中间件

服务端中间件会拦截所有请求:

ts 复制代码
// server/middleware/auth.ts
export default defineEventHandler((event) => {
  const url = getRequestURL(event)
  
  // 只拦截需要认证的路由
  if (url.pathname.startsWith('/api/protected')) {
    const token = getHeader(event, 'authorization')
    
    if (!token) {
      throw createError({
        statusCode: 401,
        message: 'Unauthorized'
      })
    }
    
    // 验证 token
    const user = verifyToken(token)
    event.context.user = user
  }
})

六、存储抽象

Nitro 提供统一的存储接口,支持多种后端:

ts 复制代码
// server/api/views.ts
export default defineEventHandler(async (event) => {
  const storage = useStorage()
  
  // 读取
  const views = await storage.getItem('views') || 0
  
  // 写入
  await storage.setItem('views', views + 1)
  
  return { views }
})

配置存储后端:

ts 复制代码
// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    storage: {
      // 内存存储(开发环境)
      cache: {
        driver: 'memory'
      },
      
      // Redis(生产环境)
      redis: {
        driver: 'redis',
        url: 'redis://localhost:6379'
      },
      
      // 文件系统
      data: {
        driver: 'fs',
        base: './data'
      }
    }
  }
})

使用指定存储:

ts 复制代码
// 使用 redis 存储
const storage = useStorage('redis')
await storage.setItem('user:1', { name: 'Alice' })

// 使用文件存储
const fsStorage = useStorage('data')
await fsStorage.setItem('config.json', { theme: 'dark' })

七、数据库集成

以 Prisma 为例:

bash 复制代码
pnpm add prisma @prisma/client
npx prisma init
prisma 复制代码
// prisma/schema.prisma
model Article {
  id        Int      @id @default(autoincrement())
  title     String
  content   String
  createdAt DateTime @default(now())
}
ts 复制代码
// server/utils/db.ts
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

export const usePrisma = () => prisma
ts 复制代码
// server/api/articles/index.get.ts
export default defineEventHandler(async () => {
  const prisma = usePrisma()
  return prisma.article.findMany()
})

八、计划任务

Nitro 支持定时任务:

ts 复制代码
// server/tasks/cleanup.ts
export default defineTask({
  name: 'cleanup:expired-sessions',
  run: async () => {
    const storage = useStorage()
    const sessions = await storage.getKeys('session:')
    
    for (const key of sessions) {
      const session = await storage.getItem(key)
      if (session?.expiresAt < Date.now()) {
        await storage.removeItem(key)
      }
    }
    
    return { result: 'success' }
  }
})

配置执行间隔:

ts 复制代码
// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    scheduledTasks: {
      '0 * * * *': 'cleanup:expired-sessions'  // 每小时执行
    }
  }
})

九、缓存控制

响应缓存

ts 复制代码
// server/api/articles/[id].ts
export default defineCachedEventHandler(
  async (event) => {
    const id = getRouterParam(event, 'id')
    const article = await getArticle(id)
    return article
  },
  {
    maxAge: 60 * 60,  // 缓存 1 小时
    swr: true,        // 启用 SWR
    varies: ['Authorization']  // 按认证状态分别缓存
  }
)

缓存失效

ts 复制代码
// 手动清除缓存
export default defineEventHandler(async (event) => {
  const cache = useStorage('cache')
  await cache.removeItem('api:articles:123')
  
  return { cleared: true }
})

十、错误处理

ts 复制代码
export default defineEventHandler((event) => {
  const id = getRouterParam(event, 'id')
  
  if (!id) {
    throw createError({
      statusCode: 400,
      message: 'Missing id parameter'
    })
  }
  
  const article = getArticle(id)
  
  if (!article) {
    throw createError({
      statusCode: 404,
      message: 'Article not found'
    })
  }
  
  return article
})

全局错误处理:

ts 复制代码
// server/middleware/error.ts
export default defineEventHandler((event) => {
  event.node.res.on('finish', () => {
    const status = event.node.res.statusCode
    if (status >= 400) {
      console.error(`Error ${status}: ${getRequestURL(event)}`)
    }
  })
})

总结

Nitro 核心能力:

功能 用途
API 路由 创建后端接口
中间件 请求拦截
存储抽象 统一存储接口
缓存 提升性能
计划任务 定时执行
多部署目标 部署到任意平台

有了 Nitro,Nuxt 不只是前端框架,而是全栈框架。下一篇聊聊 Server Routes 的实战应用。

相关文章

入门篇三:Nuxt4组件自动导入:写代码少敲一半字

入门篇二:Nuxt 4路由自动生成:告别手动配置路由的日子

延伸阅读

nuxt4完整系列,持续更新中。。,欢迎来逛逛


内容有帮助?点赞、收藏、关注三连!评论区等你 💪

相关推荐
brien919 分钟前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance35 分钟前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M41 分钟前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写43 分钟前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李1 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易1 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马1 小时前
前沿视界
前端
kyriewen1 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易2 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员
IT_陈寒2 小时前
Vue的这个响应式陷阱,差点让我加班到天亮
前端·人工智能·后端