上上一期链接——也就是本文的基础,参考KOA,5步手写一款粗糙的web框架

上一期链接——有关Router的实现思路,这份Koa的简易Router手敲指南请收下

本文参考仓库:点我

上一期科普了Router,我们可以为每一张页面配置一个路由,但是我们不可能每个router.get(path,(ctx,next)=>{ctx.body=...})都直接写html,这样代码也太难维护了。于是出现了模版这个东西,模版主要是用来管理页面的。每一个html都放入一个单独的文件中,这样无论是调用还是复用都很方便。这里我用了ejs的语法,来写这个模版引擎的中间件。

那么,我们从最简单的静态页面开始吧~

STEP 1 静态页面调用

调用文件不是一件难事,只需要读取,然后赋值给ctx.body即可:

  1. const fs=require("fs")
  2. const path=require("path")
  3. let indexTPL=fs.readFileSync(path.join(__dirname,"/pages/template.ejs"),"utf-8")
  4. ctx.body=indexTPL;

这里我先以逻辑为主,所以我用了readFileSync这个同步方法,而没有用异步读取的方法。

STEP 2 封装一个中间件View

这里,我们新创建一个名为View中间件,专门用于模板嵌套。

  1. const fs=require("fs")
  2. const path=require("path")
  3. function View(path){
  4. let tpl="";
  5. return async (ctx,next)=>{
  6. tpl = fs.readFileSync(path.join(__dirname,path),"utf-8")
  7. ctx.body= tpl
  8. await next();
  9. }
  10. }

然后我们就可以直接在项目中应用这个中间件了。

  1. let view=require("./Views")
  2. let router=new Router()
  3. router.get("/",view("/pages/template.ejs"))

或者

  1. app.use(view("/pages/template.ejs"))

都是可行的,因为我创建的是标准的中间件啊~

STEP 3 提取模板标签

我们为什么要用模板!当然是为了动态页啊!所以我们需要替换模板标签<%=参数名%>为我们需要值。同时模板也需要支持一些函数,比如数组循环填充列表。

那么第一步,我们需要的就是将这个标签提取出来,然后替换成我们特有的标签<!--operator 1-->这个可以自定义一个特别的标签用于占位符。

大家没听错,提取,替换!所以正则表达式是躲不过了,他已经在虐我的路上了……

因为单纯的赋值和执行函数差别比较大,所以我把他们分开识别。如果大家有更好的方法,记得推荐给我。(正则渣渣瑟瑟发抖)

  1. let allTags=[];
  2. function getTags(){
  3. //先取出需要执行的函数,也就是不带"="的一对标签,放入数组,并且,将执行函数这一块替换成占位符。
  4. let operators = tpl.match(/<%(?!=)([\s\S]*?)%>([\s\S]*?)<%(?!=)([\s\S]*?)%>/ig)||[]
  5. operators.forEach((element,index )=> {
  6. tpl=tpl.replace(element,`<!--operator ${index}-->`)
  7. });
  8. //再取出含有“=”的专门的赋值标签,怕和执行函数中的赋值标签搞混,所以这边我分开执行了
  9. let tags=tpl.match(/<%=([\s\S]*?)%>/ig)||[]
  10. tags.forEach((element,index) => {
  11. tpl=tpl.replace(element,`<!--operator ${index+operators.length}-->`)
  12. });
  13. //给我一个整套的待替换数组
  14. allTags=[...operators,...tags];
  15. }

STEP 4 替换模板标签

重头戏来了,现在我们要进行模板替换了,要换成我们传入的值。这里需要注意的就是我们将allTags逐个替换成可执行的js文本,然后执行js,生成的字符串暂存于数组之中。等执行完毕,再将之前的<!--operator 1-->占位符替换掉。

