本文由郝晨光整理总结并编写,未经允许禁止转载。
前言
学习koa,我之前学习过express,但是在使用express的时候,还是一直使用的回调函数的方式来处理异步,现在想想真是恐怖,后来了解到koa这个框架,它相对于express来说,小巧了很多,对于异步的处理也变得更加优雅了。用官方的话来说,koa是基于Node.js平台的下一代web平台开发框架。
正文
- 首先肯定要先开始一个新的项目,新建一个文件夹
- 在文件夹目录中,使用命令提示符执行
,初始化npm init -y
文件;package.json
- 安装
- Koa 依赖 node - v7.6.0 或 ES2015及更高版本和 async 方法支持。
- 先查看node的版本
;node -v
- 然后执行
进行安装;npm install koa
- 使用
- 新建index.js;
- 先上官网上最经典的hello wrold案例;
-
const Koa = require('koa'); const app = new Koa(); app.use(async ctx => { ctx.body = 'hello word!' }) app.listen(8000, () => { console.log('Koa server listen in http://localhost:8000/'); })
- 打开浏览器,输入
,就可以看到我们的页面上显示了hello world!;localhost:8000/
- 那我们的第一个koa应用就已经运行起来了~。
- 读取 HTML 页面
- 在Koa中,我们如果要使用 HTML 应该怎么做呢?
- 在原生nodejs或者express中,我们使用的是fs模块,然后使用回调函数,一层套一层;
- 在Koa中,我们依旧使用fs模块,但是,要对fs模块的读取文件,进行进一步的封装;
- 在目录下,我们新建一个
,并随便写一点东西;index.html
- 接着开始我们渐行渐远的程序生涯;
-
const Koa = require('koa'); const fs = require('fs'); const app = new Koa(); function readFile(url) { return new Promise((resolve, reject) => { fs.readFile(url, (err,file) => { if(err) { reject(err); }else { resolve(file); } }) }) } app.use(async ctx => { ctx.response.type = 'html'; // 重点 ctx.body = await readFile('index.html'); }) app.listen(8000, () => { console.log('Koa server listen in http://localhost:8000/'); }) // 本文由郝晨光整理总结并编写,未经允许禁止转载。
- 可以看到,我们利用Promise将fs模块的readFile方法进行了二次封装,读取了本地的index.html文件
- 重点的地方我已经标出来了,为什么要标重点呢?
- 因为在Koa中,ctx.body默认返回的类型是
;而我们要返回html文件,所以应该在返回文件之前,设置好返回类型,让客户端可以正确的接收;text/plain
- 然后我们使用
组合,在数据读取完成之后,返回读取的文件;async + await
- 最后,打开浏览器,可以看到浏览器上输出的就是我们
文件中写的内容index.html
- 读取静态资源
- html文件我们已经可以正确的读取并返回了,在我们的页面上,避免不了会使用很多的静态资源,例如css、js、image、font等等;这些静态资源我们应该怎么处理呢?
- 在这里,我们使用一个Koa的中间件
koa-static
- 在命令提示符中执行
;npm install koa-static
- 接着书写我们的程序
-
// ~~ 省略 ~~ const app = new Koa(); // 新建koa对象 const KoaStatic = require('koa-static'); // koa静态文件读取 app.use(KoaStatic(__dirname)); // 使用中间件 // ~~ 省略 ~~
- 接着,打开我们的浏览器,可以看到静态资源已经可以正确的请求到了;
- 当然了,这个时候,我们可以把之前返回html文件时设置响应类型的一步省略掉了,因为
已经帮我们做了这件事了。koa-static
- 定义路由
- 所谓的路由,就是根据不同的请求路径,响应不同的内容;
- 在Koa中,我们可以定义原生路由,也可以使用封装好的路由中间件;
- 先看一下原生路由的写法吧!
-
app.use(async ctx => { if(ctx.request.method==='GET') { // GET请求 switch (ctx.request.path) { case '/': // 匹配默认路由 ctx.body = await readFile('index.html'); // 返回index.html文件 break; case '/about': // 匹配/about路由 ctx.body = 'about路由页面'; // 返回about路由页面 break; default: ctx.body = await readFile('index.html'); // 默认返回index.html文件 break; } }else if(ctx.request.method==='POST') { // POST请求 switch (ctx.request.path) { default: ctx.body = 'post 请求'; // 响应post请求 break; } } }); //本文由郝晨光整理总结并编写,未经允许禁止转载。
- 可以看到我上边写的,对请求方式以及路由进行了处理,在不同的请求方式,不同的请求路径下,执行不同的操作,响应不同的结果。
- 路由中间件
- 我们使用原生方式定义路由,未免有些太过繁琐,并且代码不便于阅览和维护;所以,建议使用中间件的方式进行路由配置。
- 我这里使用的是 koa-router 这个中间件,当然,koa的路由中间件不是只有这一种。
- 在命令提示符中安装
;npm install koa-router
- 接着,进行我们的程序生涯;
-
// ~~ 省略 ~~ const KoaRouter = require('koa-router'); const router = new KoaRouter(); router.get('/',async ctx => { ctx.body = await readFile('index.html'); }); router.get('/about',async ctx=> { ctx.body = 'about路由页面' }); router.post('/form',async ctx => { ctx.body = 'post请求' }); app.use(router.routes()); // ~~ 省略 ~~
- 打开页面,并进行路由切换,可以看到没有任何问题;
- 但是这样所有的路由配置都写在了index.js中,不利于维护,所以我们要将路由内容和公用方法提取出来。
- 代码模块化
- 首先对路由进行模块化
- 在目录下新建routes文件夹,并新建home.js,用来存放关于首页的一些路由配置。
- 将上边的路由代码单独写到home.js中,并抛出,如下:
- home.js
-
const KoaRouter = require('koa-router'); const router = new KoaRouter(); router.get('/',async ctx => { ctx.body = await readFile('index.html'); }); router.get('/about',async ctx=> { ctx.body = 'about 路由' }); router.post('/from',async ctx => { ctx.body = 'post请求' }); module.exports = router;
- index.js
-
const homeRouter = require('./routes/home'); app.use(KoaStatic(__dirname)); app.use(homeRouter.routes());
- 在index.js中,直接引入使用即可,和原有代码没有任何区别。
- 需要注意的是,对于功能中间件,例如
这种,我们应该放在路由中间件之前。koa-static
- 否则的话可能会出错,在路由中不能正确的读取静态文件等。
- 二级路由
- 已经学会了一级路由的定义,我们趁热打铁,学习二级路由
- 在routes目录下,新建user.js,定义用户路由
-
// user.js const KoaRouter = require('koa-router'); const router = new KoaRouter({ prefix: '/user' }); router.get('/',async ctx => { ctx.body = '用户界面' }); router.get('/:id',async ctx => { ctx.body = '用户详情'+ctx.params.id; }); router.post('/login',async ctx => { ctx.body = '用户注册成功!' }); module.exports = router; // index.js // ~~ 省略 ~~ const userRouter = require('./routes/user'); // ~~ 省略 ~~ app.use(userRouter.routes());
- 接着,打开浏览器,我们测试一下我们的二级路由,是没有任何问题的。
- post请求处理
- 我们都知道,使用post请求一般用来提交表单,或者修改数据等。而post请求中的数据,存放在请求体中,使用原生Node的方法的话,我们需要监听原生req对象上的data方法以及end方法,并将数据格式转化。而express中,我们有一个
的插件可以使用。body-parser
- 那么在Koa中,我们应该使用什么呢?
- 在Koa中,我们使用
这个中间件来处理post请求的数据koa-bodyparser
- 在命令提示符安装
;npm install koa-bodyparser
- 接着我们在index.js中使用这个中间件;
- 加入下面两行代码,与原先使用
中间件位置一样即可;koa-static
-
const KoaBodyParser = require('koa-bodyparser'); app.use(KoaBodyParser());
- 使用了这个中间件,我们就可以在
中拿到post请求的数据了;ctx.request.body
-
router.post('/login',async ctx => { ctx.body = ctx.request.body; });
- 接着改造一下我们的html文件,提交一下表单;
-
<form action="/user/login" method="post"> 姓名:<input type="text" name="name" autocomplete="off"/> <br> <input type="radio" name="sex" value="男"/>男 <input type="radio" name="sex" value="女"/>女 <input type="submit" value="提交form"> </form>
- 可以看到,表单提交正常,我们也正确的拿到了表单提交的数据。
- 我们都知道,使用post请求一般用来提交表单,或者修改数据等。而post请求中的数据,存放在请求体中,使用原生Node的方法的话,我们需要监听原生req对象上的data方法以及end方法,并将数据格式转化。而express中,我们有一个
- CORS跨域配置
- 在我们现在的服务器生涯中,避免不了要使用跨域,特别是CORS跨域。
- 那么,在Koa中,我们应该怎么去设置跨域呢?
- 我们可以使用中间件,也可以使用原生方法手写。
- 老规矩,先看原生方法。
-
app.use(async (ctx, next) => { // 允许来自所有域名请求 ctx.set("Access-Control-Allow-Origin", "*"); // 这样就能只允许 http://localhost:8080 这个域名的请求了 // ctx.set("Access-Control-Allow-Origin", "http://localhost:8080"); // 设置所允许的HTTP请求方法 ctx.set("Access-Control-Allow-Methods", "OPTIONS, GET, PUT, POST, DELETE"); // 字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段. ctx.set("Access-Control-Allow-Headers", "x-requested-with, accept, origin, content-type"); // 服务器收到请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。 // Content-Type表示具体请求中的媒体类型信息 ctx.set("Content-Type", "application/json;charset=utf-8"); // 该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。 // 当设置成允许请求携带cookie时,需要保证"Access-Control-Allow-Origin"是服务器有的域名,而不能是"*"; ctx.set("Access-Control-Allow-Credentials", true); // 该字段可选,用来指定本次预检请求的有效期,单位为秒。 // 当请求方法是PUT或DELETE等特殊方法或者Content-Type字段的类型是application/json时,服务器会提前发送一次请求进行验证 // 下面的的设置只本次验证的有效时间,即在该时间段内服务端可以不用进行验证 ctx.set("Access-Control-Max-Age", 300); /* CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段: Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。 */ // 需要获取其他字段时,使用Access-Control-Expose-Headers, // getResponseHeader('myData')可以返回我们所需的值 ctx.set("Access-Control-Expose-Headers", "myData"); await next(); })
- 原生方法来自:node.js 应答跨域请求实现(以koa2-cors为例)
- 接着我们来看中间件吧!
- 在Koa2中,我们使用
这个中间件来设置CORS跨域请求。koa2-cors
- 先安装
;npm install koa2-cors
- 接着,在index.js中使用。
-
const KoaCors = require('koa2-cors'); // ~~ 省略 ~~ // CORS跨域 app.use(KoaCors({ origin: ctx => { return ctx.request.header.origin; }, exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'], maxAge: 5, credentials: true, allowMethods: ['GET', 'POST', 'DELETE', 'OPTIONS'], allowHeaders: ['Content-Type', 'Authorization', 'Accept', 'Origin'], })); //本文由郝晨光整理总结并编写,未经允许禁止转载。
- 特别需要注意的是,在
使用app.use()
的时候,我们应该把它放在别的中间件的前边,特别是静态资源处理和路由处理中间件的前边,这样可以保证我们在跨域请求静态资源的时候不会出问题。koa2-cors
如果本文对您有帮助,可以看看本人的其他文章:
前端常见面试题(十三)@郝晨光
前端常见面试题(十二)@郝晨光
前端常见面试题(十一)@郝晨光
结言
感谢您的查阅,本文由郝晨光整理并总结,代码冗余或者有错误的地方望不吝赐教;菜鸟一枚,请多关照