精华 科普文:模板引擎
发布于 8 个月前 作者 i5ting 2839 次浏览 最后一次编辑是 4 个月前 来自 分享

好久没写了

模板引擎

模板引擎是一种复用思想,通过定义模板,用的时候和数据一起编译,生成html,以便浏览器渲染。从这个定义里我们可以找出几个关键点

编译(模板 + 数据) => html

模板引擎有好多种,下面介绍2种典型的模板引擎

  • ejs:嵌入js语法的模板引擎(e = embed),类似于jsp,asp,erb的,在html里嵌入模板特性,如果熟悉html写起来就非常简单,只要区分哪些地方是可变,哪些地方是不变即可
  • jade:缩进式极简写法的模板引擎,发展历史 HAML -> Jade -> Slim -> Slm,最早是ruby里有的,目前以jade用的最多,这种写法虽好,,但需要大脑去转换,这其实是比较麻烦的,如果对html不是特别熟悉,这种思维转换是非常难受的。

更多见 https://github.com/tj/consolidate.js#supported-template-engines

ejs

准备工作

cd web/tpl/ejs
npm init(注意名字不要用ejs,不然无法安装ejs模块的)
touch index.js
npm install --save ejs

定义模板

首先我们定义user.ejs

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

这段模板里,首先我们看到的if语句,判断user对象是否存在,如果存在就显示<h2>标签,并把user.name作为变量,嵌入到<h2>标签的显示内容里,一会编译的时候我们讲讲这种嵌入的好处。

我们知道模板原理

编译(模板 + 数据) => html

编译

下面我们看一下具体如何实现编译

var fs = require('fs')
var ejs = require('ejs')

// 读取模板文件,放到user_tpl_str变量中
var user_tpl_str = fs.readFileSync('./user.ejs').toString();

console.log(user_tpl_str)

// 通过ejs的render方法,对user_tpl_str和数据进行编译
var html = ejs.render(user_tpl_str, {
  user:{
    name: 'i5ting'
  }
});

console.log(html)

下面执行,看一下编译后的结果

$ node index.js
<% if (user) { %>
    <h2><%= user.name %></h2>
<% } %>

    <h2>i5ting</h2>

核心要点

  1. 通过fs.readFileSync读取模板文件
  2. 通过ejs.render方法进行编译
  3. 模板是固定的,不可变,而数据可以在编译时填入的,是可变的
  4. 编译后的结果是浏览器能够渲染的html代码

上面的数据是一个plain old object

{
  user:{
    name: 'i5ting'
  }
}

它是编译时传进去,也就是说我们可以按照自己的需要改变,并多次复用模板。

再想想user.ejs里的这句

    <h2><%= user.name %></h2>

它会编译成

    <h2>i5ting</h2>

如果我想让它输出其他名字呢?其实只要改变以user.name的数据即可

模板是可以嵌入逻辑的,上面的user.ejs里使用if指令,也就是只有满足条件的情况下才会输入对应的html,这在我们的页面里是经常使用的技巧。

// 如果user为空,测试编译结果
var empty_html = ejs.render(user_tpl_str, {
  user:undefined
});

console.log(empty_html)

这种情况下,执行是不会打出任何内容的,因为user不存在。

模板里支持for循环的,我们看一下list.ejs,列出所有用户列表

<ul>
  <% users.forEach(function(user){ %>
        <li><%= user.name %></li>
  <% }) %>
</ul>

核心代码

// 通过ejs的render方法,对user_tpl_str和数据进行编译
var html = ejs.render(user_tpl_str, {
  users:[
    {
      name: '朴灵'
    }, {
      name: 'alsotang'
    },{
      name: 'i5ting'
    }
  ]
});

这里主要是传入的数据是对象数组而已,然后模板里通过forEach遍历,然后再生产html,下面看一下执行结果

$ node list.js 
<ul>
  <% users.forEach(function(user){ %>
        <li><%= user.name %></li>
  <% }) %>
</ul>
<ul>
  
        <li>朴灵</li>
  
        <li>alsotang</li>
  
        <li>i5ting</li>
  
</ul>

其实还有很多特性,限于篇幅,这里就不详细讲了,自己查看官方文档

jade

准备工作

cd web/tpl/jade
npm init(注意名字不要用jade,不然无法安装jade模块的)
touch index.js
npm install --save pug

这里安装的pug模块,原因是jade因为版权问题,已更名为pug,但我们更喜欢称她为jade,所以本书都会这样约定,大家要注意区别