这里需要注意的是,我们先把赋值的标签<%=%>去除,变成${},就像下方这样:

  1. let str="let tmpl=`<p>字符串模板:${test}</p>
  2. <ul>
  3. <li>for循环</li>
  4. <% for(let user of users){ %>
  5. <li>${user}</li>
  6. <% } %>
  7. </ul>`
  8. return tmpl"

然后我们再把可执行的函数的<%%>去除,首尾加上```闭合字符串,就像下方这样:

  1. let str="let tmpl=`<p>字符串模板:${test}</p>
  2. <ul>
  3. <li>for循环</li>`
  4. for(let user of users){
  5. tmpl+=`<li>${user}</li>`
  6. }
  7. `</ul>`
  8. return tmpl"

但是这是字符串啊,这个时候我们要借助一个方法Function 构造函数

我们可以new一个Function,然后将字符串变成可以执行的js。

Function的语法是这样的new Function ([arg1[, arg2[, ...argN]],] functionBody),再字符串之前可以声明无数个参数,那么我们就借助...三个帮我们把Object变成单个参数放进去就可以了。

举个例子:

  1. let data={
  2. test:"admin",
  3. users:[1,2,3]
  4. }

上方对象,我们用Object.keys(data),提取字段名,然后利用三点扩展运算符...,变成test,users

  1. new Function(...Object.keys(data),方法字符串)

也就等同于

  1. new Function(test,users,方法字符串)

我们合并下上方的字符串,这个可执行的模板js就是这样的,怎么样是不是好理解了?

  1. function xxx(test,users){
  2. let tmpl=`<p>字符串模板:${test}</p>
  3. <ul>
  4. <li>for循环</li>`
  5. for(let user of users){
  6. tmpl+=`<li>${user}</li>`
  7. }
  8. `</ul>`
  9. return tmpl;
  10. }

感觉要变成可执行的js,原理不难,就是拼合起来很复杂。

下方是完整的执行代码:

  1. function render(){
  2. //获取标签
  3. getTags();
  4. //开始组合每个标签中的内容,然后将文本变成可执行的js
  5. allTags=allTags.map((e,i)=>{
  6. let str = `let tmpl=''\r\n`;
  7. str += 'tmpl+=`\r\n';
  8. str += e
  9. //先替换赋值标签
  10. str = str.replace(/<%=([\s\S]*?)%>/ig,function () {
  11. return '${'+arguments[1]+'}'
  12. })
  13. //再替换函数方法,记得别忘了首位的"`"这个闭合标签
  14. str = str.replace(/<%([\s\S]*?)%>/ig,function () {
  15. return '`\r\n'+arguments[1] +"\r\ntmpl+=`"
  16. })
  17. str += '`\r\n return tmpl';
  18. //提取object的key值,用于function的参数
  19. let keys=Object.keys(data);
  20. let fnStr = new Function(...keys,str);
  21. return fnStr(...keys.map((k)=>data[k]));
  22. })
  23. allTags.forEach((element,index )=> {
  24. tpl=tpl.replace(`<!--operator ${index}-->`,element)
  25. });
  26. }

STEP + 如果想用异步的方式读取文件,我推荐:

readFile变成一个Promise,然后放入中间件中await一下,这样就可以实现异步了~

如果不了解async/await,科普传送门

  1. const util=require("util")
  2. const fs=require("fs")
  3. const path=require("path")
  4. let readFile=util.promisify(fs.readFile)
  5. function view(p,data){
  6. let tpl="";
  7. let allTags=[];
  8. function getTags(){
  9. //略
  10. }
  11. function render(){
  12. //略
  13. }
  14. return async (ctx,next)=>{
  15. tpl = await readFile(path.join(__dirname,p),"utf-8")
  16. //别忘了运行render(),替换模板标签
  17. render();
  18. ctx.body=tpl;
  19. await next();
  20. }
  21. }

