koa是Express团队打造的新一代web框架,特点是更小,更舒服的开发体验。
前一节我们已经讲过koa的基本使用(不了解的可以先读读这篇文章《koa快速入门之基础使用》,下面的思维导图列出来大概的知识),但是都是基于单文件开发的,本文介绍一下koa项目开发的最佳实践。
本文编写的原因是,koa没有官方的项目生成器,虽然官方提供了一些其他人的最佳实践,可以参考下koa项目示例,但是这些实践都不合作者的心意,有些太过简单,有些结构太过杂乱。
于是作者决定基于koa2和TypeScript,自己组织一个最佳实践。
先把成果的github拿出来😄,达到目的的读者不要急着走,麻烦帮忙点赞收藏下再走😂。
🌟GitHub:koa-ts-template koa2+TypeScript实现的api和html服务模板🌟
首先确定koa2和typeScript是我们项目的基础。
选择ts一方面是为了类型检查,另一方面是为了更好的面向接口编程。
为了规定代码,我会用到ESlint,相关配置,可以参考我们最后的koa-ts-template 。
更多代码规范的最佳实践知识,可以参考这篇文章《2022代码规范最佳实践(附web和小程序最优配置示例)》
为了完善服务的功能,我选择加入一些常用的中间件,这里为了维持模板是最简洁的,我们只引入必要的中间件,同时对中间件也不做过多的配置。
为了使项目简洁,最重要的是分离基础的配置(比如路由配置)和业务功能api的实现代码、业务html的实现代码。同时,不同功能点也要分离解耦。
最后,我们的项目结构组织如下,在业务开发是,我们主要需要关注view(html模板)、route(路由)、public(静态资源)、controller(业务逻辑)这几个模块。
koa-ts-template
├─views // 模板文件
| ├─index.ejs
| ├─signin-failed.ejs
| ├─signin-ok.ejs
| └signin.ejs
├─route // 路由配置
| ├─api.ts // api路由
| ├─html.ts // html路由
| └index.ts
├─public // 静态资源
| └root.txt
├─controller // 逻辑控制
| ├─html // html逻辑
| | ├─index.ts
| | └signin.ts
| ├─api // api逻辑
| | ├─signin.ts
| | └user.ts
├─.vscode
| └settings.json
├─app.ts // 入口文件
├─.eslintignore
├─.eslintrc.js
├─README.md
├─package-lock.json
├─package.json
├─tsconfig.json
下面我们来填充具体的实现代码,为了方便演示,这里实现了一个简化版本的登录功能,包括登录用的api接口和html页面。
首先是入口文件app.ts
在这里我们需要做的是:
// app.ts
import Koa from 'koa';
import koaBody from 'koa-body';
import koaViews from 'koa-views';
import koaStatic from 'koa-static';
import koaLogger from 'koa-logger';
import router from './route';
import path from 'path';
const app = new Koa();
// 解析 request body:
app.use(koaBody());
// 加载模板引擎
app.use(koaViews(path.join(__dirname, './views'), {
extension: 'ejs',
}));
// 日志
app.use(koaLogger())
// 静态资源服务
app.use(koaStatic(path.join(__dirname, './public'), {
maxAge: 30 * 24 * 60 * 60 * 1000, // 一个月
}));
// 路由
app.use(router.routes())
.use(router.allowedMethods());
app.listen(3000);
这些中间件的可配置参数还有很多,具体参考各自的文档,这里不加赘述。
从上面的目录结构可以看到,我把api和html的路由分开配置,同时暴露一个路由入口文件,用来合并这两块的路由。
// route/index.ts
import Router from 'koa-router';
import ApiRoute from './api';
import HtmlRoute from './html';
const router = new Router();
router.use('/api', ApiRoute.routes());
router.use('/html', HtmlRoute.routes());
export default router;
// route/api.ts
import Router from 'koa-router';
import userController from '../controller/api/user';
import signController from '../controller/api/signin';
const router = new Router();
router.get('/user', userController.getUser);
router.post('/signin', signController.postSignIn);
export default router;
html路由配置也是差不多的结构,具体见项目仓库。
我在controller里面实现具体的业务逻辑,比如,在controller/api/signin.ts实现登录api接口。
结合路由配置里面api的路由配置,我们可以知道,这个api接口的路径是localhost:3000/api/signin
,是post请求。阅读项目仓库,还可以发现签到的html页面,路径是localhost:3000/html/signin
在业务逻辑中,我们获取name和password两个参数,然后简单判断用户名密码是否正确,根据判断结果,跳转不同的html页面。
// controller/api/signin.ts
import { Context } from 'koa';
export default {
postSignIn(ctx: Context): void {
const name = ctx.request.body.name || '';
const password = ctx.request.body.password || '';
console.log(`signin with name: ${name}, password: ${password}`);
if (name === 'koa' && password === '12345') {
ctx.redirect(`/html/signin-ok?name=${name}`);
} else {
ctx.redirect('/html/signin-failed');
}
},
};
为了直接运行ts应用,我用到了ts-node。
为了服务能够热更新,我用到了nodemon。
简单安装后(直接用package就行了,不用全局安装),我们配置下面这个script命令,就可以跑起来一个可以热更新的ts应用了。
"scripts": {
"dev": " nodemon --exec 'ts-node' --files app.ts",
"start": "npm run dev"
}
到这里,我们的模板项目就完成了。
npm run start
我们可以跑起来api服务和html页面,敬请clone koa-ts-template 体验一下。