34 # 模板引擎的实现原理

简介: 34 # 模板引擎的实现原理

模板引擎的实现原理:(with 语法 + 字符串拼接 + new Function 来实现)

下面实现自定义的模板引擎,新建一个模板

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    {{name}}{{age}}
</body>
</html>

下面实现模板引擎

const fs = require("fs");
const path = require("path");
const kaimoRenderFile = (filePath, obj, cb) => {
    fs.readFile(filePath, "utf-8", (err, html) => {
        if (err) {
            return cb(err, html);
        }
        // 正则匹配 `{{}}` 里面的任意字符不是大括号就行至少一个
        // 分组是用圆括号“()”括起来的正则表达式,匹配出的内容就表示一个分组。
        html = html.replace(/\{\{([^}]+)\}\}/g, function () {
            // arguments[0]:就是匹配到的原字符串,arguments[1]:就是第一个园括号
            let key = arguments[1].trim();
            return obj[key];
        });
        cb(err, html);
    });
};
kaimoRenderFile(
    path.resolve(__dirname, "../file/kaimo-template.html"),
    { name: "kaimo", age: "313", arr: [1, 2, 3] },
    (err, data) => {
        console.log(data);
    }
);

然后处理下面这种模板

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    {{name}}
    {{age}}
    {%arr.forEach(item => {%}
    <li>{{item}}</li>
    {%})%}
</body>
</html>

需要把字符串 {%%} 替换调,并且拼出一个结果的字符串,new Function 的方式变为函数,利用 with 解决作用域问题

大致处理成这样

function anonymous(obj) {
    let str = "";
    with (obj) {
        str += `<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        ${name}
        ${age}
        `;
        arr.forEach((item) => {
            str += ` 
        <li>${item}</li>
        `;
        });
        str += ` 
    </body>
    </html>`;
    }
    return str;
}

实现如下:

const fs = require("fs");
const path = require("path");
const kaimoRenderFile = (filePath, obj, cb) => {
    fs.readFile(filePath, "utf-8", (err, html) => {
        if (err) {
            return cb(err, html);
        }
        // 正则匹配 `{{}}` 里面的任意字符不是大括号就行至少一个
        // 分组是用圆括号“()”括起来的正则表达式,匹配出的内容就表示一个分组。
        html = html.replace(/\{\{([^}]+)\}\}/g, function () {
            // arguments[0]:就是匹配到的原字符串,arguments[1]:就是第一个园括号
            let key = arguments[1].trim();
            // 这里将 {{name}} 处理成 ${name}
            return "${" + key + "}";
        });
        // 正则匹配 `{{}}` 里面的任意字符不是百分号就行至少一个
        let head = `let str = '';\r\n with(obj){ \r\n`;
        head += "str+=`";
        html = html.replace(/\{\%([^%]+)\%\}/g, function () {
            return "`\r\n" + arguments[1] + "\r\n str+=` \r\n";
        });
        let tail = "`} \r\n return str;";
        console.log(head + html + tail);
        // 参数是obj,函数体是 head + html + tail
        let fn = new Function("obj", head + html + tail);
        console.log(fn.toString());
        cb(err, fn(obj));
    });
};
kaimoRenderFile(
    path.resolve(__dirname, "../file/kaimo-template2.html"),
    { name: "kaimo", age: "313", arr: [1, 2, 3] },
    (err, data) => {
        console.log(data);
    }
);

目录
相关文章
|
移动开发 小程序 JavaScript
【小程序质量提优解决方案】(二)内嵌H5加载异常(404)
【小程序质量提优解决方案】(二)内嵌H5加载异常(404)
530 0
|
前端开发
前端base64转Blob,Blob转文件下载
前端将base64字符串转换为Blob对象,再将Blob对象转换为文件并实现下载。包括处理数据URL和纯base64字符串的情况,并提供了一个辅助函数用于转换。
397 2
|
存储 分布式计算 物联网
Apache IoTDB进行IoT相关开发实践
当今社会,物联网技术的发展带来了许多繁琐的挑战,尤其是在数据库管理系统领域,比如实时整合海量数据、处理流中的事件以及处理数据的安全性。例如,应用于智能城市的基于物联网的交通传感器可以实时生成大量的交通数据。据估计,未来5年,物联网设备的数量将达数万亿。物联网产生大量的数据,包括流数据、时间序列数据、RFID数据、传感数据等。要有效地管理这些数据,就需要使用数据库。数据库在充分处理物联网数据方面扮演着非常重要的角色。因此,适当的数据库与适当的平台同等重要。由于物联网在世界上不同的环境中运行,选择合适的数据库变得非常重要。 原创文字,IoTDB 社区可进行使用与传播 一、什么是IoTDB 我
542 9
Apache IoTDB进行IoT相关开发实践
|
存储 缓存 网络协议
MAC协议原理与ARP协议
总结一下,MAC协议是控制同一网络媒介上多个设备的数据访问的规范,而ARP是解决局域网络中的IP地址到MAC地址的转换问题,以确保IP包能在本地网络上传输到正确的设备。尽管这两种协议服务于网络通信中的不同层面,但它们都是网络正常操作的基本要素,保证了数据能从一个设备准确无误地传递到另一个设备。
442 0
|
存储 数据处理 C++
内存 vs 硬盘:固态硬盘代替内存可以工作吗?
内存 vs 硬盘:固态硬盘代替内存可以工作吗?
550 2
|
存储 缓存 调度
FFmpeg开发笔记(十九)FFmpeg开启两个线程分别解码音视频
《FFmpeg开发实战》第10章示例playsync.c在处理音频流和视频流交错的文件时能实现同步播放,但对于分开存储的格式,会出现先播放全部声音再快速播放视频的问题。为解决此问题,需改造程序,增加音频处理线程和队列,以及相关锁,先将音视频帧读入缓存,再按时间戳播放。改造包括声明新变量、初始化线程和锁、修改数据包处理方式等。代码修改后在playsync2.c中,编译运行成功,控制台显示日志,SDL窗口播放视频并同步音频,证明改造有效。
394 0
FFmpeg开发笔记(十九)FFmpeg开启两个线程分别解码音视频
putty配色方案
putty默认的配色方案简直毫无人道主义可言,所以找了个,好多了,转载自https://darekkay.com/2015/03/21/my-putty-color-scheme/ 预览效果 ; Default Fo...
3657 0
|
XML 存储 安全
【KVM虚拟化】· 命令行KVM安装linux
【KVM虚拟化】· 命令行KVM安装linux
534 0