KOA的简易模板引擎实现方式的更多相关文章

  1. php 实现简易模板引擎

    1.MVC简介 MVC 是一种使用 MVC(Model View Controller 模型-视图-控制器)设计创建 Web 应用程序的模式(详情自己百度): 1. Model(模型)表示应用程序核心 ...

  2. js 简易模板引擎 , 持续更新。。。

    <script> var _mytpl = (function(){ var _verson = 1.0; return { _data:{}, load:function(html,da ...

  3. koa art-template模板引擎的使用

    art-template 模板引擎介绍 art-template 是一个简约.超快的模板引擎. 它采用作用域预声明的技术来优化模板渲染速度,从而获得接近 JavaScript 极限的运行 性能,并且同 ...

  4. koa 基础(十三)koa-art-template 模板引擎的使用

    1.项目目录 2.app.js /** * http://aui.github.io/art-template/koa/ * 1.npm install --save art-template * n ...

  5. MVC开发模式以及Smarty模板引擎的使用

    Linux 全局安装 composer 将目录切换到/usr/local/bin/目录 cd /usr/local/bin/ 在 bin 目录中下载 composer curl -sS https:/ ...

  6. PHP实现简易的模板引擎

    PHP实现简易的模板引擎 1.MVC简介 MVC 是一种使用 MVC(Model View Controller 模型-视图-控制器)设计创建 Web 应用程序的模式(详情自己百度): 1. Mode ...

  7. 简易js模板引擎

    前面 js 模板引擎有很多很多,我以前经常用 art-template ,有时候也会拿 vue 来当模板引擎用. 直到...... 年初的时候,我还在上个项目组,那时候代码规范是未经允许不能使用 [外 ...

  8. Thymeleaf模板引擎+Spring整合使用方式的介绍

    尊重原创,原文地址为:https://www.cnblogs.com/jiangchao226/p/5937458.html 前言 这个教程介绍了Thymeleaf与Spring框架的集成,特别是Sp ...

  9. 3、KOA模板引擎+访问静态资料中间件

    一.Koa模板引擎初识1.安装中间件 : npm i --save koa-views2.安装ejs模板引擎 :npm i --save ejs3.编写模板:<%= title %> 是调 ...

随机推荐

  1. 在不同的系统中的virtualbox中安装Ubuntu SDK

    对非常多的开发人员来说.你们可能使用的不是Ubuntu操作系统.在这样的情况下,开发人员须要在自己的操作系统中(OS X及Windows)安装virtualbox,并在VirtualBox中安装Ubu ...

  2. https://security.stackexchange.com/questions/68405/what-is-tmunblock-cgi-and-can-it-be-exploited-by-shellshock-linux-apache-w

    hndUnblock.cgi   Line #1124 : 187.38.233.45 - - [15/Jan/2018:21:36:45 +0800] "GET /hndUnblock.c ...

  3. [Codeforces 1037D] Valid BFS?

    [题目链接] http://codeforces.com/problemset/problem/1037/D [算法] 首先求出每个点的父节点 , 每棵子树的大小 然后判断BFS序是否合法即可 时间复 ...

  4. POJ2686 Traveling by Stagecoach 状态压缩DP

    POJ2686 比较简单的 状态压缩DP 注意DP方程转移时,新的状态必然数值上小于当前状态,故最外层循环为状态从大到小即可. #include <cstdio> #include < ...

  5. JS浮点数精度运算

    一般来讲,我们在项目中必不可少的需要进行各种数值的计算,但是这种计算全部放在服务端会给服务器带来很大的压力,所以势必要客户端来 分担一些计算的压力. 从客户端来说,JavaScript是一门弱类型语言 ...

  6. DHTML_____document对象的方法

    <html> <head> <meta charset="utf-8"> <title>document对象的方法</titl ...

  7. php 5.6 版本配置 oracle ddl

    1. Windows版PHP内置了Oracle驱动,在ext目录下:php_oci8.dllphp_oci8_11g.dllphp_pdo_oci.dllLinux上如果自己编译的话则添加下面的con ...

  8. python程序展现图片

    突然想写一个python程序能够显示图片的 ,展示文字的已经实现了 现在就搞一搞这个吧 相信也是很简单 首先是放一张图片在e盘下面 等会程序打包的时候将会用到 就决定是你啦 皮卡丘: 然后就写代码吧:

  9. msxml3.dll 错误 '800c0005' 系统错误: -2146697211。

    asp网站 因为这个问题,困扰自己好多次,还重装过两次服务器系统,非常的麻烦,这次终于找到了问题所在,记录下来,方便以后查看. 服务器症状: 1.服务器上的IE浏览器不能访问外网: 2.set htt ...

  10. Java 8 (9) Optional取代null

    NullPointerException,大家应该都见过.这是Tony Hoare在设计ALGOL W语言时提出的null引用的想法,他的设计初衷是想通过编译器的自动检测机制,确保所有使用引用的地方都 ...