返回

亲手构建 EJS 模板引擎:一次代码之旅

前端

导语:

在软件开发的世界中,模版引擎扮演着至关重要的角色,使我们能够轻松地将数据与视图分离开来,从而创建动态的、可维护的 Web 应用程序。EJS(嵌入式 JavaScript)模板引擎是这项工作中的佼佼者,它以其简单性、灵活性和强大性而闻名。今天,我们将踏上构建自己的 EJS 模板引擎的旅程,揭开其幕后的奥秘。

准备工作:

要踏上我们的代码之旅,你需要一个现代的 JavaScript 运行时环境,例如 Node.js。你还需要熟悉 JavaScript 的基础知识,以及对正则表达式有一些了解。

第一步:解析模板

EJS 模板引擎的核心功能之一是能够解析包含 EJS 语法的模板。我们首先从一个简单的正则表达式开始,它可以识别 EJS 表达式:

const EJS_EXPR_RE = /<%-?\s*(.+?)\s*%>/g;

此正则表达式将匹配所有 EJS 表达式,无论是输出表达式(<%= ... %>)还是执行表达式(<% ... %>)。

第二步:编译模板

解析模板后,下一步是编译它,生成 JavaScript 代码,该代码可以动态呈现数据。我们为此创建一个 compile 函数:

function compile(template) {
  return template.replace(EJS_EXPR_RE, (match, expression) => {
    if (expression.startsWith('-')) {
      return `"${expression.slice(1).trim()}"`;
    } else {
      return `_.escape(${expression})`;
    }
  });
}

此函数遍历模板,并用 JavaScript 代码替换每个 EJS 表达式。对于输出表达式,它简单地将其包装在引号中。对于执行表达式,它使用 Lodash 的 _.escape 函数对结果进行转义,以防止 XSS 攻击。

第三步:渲染模板

有了编译后的模板,我们现在可以使用它来呈现数据。为此,我们创建一个 render 函数:

function render(template, data) {
  const compiledTemplate = compile(template);
  const functionBody = `
    with (data) {
      return ${compiledTemplate};
    }
  `;

  // 使用 Function 构造函数动态创建并调用渲染函数
  const renderFunction = new Function('_', functionBody);
  return renderFunction(data);
}

render 函数首先编译模板,然后使用 Function 构造函数动态创建并调用渲染函数。with 语句将数据对象作为函数作用域中的当前对象,使模板可以访问数据。

第四步:使用模板引擎

我们现在可以将我们的 EJS 模板引擎用于实际应用程序。以下示例演示如何使用它呈现一个简单的问候消息:

const template = "<%= message %>";
const data = { message: "你好,世界!" };
const output = render(template, data);
console.log(output); // 输出:你好,世界!

结束语:

构建自己的 EJS 模板引擎是一次有益的旅程,它使我们深入了解了模板引擎背后的技术。虽然我们的实现是基础性的,但它为进一步扩展和定制奠定了坚实的基础。通过自定义我们的模板引擎,我们可以根据应用程序的特定需求对其进行优化,创造更加强大的 Web 应用程序。