Skip to content

Underscore 模板引擎 API 更新 #4

Open
@cssmagic

Description

@cssmagic

Underscore 模板引擎 API 更新

语法

Underscore 的模板引擎 _.template() 脱胎于 jQuery 作者的作品 Micro-Templating。但从 Underscore 1.3.3 开始,这个方法做了较大的调整,在保留旧语法的基础上,还新增支持了一个 {variable: 'foo'} 对象作为第三个参数。

一旦传入了这个参数,则模板(第一个参数)中的变量将不再指向待渲染数据(第二个参数)的属性,模板中的 foo 变量将直接指向待渲染数据(第二个参数)自身

比如,原来的调用方法:

_.template('I love <%= person %>.', {person: 'you'});

在新语法下可以写为:

_.template('I love <%= foo.person %>.', {person: 'you'}, {variable: 'foo'});

比较

看起来似乎变复杂了,但这样做有两个好处:

内部实现

旧语法在实现上,需要使用 with 声明来实现对数据对象属性的查找。with 有哪些问题,这里就不多说了。而新语法由于已经在模板中指定了数据对象自身,则不需要用 with 来搜索其属性。据官方文档称,“模板渲染性能得到极大提升”。

外部接口

新语法带来了一个隐性的改良——接口灵活性更高,即待渲染数据(第二个参数)可以不仅是对象,也可以是数组等其它数据类型。仍然以上面的代码为例,用新语法还可以写成这样:

_.template('I love <%= foo %>.', 'you', {variable: 'foo'});

这样传入的数据更自由,数组等数据不需要被包装为对象再传入了。

使用

在实际应用中,往往存在某个常用的模板需要被多次渲染的情况。此时,为优化性能,我们通常会采用“两步渲染法”——先把模板编译成模板函数备用;按需执行已经编译好的模板函数,把不同的数据渲染为不同的结果——以避免同一模板的重复编译。如下所示:

var fnRender = _.template('I love <%= person %>.');
fnRender({person: 'you'});  //'I love you.'
fnRender({person: 'her'});  //'I love her.'

在这种情况下,我们无法使用 {variable: 'foo'} 参数。那怎么办呢?

幸好有 _.templateSettings 可以进行 _.template() 的全局设置:

_.extend(_.templateSettings, {variable: 'foo'});

在此之后编译的所有模板函数即工作在新语法之下。需要注意的是,这个设置是全局的,也就是说,当前页面的所有模板和相关功能都需要以新语法来写,并将 foo 统一命名。


更新

从 Underscore 1.7 开始,这个 API 的行为又发生了一些变化,我们有必要再来看一看。

从这个版本开始,_.template() 方法将不再接受模板数据了,它的返回值就总是编译生成的模板函数了。也就是说原先一步渲染模板的用法需要修改成两步走:

// before
_.template('I love <%= person %>.', {person: 'you'});

// after
_.template('I love <%= person %>.')({person: 'you'});

看起来很蛋疼?无关痛痒?其实 Underscore 下决心引入这个 “破坏性变更” 还是很有深意的。我认为这个改动的好处在于:

  • 消灭了返回值的不确定性,令这个 API 的行为更易于理解。
  • 去掉一步到位的用法,虽然牺牲了眼前的便利,但同时也强制使用者了解模板引擎的基本原理,一定程度上会推动使用者考虑模板缓存,进而提升应用的整体性能。

© Creative Commons BY-NC-ND 4.0   |   我要订阅   |   我要打赏

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions