在网络请求中,请求往往分成两种类型,一种是静态资源,直接从服务器的文件存储中读取,一种是动态资源,一般需要先从数据库获取数据,然后经过一定的处理,最后返回给客户端。
根据上面的思想,所以实现简单版的static,可以将static单独存在一个js文件按中,然后require进来,这样使用和koa一样:
const Koa = require('koa');
const path = require('path');
const Router = require('koa-router');
const fs = require('fs');
const {promisify} = require('util'); //将函数promise化
const stat = promisify(fs.stat); //用来获取文件的信息
const mime = require('mime'); //mime类型获取插件
let app = new Koa();
let router = new Router();
function static(dir) {
return async (ctx,next)=>{
let pathname = ctx.path;
//获取请求文件的绝对路径
let realPath = path.join(dir,pathname);
try{
let statObj = await stat(realPath);
if (statObj.isFile()) {
//如果是文件则读取文件,并且设置好相应的响应头
ctx.set('Content-Type', mime.getType(realPath)+";charset=utf-8");
ctx.body = fs.createReadStream(realPath)
} else {
//如果不是文件,则判断是否存在index.html
let filename = path.join(realPath, 'index.html')
await stat(filename)
ctx.set('Content-Type', "text/html;charset=utf-8");
ctx.body = fs.createReadStream(filename);
}
}catch(e){
await next(); //交给后面的中间件处理
}
}
}
app.use(static(path.resolve(__dirname, 'public')));
app.use(router.routes());
app.listen(3000);
复制代码
以ejs模板为例,假设要渲染的模板是:
//template.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<%arr.forEach(a=>{%>
<li><%=a%></li>
<%})%>
</body>
</html>
复制代码
渲染页面的逻辑:
const Koa = require('koa');
const path = require('path');
const Router = require('koa-router')
const views = require('koa-views');
let app = new Koa();
let router = new Router();
app.use(views(path.resolve(__dirname), {
//不设置的话,模板文件要使用.ejs后缀而不是.htmls后缀
map: { html: 'ejs' }
}));
router.get('/',async (ctx,next)=> {
await ctx.render('template.html',{arr:[1,2,3]})
})
app.listen(3000);
复制代码
koa中会设置采用渲染模板的方式,一般会采用ejs模板引擎渲染页面:
//简化渲染模板便于理解,去掉其他标签,真实渲染时,这些标签时存在的
<body>
<%arr.forEach(a=>{%>
<li><%=a%></li>
<%})%>
</body>
复制代码
ejs中的render函数,简化版本:
function render(r, obj) {
let head = `let str=''\r\n`;
//with可以将变量的上下文指向为obj,所以a => obj.a
head += 'with(b){\r\n'
let content = 'str+=`'
//先将匹配<%=xx%>将其变成${xx}
r = r.replace(/<%=([\s\S]*?)%>/g, function () {
return '${' + arguments[1] + '}'
});
//匹配<%xxxx%>将xxxx中的内容拼接起来变成一个函数主要逻辑
content += r.replace(/<%([\s\S]*?)%>/g, function () {
return '`\r\n' + arguments[1] + "\r\n str+=`"
});
let tail = "`\r\n} \r\n return str";
let fnStr = head + content + tail;
let fn = new Function('b', fnStr)
return fn(obj);
}
//fn= function(b){
// let str='';
// with(b){
// str+='<body>';
// b.arr.forEach(a=>{str += '<li>${a}</li>'});
// str += '</body>';
// }
// return str
//}
复制代码
function views(p,opts) {
return async(ctx,next)=>{
function render(r, obj) {
let head = `let str=''\r\n`;
head += 'with(b){\r\n'
let content = 'str+=`'
r = r.replace(/<%=([\s\S]*?)%>/g, function () {
return '${' + arguments[1] + '}'
});
content += r.replace(/<%([\s\S]*?)%>/g, function () {
return '`\r\n' + arguments[1] + "\r\n str+=`"
});
let tail = "`\r\n} \r\n return str";
let fnStr = head + content + tail;
let fn = new Function('b', fnStr)
return fn(obj);
}
//在ctx上挂在render函数,读取文件,然后渲染
ctx.render = async (filename,obj) => {
let realPath = path.join(p,filename);
let { promisify} = require('util');
let fs = require('fs');
let read = promisify(fs.readFile); //promise化
let r = await read(realPath,'utf8');
ctx.body = render(r, obj);
}
return next();
}
}
module.exports = views;
复制代码
渲染页面的逻辑:
const Koa = require('koa');
const path = require('path');
const Router = require('koa-router')
const views = require('koa-views');
let app = new Koa();
let router = new Router();
app.use(views(path.resolve(__dirname), {
map: { html: 'ejs' }
}));
router.get('/',async (ctx,next)=> {
await ctx.render('template.html',{arr:[1,2,3]})
})
app.listen(3000);
复制代码
koa-router中间件的原理基本就介绍完了,后面一起学习kao的其他中间件:
作者:梦想攻城狮
链接:https://juejin.im/post/5baefe335188255c6a0449c8
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。