将 Koa 与 Nuxt 集成
Koa
与 Nuxt
的集成可采用两种方式:单端口部署(适用于单域名应用)或多端口部署(适用于跨域应用)。本章将重点介绍单域名集成方案,跨域集成方案将在第 12 章《实现用户登录与 API
认证》中详细讲解。我们将基于前文开发的 Koa
骨架工程实现这两种集成方式。单域名集成需要完成以下配置步骤,现在开始操作:
-
在
Nuxt
项目根目录创建/server/
服务端目录,使用create-nuxt-app
脚手架工具生成项目后,按以下结构组织目录:├── package.json ├── nuxt.config.js ├── server │ ├── config # 配置文件目录 │ │ └── ... # 配置文件 │ ├── public # 公共资源目录 │ │ └── ... # 公共资源文件 │ ├── static # 静态文件目录 │ │ └── ... # 静态资源 │ └── index.js # 服务入口文件 └── pages # Nuxt页面目录 └── ... # 页面组件
-
修改脚手架工具生成的默认
package.json
文件中的脚本配置,集成Backpack
构建工具:package.json"scripts": { "dev": "backpack", // 开发模式启动 "build": "nuxt build && backpack build", // 构建命令 "start": "cross-env NODE_ENV=production node build/main.js", // 生产启动 "generate": "nuxt generate" // 静态生成 }
-
在项目根目录(与
nuxt.config.js
同级)创建Backpack
配置文件,将默认入口目录修改为刚刚创建的/server/
目录:// backpack.config.js module.exports = { webpack: (config, options, webpack) => { config.entry.main = './server/index.js' // 重定向入口路径 return config } }
-
在
/server/
目录下创建index.js
入口文件(确保已安装Koa
),将Koa
作为主应用、Nuxt
作为中间件集成:// server/index.js import Koa from 'koa' import consola from 'consola' // 控制台日志工具 import { Nuxt, Builder } from 'nuxt' const app = new Koa() const nuxt = new Nuxt(config) async function start() { // 将Nuxt挂载为Koa中间件 app.use((ctx) => { ctx.status = 200 // 设置HTTP状态码 ctx.respond = false // 禁用Koa默认响应 ctx.req.ctx = ctx // 注入上下文对象 nuxt.render(ctx.req, ctx.res) // Nuxt渲染管线 }) } start()
请注意,我们创建了一个
async
函数来将Nuxt
用作中间件,以便我们可以在下一步中使用await
语句来运行Nuxt
构建过程。要注意,
Consola
是一个控制台日志记录器,你必须在使用它之前通过npm
安装它。有关此软件包的更多信息,请访问 https://github.com/nuxt-contrib/consola 。 -
加载 Nuxt 配置并处理构建流程 在注册 Nuxt 中间件前,导入 Nuxt 配置文件并根据环境处理构建逻辑:
// server/index.js let config = require('../nuxt.config.js') config.dev = !(app.env === 'production') // 根据环境设置开发模式 if (config.dev) { const builder = new Builder(nuxt) await builder.build() // 开发模式执行构建 } else { // TODO 到底是 nuxt.render() 还是 nuxt.ready() await nuxt.ready() // 生产模式直接启动 }
-
启动服务并监听端口 通过
Consola
输出服务状态信息:app.listen(port, host) // 监听指定端口和主机 consola.ready({ message: `服务已启动: http://${host}:${port}`, // 带格式化的日志输出 badge: true // 启用徽标样式 })
-
开发模式启动命令
$ npm run dev # 启动开发服务器
至此,Nuxt
与 Koa
已成功整合为单一应用。您应当注意到,Nuxt
现在作为 Koa
的中间件运行。所有 Nuxt
页面仍可通过 localhost:3000
正常访问(保持原有功能不变),我们将在下一节配置 localhost:3000/api
作为 API
主端点。
添加路由和其他必要的中间件
在前一节完成基础集成和目录结构搭建后,我们现在通过以下步骤完善 API
路由和其他中间件配置:
-
安装
Koa
路由和静态资源中间件$ npm i koa-router koa-static
-
创建服务端配置文件
// server/config/index.js export default { static_dir: { root: '../static' // 静态资源目录配置 } }
-
在
/server/
目录下创建routes.js
文件,用于定义对外开放的路由并配置模拟用户数据:// server/routes.js import Router from 'koa-router' const router = new Router({ prefix: '/api' }) // 统一添加/api前缀 const users = [ { id: 1, name: 'Alexandre' }, { id: 2, name: 'Pooya' }, { id: 3, name: 'Sébastien' } ] // 基础测试接口 router.get('/', async (ctx) => { ctx.type = 'json' ctx.body = { message: 'Hello World!' } }) // 用户列表接口 router.get('/users', async (ctx) => { ctx.type = 'json' ctx.body = users }) // 用户详情接口 router.get('/users/:id', async (ctx) => { const id = parseInt(ctx.params.id) const found = users.find(user => user.id == id) found ? ctx.body = found : ctx.throw(404, 'user not found') })
-
在独立的
middlewares.js
文件中导入其他中间件,并引入第 1、2 步创建的路由和配置文件:// server/middlewares.js import serve from 'koa-static' import bodyParser from 'koa-bodyparser' import config from './config' import routes from './routes' export default (app) => { // 基础中间件注册 app.use(serve(config.static_dir.root)) app.use(bodyParser()) app.use(routes.routes(), routes.allowedMethods()) }
我们将不在
API
中使用koa-favicon
中间件,原因如下:-
当前
API
仅输出JSON
格式数据,浏览器标签页不会显示favicon.ico
图标 -
Nuxt
已在nuxt.config.js
配置文件中内置了favicon
处理功能 -
因此可以从骨架工程中移除
koa-favicon
中间件,转而创建标准化JSON
响应中间件,其输出格式规范如下:-
成功响应(状态码 200):
{ "status": <status code>, "data": <data> }
-
错误响应(如 400/500 等):
{ "status": <status code>, "message": <error message> }
-
-
-
在
app.use(serve(config.static_dir.root))
代码行之前添加以下代码,以实现上述标准化JSON
响应格式:// 标准化JSON响应格式 app.use(async (ctx, next) => { try { await next(); if (ctx.status === 404) { ctx.throw(404); } if (ctx.status === 200) { ctx.body = { status: 200, data: ctx.body }; } } catch (err) { ctx.status = err.status || 500; ctx.body = { status: ctx.status, message: err.message, }; ctx.app.emit('error', err, ctx); } });
现在,通过该中间件处理后,原本的输出格式 {"message":"Hello World!"} 将被标准化为:
{ "status": 200, "data": { "message": "Hello World!" } }
-
在注册
Nuxt
中间件之前,于主index.js
文件中导入middlewares.js
模块:// server/index.js import middlewares from './middlewares' middlewares(app) // 加载所有中间件 app.use(ctx => { ... nuxt.render(ctx.req, ctx.res) })
-
开发模式运行验证
$ npm run dev # 启动开发服务器
-
好的,如果你在浏览器中访问
localhost:3000/api
,你将会在屏幕上看到以下输出:{"status":200,"data":{"message":"Hello World!"}}
如果你访问用户索引页面
localhost:3000/api/users
,你将会在屏幕上看到以下输出:{"status":200,"data":[{"id":1,"name":"Alexandre"},{"id":2,"name":"Pooya"},{"id":3,"name":"Sébastien"}]}
你也可以使用
localhost:3000/api/users/<id>
来获取特定的用户。例如,如果你使用/api/users/1
,你将会在屏幕上看到以下输出:{"status":200,"data":{"id":1,"name":"Alexandre"}}
你可以在我们的 |
好的,在接下来的部分,我们将探讨如何在客户端通过 Nuxt
页面的 asyncData
方法来请求前面提到的 API
数据。