一文彻底搞懂koa2和ejs
一、安装
开发koa2之前,要求nodejs版本要高于v7.6。因为nodejs7.6版本开始完全支持async/await,所以才能完全支持koa2
安装语法 : npm install koa --save-dev
使用之前,先npm init 初始化
二、简单使用
var koa= require( ‘koa’);
var app= new koa();
//中间件,express中的req,res参数变成了ctx
app.use(async(ctx)=>{
ctx.body=”你好koa2.x”
})
app.listen(3000);
三、koa路由
3.1 基本实现
· koa中的路由跟express有所不同,我们需要安装对应的koa-router路由模块来实现
npm install koa-router --save-dev
引入:
var koa =require(‘koa’);
var router=require(‘koa-router’)();
var app=new koa();
//ctx 上下文context, 包含了request和response等信息
router.get(‘/’, async (ctx) => {
ctx.body=’首页’//相当于res.end( )
}).get(‘/news’, async (ctx) => { //可以连续使用,也可以分开用
ctx.body=‘这是一个新闻页面’
})
app.use(router.routes())//启动路由
app.use(router.allowedMethods())
//作用:这是官方文档的推荐用法,
//我们可以看到router.allowedMethods()用在了路由匹配router.routes()之后,
//所以在当所有路由中间件最后调用,此时根据ctx.status设置响应头
app.listen(3000)
3.2 get传值
如何获取参数?
方式一:(推荐)
ctx.query //打印{aid: ‘123’} 获取的是对象(用的最多的方式)
ctx.querystring // aid=123&name=zhangsan 获取的是一个字符串
方式二:我们可以从ctx里面的request里面获取get传值
ctx.request.requery // {aid: ‘123’}
ctx.quest.querystring // aid=123&name=zhangsan
3.3 动态路由
router.get('/newscontent/:aid', async (ctx) => {
console.log(ctx.params) // { aid : ‘123’}
ctx.body = '新闻详情'
})
动态路由可以传入多个值
router.get('/newscontent/:aid/:cid', async (ctx) => {
console.log(ctx.params) //{aid:’123’ , cid: ‘456’}
ctx.body = '新闻详情'
})
四、koa中间件
4.1 中间件使用
中间件的功能:
· 执行任何代码
· 修改请求和响应对象
· 终结请求--响应循环
· 调用堆栈中的下一个中间件
如果get、post回调函数中没有next参数,name就匹配上第一个路由,就不会往下匹配了,如果想往下匹配,那么就需要写next()
4.2 应用级中间件
· 定义:就是在匹配所有路由之前进行操作某种需求
const Koa = require('koa');
const router = require('koa-router')();
const app = new Koa();
// 中间件,匹配任何路由,也就是在匹配任何路由之前调用它
app.use(async(ctx,next)=>{
ctx.body="中间件"
console.log(new Date())
await next() // 当前路由匹配完成以后继续向下匹配,不写就不会向下匹配
})
router.get('/', async (ctx) => {
ctx.body = '首页'
})
router.get('/news', async (ctx) => {
ctx.body = '新闻'
})
app.use(router.routes()); //启动路由
app.use(router.allowedMethods())
app.listen(3000)
4.3 路由中间件
· 定义: 匹配特定路由前进行某种操作
router.get('/news', async (ctx,next) => {//匹配到news路由后继续向下匹配路由
console.log(“新闻中间件”)
await next()
})
router.get('/news', async (ctx) => {
ctx.body = '新闻'
})
4.4 错误处理中间件
· express的中间件和路由有前后顺序,而koa没有顺序,即使放在路由最后面,也先执行中间件
例子:404页面
app.use(async(ctx,next)=>{
console.log(‘这是一个中间件01’)
await next()
if(ctx.status==404){
ctx.status=404;
ctx.body=”这是一个404页面”
}else{
console.log(‘正常’)
}
})
router.get('/', async (ctx) => {
ctx.body = '首页'
})
打印结果:这是一个中间件01 -> 首页 -> 正常
执行顺序,先打印:这是一个中间件01,然后执行next()下面的路由,当执行完路由返回结果后,再回到app.use中间件,执行if判断
· 这时候我们就要说一下koa的执行模式
app.use(async(ctx,next)=>{
console.log('中间件01')
await next()
console.log('1')
})
app.use(async(ctx,next)=>{
console.log('中间件02')
await next()
console.log('2')
})
app.use(async(ctx,next)=>{
console.log('中间件03')
await next()
console.log('3')
})
router.get('/', async (ctx) => {
ctx.body = '首页'
})
router.get('/news', async (ctx) => {
console.log('匹配到新闻页面')
ctx.body = '新闻'
})
执行结果:
中间件01 ->中间件02 ->中间件03->匹配到新闻页面-> 3 -> 2 -> 1
koa就像洋葱一样,先执行request,然后再执行response(从外到内,再从内到外)
如图:
[图片上传失败...(image-559dff-1692700854499)]
4.5 第三方中间件
五、 ejs模板引擎
5.1 安装 koa-views ejs
· npm install koa-views --save
· npm install ejs --save
5.2 使用
const Koa = require('koa');
const views =require('koa-views')
const router = require('koa-router')();
const app = new Koa();
// app.use(views(__dirname + '/views', {map: {html: 'ejs'}})) 这种方式文件名得写index.html
app.use(views(__dirname+'/views', { extension: 'ejs' })) 这种写index.ejs
router.get('/', async (ctx) => {
let title=”hello world
let arr=[‘刚刚’,‘他娃儿噶’,‘大幅度’]
await ctx.render('index',{ //别忘了加 await
title:title,
arr:arr
})
})
html部分
<h1>这是一个ejs的模板引擎</h1>
<h2><%= title %></h2>
<ul>
<% for(var i=0; i<arr.length; i++){ %>
<li><%= arr[i] %></li>
<% }%>
</ul>
5.3 可以引入公共header 和footer
新建一个header.ejs文件
<h1>我是公共header</h1>
然后通过 <%- include('public/header.ejs') -%>引入
5.4 绑定数据
nodejs部分
router.get('/', async (ctx) => {
let content = '<h3>我是数据</h3>'
await ctx.render('index',{
content:content
})
})
ejs部分
<%=content%>
可是这样输出的是 <h3>我是数据</h3>,不能解析<h3>标签
这时候我们需要用<%- %>
<%-content%>这样就可以了
5.5 条件判断
router.get('/', async (ctx) => {
let number=123
await ctx.render('index',{
number:number
})
})
ejs部分
<% if(number>20){%>
<p>大于20</p>
<%} else {%>
<p>小于20</p>
<%}%>
5.6 如何配置公共数据
· 如果所有的页面都需要同一个数据的时候,该怎么办呢?
当然你也可以一个页面写一个,但是如果有几十个上百个页面那就太麻烦了,这时候我们就可以在中间件的ctx.state中去定义
例子:比如每个页面都需要userInfo数据
我们可以这样做
nodejs部分
app.use(anync (ctx,next) =>{
ctx.state.userInfo=”我是公共数据”
await next()
})
ejs调用
<%=userInfo%>
六、 koa post提交数据
6.1 nodejs原生实现接收表单请求数据
ejs部分
<form action="/doAdd" method="post">
<label for="">用户名:</label>
<input type="text" name="username">
<br>
<label for="">密码:</label>
<input type="password" name="password">
<br>
<button type="submit">确定</button>
</form>
nodejs部分
const Koa = require('koa');
const views =require('koa-views')
const router = require('koa-router')();
const common =require('./module/common.js')
const app = new Koa();
app.use(views(__dirname+'/views', { extension: 'ejs' }))
router.get('/', async (ctx) => {
await ctx.render('index.ejs')
})
router.post('/doAdd', async (ctx) => {
var data= await common.getPostData(ctx)
console.log(data)
ctx.body=data;
})
app.use(router.routes()); //启动路由
app.use(router.allowedMethods())
app.listen(3000)
我们新建了一个module文件夹,在里面新建了一个common.js
通过node原生代码实现接收客户端请求的数据
在common.js中封装了一个函数getPostData
代码如下
exports.getPostData=function(ctx){
return new Promise(function(resolve,reject){
try{
var str='';
ctx.req.on('data',function(chunk){
str+=chunk;
})
ctx.req.on('end',function(chunk){
resolve(str)
})
}catch(err){
reject(err)
}
})
}
6.2 koa-bodyparser获取表单提交的数据
①先安装
npm install koa-bodyparser --save
② 引入
const bodyParser =require(‘koa-bodyparser’)
③配置koa-bodyparser中间件
app.use(bodyParser())
④ 获取表单提交的数据
ctx.request.body
代码如下
router.post('/doAdd', async (ctx) => {
var data = ctx.request.body
ctx.body=data;
})
七、 koa-static 静态资源中间件
为什么要用它?
像css文件,如果只用link引入的话是无效的,这时候就用到了koa-static来处理静态资源
① 安装
npm install koa-static --save
② 引入
const static = require(‘koa-static’)
③ 配置,可以配置多个
app.use(static(__dirname+'/static'))
首先去static寻找,如果找到了返回对应的文件,找不到就next()
注意:link引入的时候,不要写外部的文件夹
比如:<link rel="stylesheet" href="../static/css/style.css"> 这样写是不行的
<link rel="stylesheet" href="css/style.css"> 这样写就可以了
同样,图片引入的时候也不用写static
八、 art-template模板引擎(看官网)
art-template 是一个简约、超快的模板引擎。
它采用作用域预声明的技术来优化模板渲染速度,从而获得接近 JavaScript 极限的运行性能,并且同时支持 NodeJS 和浏览器。
用两种写法:①跟ejs类似 ② 跟angular类似
render(app, {
root: path.join(__dirname, 'view'), //视图
extname: '.art', //后缀名
debug: process.env.NODE_ENV !== 'production' //是否开启调试
});
九、 koa中cookie的使用
9.1 cookie 简介
· cookie是存储于访问者的计算机中的变量
· http协议是无状态的
· cookie的作用
① 保存用户信息
② 浏览器历史记录
③ 猜你喜欢的功能
④ 10天免登陆
⑤ 多个页面之间的数据传递
⑥ cookie实现购物车功能
9.2 koa中设置cookie的值
ctx.cookies.set(name,value,[options])
通过options设置cookie 中name 的value的配置
cookie的参数
[图片上传失败...(image-fd84b1-1692700854499)]
· 实现多个页面之间的cookie共享
router.get('/', async (ctx) => {
ctx.cookies.set('userinfo','zhangsan',{
maxAge:60*1000*60, //多长时间以后过期
path:’/news’,//特定路径才能访问
domain:’.baidu.com’,
// 表示 a. baidu.com b.baidu.com 可以共享cookie的数据,
//正常情况下不要设置,默认就是当前域下面的所有页面都可以访问
httpOnly:true ,
//true表示这个cookie只有服务器端可以访问(在页面中通过js访问不到),
//false表示服务端和都可以访问
})
await ctx.render('index.ejs')
})
router.get('/news', async (ctx) => {
var cookdata = ctx.cookies.get('userinfo')
ctx.body = cookdata;
})
9.3 koa中的cookie的bug
· 如果设置cookie的值为中文时会报错
router.get('/', async (ctx) => {
ctx.cookies.set('userinfo',‘张三’,{ //会报错
maxAge:60*1000*60
})
})
解决方法:
运用buffer将汉字转换成base64 字符 ,最后再将base64还原会汉字
router.get('/', async (ctx) => {
var userinfo = new Buffer(‘张三’).toString(‘base64’)
ctx.cookies.set('userinfo',userinfo ,{
maxAge:60*1000*60
})
await ctx.render('index.ejs')
})
router.get('/news', async (ctx) => {
var cookdata = ctx.cookies.get('userinfo')
var userinfo = new Buffer(cookdata ,’base64’).toString()
ctx.body = userinfo;
})