最近学了 Node.js
相关课程,来总结记录一波~
Koa
基本概念
Koa
是一个基于 Node.js
的 Web
框架,由 Express.js
的原作者设计和开发,旨在提供一种更加简洁、灵活的方式来构建 Web
应用。Koa
采用了异步、中间件和生成器等概念,让开发者能够更轻松地处理请求和响应、控制流程以及处理异步操作。
实例
我们先来新建一个文件夹,然后在该文件夹下执行以下命令:
// 先初始化仓库
npm init
// 然后安装koa,本文使用的koa版本为2.15.0
npm install koa
新建一个 app.js
文件,先来学习一下必修的 “Hello World” 应用。
// 引入Koa.js
const Koa = require("koa")
// 创建一个新的Koa实例
const app = new Koa()
// 定义一个中间件函数
app.use(async ctx => {
// 设置响应体的内容
ctx.body = 'Hello World'
})
// 告诉 Koa 应用在端口 3000 上监听传入的 HTTP 请求,要确保3000端口号没有被其它的应用占用,否则会报错。
app.listen(3000)
终端运行app.js
:
node app.js
然后打开我们的浏览器,输入http://localhost:3000/
,可以看到屏幕上显示了 “Hello World”。
总的来说,这段代码创建了一个在端口 3000 上监听的 Web
服务器,当收到请求时,服务器会返回 “Hello World” 的响应。
中间件
上面的代码注释中,我们有看到一个概念“中间件”,什么是中间件呢?
中间件函数是一个异步函数,它的主要作用是在请求和响应对象上添加一些行为,例如设置响应体的内容、处理请求数据等,在 Koa
中,多个中间件可以串联起来形成一个处理管道,每个中间件函数可以修改请求和响应参数,并将它们传递给下一个中间件函数。
中间件函数接收两个参数,第一个参数为上下文(context),包含了请求和响应的信息。第二个参数是一个 next
函数,调用它可以传递控制权给下一个中间件。
中间件的执行顺序遵从洋葱模型,以next()
函数作为分隔点,洋葱模型的执行过程分为两部分:首先从外向内执行请求相关的逻辑,然后从内向外执行响应相关的逻辑。
我们来看下实际例子理解下,修改app.js文件:
const Koa = require('koa');
const app = new Koa();
// 中间件1
app.use(async (ctx, next) => {
console.log("中间件1 next之前");
// await next();
console.log("中间件1 next之后");
});
// 中间件2
app.use(async (ctx, next) => {
console.log("中间件2 next之前");
// await next();
console.log("中间件2 next之后");
});
// 中间件3
app.use(async (ctx, next) => {
console.log("中间件3 next之前");
// await next();
console.log("中间件3 next之后");
});
app.listen(3000);
我们再次打开浏览器进行访问的时候,发现我们的修改是没有生效的,因为我们没有重新运行 app.js
文件,每次改动都需要重新运行才会生效,这样着实有点麻烦,所有我们先来全局安装下 nodemon
,它可以监听 node.js
源代码的任何变化并自动重启我们的服务。
npm install -g nodemon
安装完成之后,可以终端输入以下命令运行我们的文件:
nodemon app.js
之后我们对 app.js
进行任何修改都不需要重新运行了。nodemon
也可以结合我们的编辑器,打开 vscode
,点击左侧菜单栏的“运行和调试”:
我们的项目根目录下就会生成一个 /.vscode/launch.json
文件,打开该文件,点击右下角的“添加配置”:
保存之后,再次点击左侧菜单栏的“运行和调试”:
点击小三角运行我们的文件,然后打开浏览器,输入http://localhost:3000/
进行访问。终端的输出如下:
中间件1 next之前
中间件1 next之后
可以看到我们在没有调用 next
函数的时候,只有第一个中间件函数被执行了,后面的都不执行。再把 await next()
前的注释去掉,重新访问页面,终端的输出如下:
中间件1 next之前
中间件2 next之前
中间件3 next之前
中间件3 next之后
中间件2 next之后
中间件1 next之后
现在再去看下之前的概念,是不是就很清晰了呢~
路由
我们的 API
都有不一样的路径,Koa
是怎么获取 API
的路径并执行相应业务的呢?这就需要通过路由来实现,路由可以根据不同的 URL
路径和 HTTP
请求方法,将请求分发到不同的处理函数,从而实现不同的功能。在 Koa
中,路由的实现需要使用第三方路由中间件,本文使用的是 koa-router
。使用步骤如下:
1、安装 koa-router
:
npm install koa-router
2、修改 app.js
文件:
const Koa = require("koa");
// 引入koa-router
const Router = require("koa-router")
const app = new Koa();
// 创建router对象
const router = new Router()
// 定义路由,定义一个GET请求的根路径“/”的路由
router.get('/', (ctx, next) => {
ctx.body = "Hello World"
})
// 定义路由,定义一个GET请求的“/home”路径的路由
router.get('/home', (ctx, next) => {
ctx.body = "home"
})
// 将router对象挂载到koa的app对象上
app.use(router.routes())
app.listen(3000);
浏览器中访问http://localhost:3000/
,页面会显示“Hello World”。访问http://localhost:3000/home
,页面会显示“home”。
如果把所有的路由都统一写在 app.js
中的话,就会使得我们的路由混乱难以维护。实际开发中,我们需要根据实际项目需求和业务模块将路由进行不同的划分。
每个模块的路由可以集中管理,方便开发和维护。一些常见的划分方式如下:
- 根据业务模块划分:可以根据不同的业务模块来划分路由,例如用户模块、商品模块、订单模块等。
- 根据功能划分:可以根据不同的功能来划分路由,例如登录、注册、获取用户信息、获取商品信息等。
- 根据请求类型划分:可以根据不同的请求类型来划分路由,例如GET请求、POST请求、PUT请求、DELETE请求等。
- 根据资源划分:可以根据不同的资源来划分路由,例如用户资源、商品资源、订单资源等。
本文要实现一个书籍列表,点进去之后可以看书籍介绍,书籍评论,以及对书籍进行收藏。所以将路由划分为四大块:用户(user)、书籍(book)、评论(comment)、我的收藏(favor)。
项目根目录下新建文件夹 /app/api/v1
,v1
文件夹标识的是版本,若后续需要对项目进行整体升级,则新的 API
就都放到 v2
文件夹中。然后新建以下四个文件:
/app/api/v1/user
:
const Router = require("koa-router");
const router = new Router({
prefix: "/v1/user",
});
router.get('/', (ctx, next) => {
ctx.body = "用户"
})
module.exports = router
/app/api/v1/book
:
const Router = require("koa-router");
const router = new Router({
prefix: "/v1/book",
});
router.get('/list', (ctx, next) => {
ctx.body = "书籍列表"
})
module.exports = router
/app/api/v1/comment
:
const Router = require("koa-router");
const router = new Router({
prefix: "/v1/comment",
});
router.get('/list', (ctx, next) => {
ctx.body = "评论列表"
})
module.exports = router
/app/api/v1/favor
:
const Router = require("koa-router");
const router = new Router({
prefix: "/v1/favor",
});
router.get('/list', (ctx, next) => {
ctx.body = "收藏列表"
})
module.exports = router
最后我们需要这些路由注册为 Koa
的中间件, 修改 app.js
文件:
const Koa = require("koa");
const userRouter = require("./app/api/v1/user")
const bookRouter = require("./app/api/v1/book")
const commontRouter = require("./app/api/v1/comment")
const favorRouter = require("./app/api/v1/favor")
const app = new Koa();
app.use(userRouter.routes())
app.use(bookRouter.routes())
app.use(commontRouter.routes())
app.use(favorRouter.routes())
app.listen(3000);
浏览器输入相应路由就可以看到相应的返回。
复杂的项目中,路由肯定不止这么几个,如果需要一个个引入并注册的话那就太繁琐了,接下来我们来实现下路由的自动注册。
app.js
文件中不适合放太多的逻辑,所以我们新建一个文件用来进行初始化相关的操作。
根目录下新建文件 /code/init.js
,我们需要借助一个第三方模块:require-directory
, 它可以递归遍历指定的目录。
安装 require-directory
:
npm install `require-directory`
/code/init.js
const requireDirectory = require("require-directory");
const Router = require("koa-router");
class InitManager {
static initCore(app){
InitManager.app = app
InitManager.initLoadRouters()
}
static initLoadRouters(){
const api = `${process.cwd()}/app/api`
requireDirectory(module, api, {visit: whenLoadModule})
function whenLoadModule(obj){
// 判断当前模块是否是路由模块,是才注册为中间件
if(obj instanceof Router){
InitManager.app.use(obj.routes())
}
}
}
}
module.exports = InitManager;
require-directory
接收三个参数:
1、固定参数 module
:module
是指当前模块的引用,在 Node.js
中,每个模块都有一个指向其本身的引用,可以通过 module
变量来获取,require-directory
通过该参数知道从哪个目录开始加载路由文件。
2、目录路径:指定 require-directory
自动加载的路径。
3、options
对象: 这是一个可选参数,可以使用它定义 require-directory
的行为。上面的代码中,visit
函数将在每个模块被加载时调用。
最后修改 app.js
文件:
const Koa = require("koa");
const InitManager = require("./core/init")
const app = new Koa();
InitManager.initCore(app)
app.listen(3000);
改造完成!之后路由文件有增加的时候就不需要我们手动引入注册了。
到这里相信大家对 Koa
的基本使用已经了解了,下一篇我们先来连接 MySQL
。
原文链接:https://juejin.cn/post/7329144189002006568 作者:SummerStar