详解nodejs模板引擎制作

详解Node.js模板引擎制作

什么是模板引擎

模板引擎是一种将数据和模板文本结合起来产生新文本的工具。模板引擎允许我们使用模板文本生成我们需要的HTML、XML、JSON等格式的文本。互联网浏览器解析HTML是一件非常耗费性能的事情,而且HTML中可以嵌入静态资源、样式、脚本等,模板引擎可以将大量的相同或类似的内容进行复用,让前端渲染部分变得更加灵活和高效。

模板引擎的运作方式

由于模板引擎是将数据和模板文本结合起来生成新文本,所以在使用模板引擎的时候,也就涉及了模板的编写和数据的填充。通常情况下,模板文本会在服务器端预处理后返回给客户端,而相应的数据会以某种格式进行传输,如JSON、XML等。前端JS则可以解析这些数据并使用对应的模板进行渲染。

常用的模板引擎有EJS、Handlebars、Pug等。

制作模板引擎

在开始制作模板引擎之前,需要确定模板文本和数据的表达方式。这里我们使用JSON格式的数据和类似Handlebars的模板语法来实现一个简单的模板引擎。

需求分析

我们需要实现一个可以将模板文本生成HTML标记语言的模板引擎,可以自由定义变量、条件语句、循环语句等。示例模板如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>{{title}}</title>
</head>
<body>
  <h1>{{title}}</h1>
  {{#if body}}
  <div class="body">{{body}}</div>
  {{else}}
  <div class="no-body">No Body Detected</div>
  {{/if}}
  <ul>
  {{#each items}}
    <li>{{this}}</li>
  {{/each}}
  </ul>
</body>
</html>

实现步骤

步骤1:模板文本解析

实现模板文本解析,将类似{{title}}{{#if body}}...{{else}}...{{/if}}{{#each items}}...{{/each}}的标记解析出来。具体实现如下:

function parser(source) {
  const REGEXP_VARIABLE = /{{(.*?)}}/g; //变量
  const REGEXP_IF = /{{#if (.*?)}}([\s\S]*?){{\/if}}/g; //条件语句
  const REGEXP_ELSE = /{{#else}}([\s\S]*?){{\/if}}/g; //否定语句
  const REGEXP_EACH = /{{#each (.*?)}}([\s\S]*?){{\/each}}/g; //循环语句

  let result = source;
  result = result.replace(REGEXP_IF, (match, condition, ifTrue) => {
    const ifFalse = REGEXP_ELSE.test(ifTrue) ? REGEXP_ELSE.exec(ifTrue)[1] : '';
    return `\`;\nif (${condition}) {\ncontent += \`${ifTrue}\`;\n} else {\ncontent += \`${ifFalse}\`;\n}\ncontent += \``;
  });
  result = result.replace(REGEXP_EACH, (match, items, content) => {
    return `\`;\nfor (let i = 0; i < ${items}.length; i++) {\nconst thisData = ${items}[i];\n${content}\n}\ncontent += \``;
  });
  result = result.replace(REGEXP_VARIABLE, (match, variable) => {
    return `\${thisData.${variable}}`;
  });

  result = 'let content = `\n' + result + '\n`;';
  result += '\nreturn content;';
  return result;
}

这里定义了三个正则表达式和一个parser函数。REGEXP_VARIABLE用于匹配变量,如{{title}}(.*?)表示非贪婪模式匹配。REGEXP_IF用于匹配条件语句,如{{#if body}}...{{else}}...{{/if}},其中([\s\S]*?)用于匹配包含换行符在内的任意字符,{{\/if}}用于匹配条件语句的结束标记。REGEXP_ELSE用于匹配否定语句,如{{#else}}...{{/if}}REGEXP_EACH用于匹配循环语句,如{{#each items}}...{{/each}}

parser函数将以上四个正则表达式结合起来实现了模板文本的解析。解析过程中,通过字符串拼接的方式生成JS代码字符串。

步骤2:模板参数生成

模板函数接收两个参数:数据和选项。数据表示模板所需的数据,选项则表示模板文本和编译之后的函数等信息。在这里,我们将解析后的JS代码字符串保存在选项的render属性中。

function compile(template) { //返回的是编译后的函数
  const render = new Function('data', `
    ${parser(template)}
  `);
  return function(data) {
    const content = render.call(data);
    return content;
  };
}

此处使用了Function构造函数将JS代码字符串转换为函数形式。render函数的实现依赖于parser函数生成的JS代码。compile函数返回的是编译后的函数。

步骤3:制作模板引擎

我们可以将compile函数封装为一个模板引擎,以便更好地使用。这里我们将模板引擎命名为CETemplate,封装的方法包括编译模板和渲染数据等。

class CETemplate { //模板引擎
  constructor() {
    this.templates = {}; //已经编译过的模板列表
  }

  compile(name, template) { //编译模板
    const compiled = compile(template);
    this.templates[name] = compiled;
    return compiled;
  }

  render(name, data) { //渲染数据
    const template = this.templates[name];
    if (!template) {
      throw new Error(`Template ${name} not found.`);
    } else {
      return template(data);
    }
  }
}

此处使用了Class关键字定义一个模板引擎类,包含了编译模板和渲染数据两个方法,以及一个保存已编译模板的templates属性。

示例说明

下面演示如何使用我们制作的模板引擎生成HTML页面。

const ce = new CETemplate();
const template = `
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>{{title}}</title>
</head>
<body>
  <h1>{{title}}</h1>
  {{#if body}}
  <div class="body">{{body}}</div>
  {{else}}
  <div class="no-body">No Body Detected</div>
  {{/if}}
  <ul>
  {{#each items}}
    <li>{{this}}</li>
  {{/each}}
  </ul>
</body>
</html>
`;
const compiled = ce.compile('template', template);
const data = {
  title: 'CE Template Engine',
  body: 'Hello, World!',
  items: ['Item 1', 'Item 2']
};
const content = ce.render('template', data);
console.log(content);

以上代码中,我们首先创建了一个模板引擎实例ce。然后,定义了一个HTML模板,使用compile将其编译,并将编译后的函数保存到ce实例中。接着,我们定义了一个数据对象并将其传递给render函数,最后打印输出渲染后的HTML代码。

再看一个更加复杂的示例,包含了不同类型的标记。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>{{title}}</title>
</head>
<body>
  <header>
    <nav>
      <ul>
        {{#each menu}}
        <li{{#if (isActive this.url)}} class="active"{{/if}}><a href="{{this.url}}">{{this.text}}</a></li>
        {{/each}}
      </ul>
    </nav>
  </header>
  <h1>{{title}}</h1>
  <div>{{content}}</div>
  {{#if isShow}}
  <div class="notice">{{notice}}</div>
  {{/if}}
  <footer>
    <p>&copy; {{year}} {{owner}}</p>
  </footer>
</body>
</html>
const ce = new CETemplate();
const template = `
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>{{title}}</title>
</head>
<body>
  <header>
    <nav>
      <ul>
        {{#each menu}}
        <li{{#if (isActive this.url)}} class="active"{{/if}}><a href="{{this.url}}">{{this.text}}</a></li>
        {{/each}}
      </ul>
    </nav>
  </header>
  <h1>{{title}}</h1>
  <div>{{content}}</div>
  {{#if isShow}}
  <div class="notice">{{notice}}</div>
  {{/if}}
  <footer>
    <p>&copy; {{year}} {{owner}}</p>
  </footer>
</body>
</html>
`;
const compiled = ce.compile('template', template);
const data = {
  title: 'CE Template Engine',
  menu: [
    { url: '/', text: 'Home' },
    { url: '/about', text: 'About' },
    { url: '/contact', text: 'Contact' },
  ],
  content: 'Hello, World!',
  isShow: true,
  notice: 'This is a notice.',
  year: 2021,
  owner: 'My Company'
};
const content = ce.render('template', data);
console.log(content);

对比两个示例,我们可以发现,使用模板引擎可以方便地生成HTML页面。虽然本文的示例比较简单,但是模板引擎的应用非常广泛,对于高效开发和优化性能都有很大的帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解nodejs模板引擎制作 - Python技术站

(0)
上一篇 2023年6月8日
下一篇 2023年6月8日

相关文章

  • Node.js进程管理之Process模块详解

    Node.js进程管理之Process模块详解 概述 在Node.js中,可以使用Process模块来管理进程,比如获取进程信息、设置环境变量、杀死进程等等。本文将详细讲解Process模块的使用方法。 获取进程信息 可以使用Process模块中的一些方法来获取当前进程的信息,如下所示: console.log(process.pid); // 获取进程ID…

    node js 2023年6月8日
    00
  • vue3.0报错Cannot find module‘worker_threads‘的解决办法

    下面是关于“vue3.0报错Cannot find module ‘worker_threads‘的解决办法”的完整攻略。 问题分析 “Cannot find module ‘worker_threads‘”错误通常会在使用 Vue.js 3.0 的时候出现。这是由于开发者在使用一些较新的 Node.js 版本时没有看到 IVue3 正在使用的 worker…

    node js 2023年6月8日
    00
  • node.js中的path.sep方法使用说明

    当我们在使用Node.js编写程序时,常常需要使用文件路径,而在不同操作系统中,文件路径的表现形式是不同的,比如在Windows下,文件路径使用的是\作为分隔符,而在Linux或Mac OS上使用的是/作为分隔符。为了解决这个问题,Node.js提供了path模块,其中的sep方法可以返回当前操作系统使用的文件路径分隔符。 使用说明 在使用path.sep方…

    node js 2023年6月8日
    00
  • Nodejs中的JWT和Session的使用

    首先我们需要明确JWT和Session的概念。JWT(JSON Web Token)是一种用于身份验证的标准,它可以在用户和服务器之间传递信息并进行验证。Session则是一种服务器端的会话技术,用于记录用户的登录状态。 Node.js是一个非常适合处理用户请求和后端逻辑的语言,因此我们可以使用Node.js来实现JWT和Session的使用。 以下是Nod…

    node js 2023年6月8日
    00
  • javascript设计模式 – 迭代器模式原理与用法实例分析

    JavaScript设计模式 – 迭代器模式原理与用法实例分析 迭代器模式通常被用于遍历数据结构。该模式提供了一种自定义遍历的方式,同时屏蔽了底层数据结构的实现细节。在 JavaScript 中,迭代器模式通常被应用于处理数组和类似数据结构的数据。在本文中,我们将会深入讲解迭代器模式的原理,并结合两个实际例子帮助你更好的理解。 迭代器模式的原理 在 Java…

    node js 2023年6月8日
    00
  • vue mvvm数据响应实现

    Vue是一款流行的前端框架,其中的MVVM设计模式实现了数据的响应式更新。在Vue中,当数据发生变化时,视图会自动更新,反之亦然。下面是“Vue MVVM数据响应实现”的攻略: 1. 数据响应式设计 Vue中实现数据响应式的核心概念是“侦听器”,其通过Object.defineProperty()方法或ES6 Proxy API(更高版本的Vue中采用的方法…

    node js 2023年6月8日
    00
  • Sea.JS知识总结

    Sea.JS知识总结 什么是Sea.JS? Sea.JS是一个遵循CMD规范的JavaScript模块加载器,可以实现模块的异步加载、依赖管理等功能,可以使得JavaScript算法复杂的应用变得更具可维护性和清晰可见性。 Sea.JS特点 遵循CMD规范,模块的代码放在单独的文件中,在需要的时候动态加载,使得代码更为模块化、复用性更好、依赖性管理更为清晰。…

    node js 2023年6月8日
    00
  • node.js 基于 STMP 协议和 EWS 协议发送邮件

    Node.js 是一种基于事件驱动和非阻塞 I/O 模型的 JavaScript 运行时环境,广泛应用于服务器端应用程序的开发。基于 STMP 协议和 EWS 协议的邮件发送是 Node.js 程序中一项常见的任务。下面是一份完整的攻略,包含邮件发送的各个步骤和两个示例说明。 准备工作 在进行邮件发送前,需要安装以下 npm 模块: nodemailer:用…

    node js 2023年6月8日
    00
合作推广
合作推广
分享本页
返回顶部