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);
    }
);

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/kaimo313/article/details/131179909
34