定义模板

user.jade

if user
  h2= user.name

这段模板里,首先我们看到的if语句,判断user对象是否存在,如果存在就显示<h2>标签,并把user.name作为变量,嵌入到<h2>标签的显示内容里,和上面的ejs版本是一样的。

编译

下面我们看一下具体如何实现编译index.js

var fs = require('fs')
var pug = require('pug');

// 读取模板文件,放到user_tpl_str变量中
var user_tpl_str = fs.readFileSync('./user.jade').toString();

console.log(user_tpl_str)

// 通过ejs的render方法,对user_tpl_str和数据进行编译
var html = pug.render(user_tpl_str, {
  user:{
    name: 'i5ting'
  }
});

console.log(html)

对比一下上面ejs编译实现,其实就是把ejs模块替换成pug而已,其他都是一模一样的。

下面执行,看一下编译后的结果

$ node index.js 
if user
  h2= user.name
<h2>i5ting</h2>

重复一下核心要点

  1. 通过fs.readFileSync读取模板文件
  2. 通过pug.render方法进行编译
  3. 模板是固定的,不可变,而数据可以在编译时填入的,是可变的
  4. 编译后的结果是浏览器能够渲染的html代码

下面我们看一下在jade里如何使用for循环,显示用户列表,看list.jade

ul
  each user in users
    li= user.name

index.js编译核心代码

// 通过jade的render方法,对user_tpl_str和数据进行编译
var html = pug.render(user_tpl_str, {
  users:[
    {
      name: '朴灵'
    }, {
      name: 'alsotang'
    },{
      name: 'i5ting'
    }
  ]
});

是不是和ejs的一模一样?

执行效果是一模一样的,如下

$ node list.js
ul
  each user in users
    li= user.name
<ul><li>朴灵</li><li>alsotang</li><li>i5ting</li></ul>

更多用法见官网

比较一下ejs和jade

user.ejs

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

特点

  • 内嵌js语句<% js语句 %>
  • html标签:<h2>

user.jade

if user
  h2= user.name

特点

  • 内嵌js语句if user,和ejs里的不太一样,极简,没有括号和结尾大括号
  • 极简html标签:h2

总结一下,如果大家对html/css/js不是特别熟悉,使用ejs是比较好的选择,学习成本较低。如果你是一个极客,并且对html/css/js有比较好的掌握,那么用jade更好一些,代码更少。其实技术选型更多的还是要看团队综合情况的,不是一个人的喜好选择。

全文完

欢迎关注我的公众号【node全栈】

node全栈.png

22 回复

欢迎大家反馈啊

又是一篇精华, 支持桑大

我平时用ejs/hbs比较多, 因为跟html同构的关系, 一般的html可以直接从bootstrap这种框架中复制过来修改, jade的话成本就会比较高

我建议这一节关于ejs的内容可以介绍一下不同tag的作用, <%- %>, <%= %>, <% %> … 我记得我刚开始用的时候这几个经常傻傻搞不清楚 hh

jade现在改名为pug了

在嵌套层级多的时候,ejs的写法太不友好了。

我以前搞过JSP、PHP,所以选择ejs比较顺手。呵呵 @i5ting 能不能说说,ejs如何开启静态缓存?

@wssgcg1213 嗯,这部分主要是简介,入门,让大家了解模板引擎,后面有专门章节讲具体tag和优化技巧的,建议非常好

@jingsam 没仔细看吧,文章里说了

这里安装的pug模块,原因是jade因为版权问题,已更名为pug,但我们更喜欢称她为jade,所以本书都会这样约定,大家要注意区别

@huangshaohui ejs整体功能没有jade强大,尤其partial等

@zouzhenxing 待我研究研究吧,我是一个jade党,哈哈

我还是喜欢swig

@liygheart swig好像也不维护了issue628,可以用nunjucks替换

@JasonBoy soga,感谢提醒,我都没在意

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

这种层级嵌套多了。。你不感觉晕。。。好像第一次学习ASP是的。。。

@weierbufan jade里是可以有include的,模块化拆分就好了

我的ejsExcel,跟这个ejs很像,不过我的是Excel模板引擎, ejs是文本模板引擎

简单的项目用 ejs,复杂的项目用 nunjucks

ejs已经没人维护了,功能太少,还是用nunjucks吧 From Noder

喜欢jade,不过用vue之类的前端框架后,就没必要再用这些了。

看似简单的东西(其实不然),桑老师理解的好透彻啊。大师~

回到顶部