EJS[0]-如何使用EJS

最近做的一个新项目,所以想着换一个新的模版引擎尝试一下。(之前我们一直在使用handlebars

本次源码分析所使用的是TJ大神开发的1.x版本
当然现在该项目已经停止维护了,目前正在维护的是2.x版本

什么是EJS

EJS是一个JavaScript模版库,用来将EJS模版结合着JSON数据转换为HTML
并且可以直接在模版中写JavaScript的语法

简单的示例

let template = '<h1>Hello, <%= name %></h1>'
let data = {
  name: 'Niko Bellic'
}

let renderStr = ejs.render(template, data)

console.log(renderStr) // => <h1>Hello, Niko Bellic</h1>

EJS模版主要还是HTML标签,仅仅添加了几对特定的标签(<% %>, <%= %>, <%- %>, <% -%>, <%# %>)。

为什么要用EJS

近年来,前端各种MV*框架层出不穷,ReactAngularVue,当然这应该也是未来几年的趋势了,但是这些大都是前端运行时进行渲染,动态的生成HTMLReact是有着服务端渲染的解决方案,为了解决SEO的问题)
但是EJS这类的模版引擎是不依赖于宿主语言环境的,只要是JavaScript即可,也就是说可以用于server端(node.js)直接渲染,返回给前端渲染好的页面。(这个在大部分后台页面的开发中还是需要的)
当请求某个链接时,直接将渲染完成的页面呈现给用户,主要的作用有两点:

  1. 避免了代码都存在前端,被某些恶意用户看到。
  2. 对搜索引擎SEO更友好。

当然,MV*框架依然是近几年的趋势,也是建议多去使用和研究那些框架,但是模版引擎和前端的那几个框架并不冲突,也是可以一起使用的。

如何使用EJS

EJS提供了数个标签来供我们使用,在标签内可以直接写JavaScript代码,如果使用服务端来渲染,你甚至可以直接引用一些npm包,来做一些想做的事情。

<% code %>

EJS会执行标签内的代码,一般用于逻辑处理或者循环创建使用。

<% if (user) { %>
    <h2><%= user.name %></h2>
<% } %>

如上文在EJS处理后的代码应该是类似这个样子的。(源代码比这个内容更丰富一些。。。)

(function () {
  var buf = []

  if (user) {
    buf.push('<h2>')
    buf.push(user.name)
    buf.push('</h2>')
  }

  return buf.join('')
})

<%- code %>

EJS会将标签内的代码执行,并获取返回值,将返回值输出到字符串中。

<span><%= 'Hello' %></span>

<!-- convert -->

<span>Hello</span>

<%= code %>

<%= code %><%- code %>类似,只不过会将返回值进行转义后输出。

<span><%= '<hello />' %></span>

<!-- convert -->

<span>&lt;hello /&gt;</span>

<%# comment %>

昂。。这个标签里边的内容是作为注释存在的。。估计很少有人会用-.- 在模版生成后,会移除里边的内容

在标签后边添加-

这个有很多种写法都可以支持,比如:<% -%><%= -%><%- -%>
这样会移除该标签后边的第一个换行符(如果有的话)

<h1>
  <%- 'Title' %>
</h1>

<!-- convert -->

<h1>
  Title
</h1>

<!-- line -->

<h1>
  <%- 'Title' -%>
</h1>

<!-- convert -->

<h1>
  Title</h1>

模版提供的各种Config

cache

默认为false,如果设置为true则认为是开启了缓存模式,对相同filename参数的模版会使用之前已经渲染好的。
前置条件:必须同时设置filename参数

filename

cache模式下作为一个存储的key

scope

可以通过该参数设置模版执行时的函数上下文。
即模版中this的指向。

debug

默认为false,如果设置为true,则会在模版执行compile时输出拼接好的JavaScript代码。方便调试。

compileDebug

默认为开启,设置为false则为关闭,在开启状态下,模版会在compile执行时额外拼接代码的行信息,这样在报错时我们可以很方便定位是哪一行出的问题。

client

默认为false,如果设置为true则会在compile函数中返回一个相对独立的Function
主要是体现在一些内置函数的引用上。

open

设置开始的界定符。默认为<%

close

设置结束的界定符。默认为%>

模版对外暴露的API

官方文档中写的接口为:compilerender
确实。。其他几个接口都不太常用,或者说,太难以使用,比如:parse属于一个半成品,renderFile又只能在node环境下使用。

clearCache

清除缓存,将之前内存中存储的模版清空。
简单粗暴的一个函数。。。老铁,没毛病。

exports.clearCache = function () {
  cache = {};
};

parse

参数 描述
str 要进行解析的模版字符串
options 一系列的配置参数

解析模版字符串,将其转换为可执行的JavaScript代码并返回。
P.S. 该函数的执行会返回一个JavaScript脚本的字符串,我们可以通过new Function()或者eval(不推荐了)来执行该脚本获得渲染好的字符串。

compile

参数 描述
str 要进行解析的模版字符串
options 一系列的配置参数

函数会调用parse,并将生成好的脚本塞进一个函数中,并将函数返回,我们可以通过调用该函数来获得渲染好的字符串。

render

参数 描述
str 要进行解析的模版字符串
options 一系列的配置参数

函数调用compile,返回值即是渲染好的字符串。

renderFile

参数 描述
path 模版字符串存储的路径
options 一系列的配置参数
fn 获取到文件后执行的回调函数

该函数会将path取出,取出对应的文件,然后将文件的文本作为模版字符串传入render方法中。
fn回调函数应遵守Error-First的规则。(第一个参数为err,后续参数为result

对外的API除去clearCache外的四个函数,是一个包含的关系,大致结构如下:

renderFile [> readFile & call render]

render [> call compile & execute]

compile [> call parse & wrap]

parse [> build ejs template & return result]

一些完整的示例

仓库中存放了一些各种使用姿势的示例:
https://github.com/Jiasm/ejs-examples