koa框架會用也會寫—(koa-view、koa-static)

Koa中經常使用的中間件:

  • koa-session:讓無狀態的http擁有狀態,基於cookie實現的後臺保存信息的session
  • koa-mysql:封裝了須要用到的SQL語句
  • koa-mysql-session:當不想讓session存儲到內存,而想讓session存儲到mysql數據庫中時使用
  • koa-router:後臺會接受到各類請求的url,路由會根據不一樣的url來使用不一樣的處理邏輯。
  • koa-view:請求html頁面時,後臺會用模板引擎渲染數據到模板上,而後返回給後臺
  • koa-static:請求img、js、css等文件時,不須要其餘邏輯,只須要讀取文件
  • koa-better-body:post上傳文件時,解析請求體

koa系列文章:

靜態資源和動態資源

在網絡請求中,請求每每分紅兩種類型,一種是靜態資源,直接從服務器的文件存儲中讀取,一種是動態資源,通常須要先從數據庫獲取數據,而後通過必定的處理,最後返回給客戶端。css

  • koa-static:用來處理靜態資源的訪問,由於它不涉及其餘的處理過程,只是單純的讀取文件,因此單獨抽離出來。
  • koa-view是用來將數據和模板結合渲染html頁面時採用的,渲染模板的邏輯都市同樣的,因此也單獨抽離出來。

koa-static

  • 判斷請求的文件是否存在,若是存在讀取文件返回
  • 若是請求的文件不存在,則默認查看當前文件下是否有index.html,若是存在返回當前文件下的index.html

根據上面的思想,因此實現簡單版的static,能夠將static單獨存在一個js文件按中,而後require進來,這樣使用和koa同樣:html

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);
複製代碼

koa-view

koa-view使用

以ejs模板爲例,假設要渲染的模板是:mysql

//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>
複製代碼

渲染頁面的邏輯:sql

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);
複製代碼

ejs渲染的原理

koa中會設置採用渲染模板的方式,通常會採用ejs模板引擎渲染頁面:數據庫

  • 匹配<%=xx%>將其變成${xx}
  • 匹配<%xxxx%>將xxxx中的內容拼接起來變成一個函數字符串
  • 而後經過new Function函數字符串生成一個函數執行數據就會返回渲染後的字符串
//簡化渲染模板便於理解,去掉其餘標籤,真實渲染時,這些標籤時存在的
<body>
  <%arr.forEach(a=>{%>
    <li><%=a%></li>
  <%})%>
</body>
複製代碼

ejs中的render函數,簡化版本:promise

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
//}  
複製代碼

koa-view的原理

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;
複製代碼

渲染頁面的邏輯:bash

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的其餘中間件:服務器

相關文章
相關標籤/搜索