{{Template7}}
移动设备优先的 JavaScript 模板引擎

下载

MIT Licensed, v1.1.0 发布于 2015.10.3 | 更新日志

Template7

Template7 是一个移动设备优先的 JavaScript 模板引擎,其语法类似于Handlebars。在Framework7中被用作默认的模板引擎。

极轻(压缩后大约 1KB) ,速度极快(在Safari手机浏览器中比 Handlebars 快2-3倍)。

下载和安装 Template7

首先我们需要下载 Template7 必须的文件:

在下载安装包的disk文件夹中,有我们需要 JavaScript 文件 (.js)。

然后在 HTML 页面加入如下代码:

<html>
    <head>
        ...
        <script src="path/to/template7.min.js"></script>
    </head>
    <body>
        ...
    </body>
</html>          

模板

Template7 模板类似于 Handlebars 模板, 看起来像嵌入表达式整齐的 HTML:

<div class="list-block">
  <ul>
    {{#each items}}
    <li class="item-content">
      <div class="item-inner">
        <div class="item-title">{{title}}</div>
      </div>
    </li>
    {{/each}}
  </ul>
</div> 

表达式语法

Template7 支持下列表达式:

变量:

区块表达式

Helpers

Helpers 可以是普通表达式或区块表达式:

编译和渲染

Template7 是 Window function 的一个全局变量。

首先我们需要做出字符串模板。比如,我们可以把模板存储在 script 标签中:

<script id="template" type="text/template7">
    <p>Hello, my name is {{firstName}} {{lastName}}</p>
</script>

然后我嘛需要用 JavaScript 编译它。Template7 会将我们的模板字符串转换为普通的 JavaScript 方法:

var template = $$('#template').html();
 
// 用 Template7 编译
var compiledTemplate = Template7.compile(template);
 
// 然后我们可以通过传递所需的上下文数据来渲染编译后的模板
var context = {
    firstName: 'John',
    lastName: 'Doe'
};
var html = compiledTemplate(context);

现在, html 变量包含:

<p>Hello, my name is John Doe</p>

内置的 Helpers

Template7 内置的 Helpers 就像预定义好的方法一样来处理带有上下文的一些事情。

{{#each}}...{{else}}...{{/each}}

{{#each}} 是一个块表达式,遍历数组项或对象的属性。

以下是在 helper 中可用的其它变量:

模板 -> 上下文 -> 输出
遍历数组项
<p>Here are the list of people i know:</p>
<ul>
  {{#each people}}
  <li>{{firstName}} {{lastName}}</li>
  {{/each}}    
</ul>          
{
  people : [
    {
      firstName: 'John',
      lastName: 'Doe'
    },
    {
      firstName: 'Mark',
      lastName: 'Johnson'
    },
  ]
}      
<p>Here are the list of people i know:</p>
<ul>
  <li>John Doe</li>
  <li>Mark Johnson</li>
</ul>    
<p>Here are the list of people i know:</p>
<ul>
  {{#each people}}
  <li>[email protected]}}. {{this}}</li>
  {{/each}}    
</ul>          
{
  people : ['John Doe', 'Mark Johnson']
}      
<p>Here are the list of people i know:</p>
<ul>
  <li>0. John Doe</li>
  <li>1. Mark Johnson</li>
</ul>  
遍历对象属性
<p>Car properties:</p>
<ul>
  {{#each props}}
  <li>[email protected]}}: {{this}}</li>
  {{/each}}
</ul>
{
  props: {
    power: '150 hp',
    speed: '200 km/h',
  }
}
<p>Car properties:</p>
<ul>
  <li>power: 150 hp</li>
  <li>speed: 200 kn/h</li>
</ul>
{{else}} 表达式
<p>Car properties:</p>
<ul>
  {{#each props}}
  <li>[email protected]}}: {{this}}</li>
  {{else}}
  <li>No properties</li>
  {{/each}}
</ul>
{
  props: {
    power: '150 hp',
    speed: '200 km/h',
  }
}
<p>Car properties:</p>
<ul>
  <li>power: 150 hp</li>
  <li>speed: 200 kn/h</li>
</ul>
<p>Car properties:</p>
<ul>
  {{#each props}}
  <li>[email protected]}}: {{this}}</li>
  {{else}}
  <li>No properties</li>
  {{/each}}
</ul>
{}
<p>Car properties:</p>
<ul>
  <li>No properties</li>
</ul>

{{#if}}...{{else}}...{{/if}}

{{#if}} helper 渲染内容,当传递的上下文不是 "false" (或 "undefined" 或 "null" 或 "" 或 "0") 的时候,相反它会渲染通过内置的 helper 表达式传递到 {{else}} 的内容:

模板 -> 上下文 -> 输出
<a href="#" {{#if active}}class="active"{{/if}}>{{title}}</a>
{
  active: true,
  title: 'Link',
}
<a href="#" class="active">Link</a>
{{else}} 表达式
<p>Hello, my name is {{name}}.</p>
{{#if hobby}}
<p>I have hobby</p>
{{else}}
<p>I don't have hobby</p>
{{/if}}                  
{
  name: 'John Doe',
  hobby: false
}
<p>Hello, my name is John Doe.</p>
<p>I don't have hobby</p>

{{#unless}}...{{else}}...{{/unless}}

{{#unless}} helper 渲染数据,如果传递的上下文是 "false" (或 "undefined" 或 "null" 或 "" 或 "0") ,相反它会渲染通过内置的 helper 表达式传递到 {{else}} 的内容:

模板 -> 上下文 -> 输出
<a href="#" {{#unless active}}class="active"{{/unless}}>{{title}}</a>
{
  active: true,
  title: 'Link',
}
<a href="#">Link</a>
{{else}} 表达式
<p>Hello, my name is {{name}}.</p>
{{#unless hobby}}
<p>I have hobby</p>
{{else}}
<p>I don't have hobby</p>
{{/unless}}                  
{
  name: 'John Doe',
  hobby: false
}
<p>Hello, my name is John Doe.</p>
<p>I have hobby</p>

{{#with}}...{{/with}}

{{#with}} helper 将传递的上下文替换为渲染的上下文:

模板 -> 上下文 -> 输出
{{#with props}}
<p>Car has {{power}} power and {{speed}} maximum speed</p>
{{/with}}
{
  props: {
    power: '150 hp',
    speed: '200 km/h',
  }
}
<p>Car has 150 hp power and 200 km/h maximum speed</p>

{{#变量名}}...{{/变量名}}

如果你用 helper 名称传递一个区块表达式,它会像{{#each}} helper 一样处理一个数组上下文,如果是一个对象会像 {{#with}} 一样处理:

模板 -> 上下文 -> 输出
<ul>
  {{#people}}
  <li>{{name}} - {{age}} years old</li>
  {{/people}}
</ul>
{
  people: [
    {
      name: 'John Doe',
      age: 18
    },
    {
      name: 'Mark Johnson',
      age: 21
    }
  ]  
}
<ul>
  <li>John Doe - 18 years old</li>
  <li>Mark Johnson - 21 years old</li>
</ul>
{{#props}}
<p>Car has {{power}} power and {{speed}} maximum speed</p>
{{/props}}
{
  props: {
    power: '150 hp',
    speed: '200 km/h',
  }
}
<p>Car has 150 hp power and 200 km/h maximum speed</p>

{{join delimiter=""}}

这个 helper 会用传递的定界符将数组项连接为一个单独的字符串

模板 -> 上下文 -> 输出
<h3>"{{title}}" TV Show</h3>
<p>Was released in year {{year}}</p>
<p>Genres: {{join genres delimiter=", "}}</p>
{
  title: 'Friends',
  year: 2001,
  genres: ['comedy', 'drama']
}
<h3>"Friends" TV Show</h3>
<p>Was released in year 2001</p>
<p>Genres: comedy, drama</p>

{{escape}}

这个 helper 返回转义的 HTML 字符串。它仅转义以下字符: < > " &

模板 -> 上下文 -> 输出
<h1>{{title}}</h1>
<p>{{escape body}}</p>
{
  title: 'Paragraphs',
  body: 'We need to use <p> tags to add paragraphs in HTML',
}
<h1>Paragraphs</h1>
<p>We need to use &lt;p&gt; tags to add paragraphs in HTML</p>

{{js "表达式"}}

这个 helper 允许直接在模板中执行一些简单的 JavaScript,动态的修改或判断上下文或一些 JS 计算

模板 -> 上下文 -> 输出
<h3>{{title}}</h3>
<p>Price: ${{js "this.price * 1.2"}} </p>
<p>{{js "this.inStock ? 'In Stock' : 'Not in stock'"}} </p>
{
  title: 'iPhone 6 Plus',
  price: 1000,
  inStock: true
}
<h3>iPhone 6 Plus</h3>
<p>Price: $1200</p>
<p>In stock</p>

{{#js_compare "表达式"}}...{{/js_compare}}

这个区块 helper 可用轻松的比较上下文变量。如果 JavaScript 表达式不是 "false" (或 "undefined" 或 "null" 或 "" 或 "0")时,它会渲染上下文,相反它会渲染通过内置的 helper 表达式传递到 {{else}} 的内容:

模板 -> 上下文 -> 输出
<h3>{{title}}</h3>
<p>Price: ${{price}} </p>
<p>{{#js_compare "color === 'white' && memory > 16"}}Not in stock{{else}}In stock{{/js_compare}} </p>
{
  title: 'iPhone 6 Plus',
  price: 1000,
  color: 'white',
  memory: 32
}
<h3>iPhone 6 Plus</h3>
<p>Price: $1000</p>
<p>Not in stock</p>
<p>{{#js_compare "a === b"}}A equals to B{{else}}A not equal to B{{/js_compare}} </p>
{
  a: 5,
  b: 34
}
<p>A not equal to B</p>

注意在 jsjs_compare helper 中,你需要用 this.variableName 来代替 variableName

自定义 Helpers

Template7 允许用以下方法注册自定义 helpers:

Template7.registerHelper(name, helper)

Helper 函数可以接收很多需要的参数,这些参数可以是上下文,字符串或 hash 数据。

下面让我们看一下如何用简单的 {{#if}} 注册一个helper:

Template7.registerHelper('if', function (condition, options) {
  // "this" 在函数上下文中等同于表达式执行的上下文
  // "condition" 参数包含传递的上下文或条件
  /*
    @options 对象包含如下属性和方法:
    "hash" - 包含传递带有参数的hash对象
    "fn" - 方法传递 helper 块内容到编译器
    "inverse" - 方法传递相反的 helper ({{else}}) 内容到编译器
    "data" - 包含额外的表达式数据,比如数组的 @index 或对象的 @key
  */
 
  // 首先我们需要先判断传递的上下文是一个函数
  if (typeof condition === 'function') condition = condition.call(this);
 
  // 判断上下文的条件
  if (condition) {
    // 我们需要传递带有相同数据的区块内容到编译器:
    options.fn(this, options.data);
  }
  else {
    // 传递相反的 helper ({{else}}) 内容到编译器:
    options.inverse(this, options.data);
  }
}); 

在 {{join}} helper 的例子中:

Template7.registerHelper('join', function (arr, options) {
  // 首先我们需要攀登传递的 arr 参数是一个函数
  if (typeof arr === 'function') arr = arr.call(this);
 
  /* 
    传递的分界符是 options.hash 的对象:
    console.log(options.hash) -> {delimiter: ', '}
  */
 
  // 返回被串联的数组
  return arr.join(options.hash.delimiter);
});          

我们可以用以下语法创建一个 Framework7 的 list-block 链接:

{{link url title target="_blank"}}
Template7.registerHelper('link', function (url, title, options){
  var ret = '<li>' +
              '<a href="' + url + '" class="item-content item-link" target="' + options.hash.target + '">' +
                '<div class="item-inner">' +
                  '<div class="item-title">' + title + '</div>' +
                '</div>' +
              '</a>' +
            '</li>';
  return ret;
});
模板 -> 上下文 -> 输出
<div class="list-block">
  <ul>
    {{#each links}}
    {{link url title target="_blank"}}
    {{/each}}    
  </ul>
</div>
{
  links: [
    {
      url: 'http://google.com',
      title: 'Google'
    },
    {
      url: 'http://idangero.us',
      title: 'iDangero.us'
    },
  ]
}
<div class="list-block">
  <ul>
    <li>
      <a href="http://google.com" target="_blank" class="item-link item-content">
        <div class="item-inner">
          <div class="item-title">Google</div>
        </div>
      </a>
    </li>
    <li>
      <a href="http://idangero.us" target="_blank" class="item-link item-content">
        <div class="item-inner">
          <div class="item-title">iDangero.us</div>
        </div>
      </a>
    </li>
  </ul>
</div>

注意,所有的自定义 helper 应该在你编译模板前定义好!

移除自定义 Helpers

Template7 允许使用以下方法移除自定义 helpers :

Template7.unregisterHelper(name)

全局上下文

Template7 也支持全局上下文,可以很容易访问任何上下文内容。

我们可以在 Template7.global 属性中定义:

Template7.global = {
    os: 'iOS',
    browser: 'Chrome',
    username: 'johndoe',
    email: [email protected]'
};

我们可以用{{@global}}变量在模板中访问它:

<p>Hello, [email protected]}}. Your email is [email protected]}}</p>

访问根上下文

有时我们可能需要访问最初传入到模板中的根上下文。对于这种情况我们需要用 {{@root}} 变量。当我们的上下文内容较深时,这种方式尤为重要:

{
    persons: [
        {
            name: 'John',
            hobby: ['Cars', 'Food']
        },
        {
            name: 'Kyle',
            hobby: ['Travel', 'Puzzles']
        },
 
    ],
    showHobby: true
}    
{{#each persons}}
    <h2>{{name}}</h2>
    <h3>Hobby:</h3>
    {{#if @root.showHobby}}
        <ul>
            {{#each hobby}}
                <li>{{this}}</li>
            {{/each}}
        </ul>
    {{/if}}
{{/each}} 

Partials

Template7 允许使用 partials 复用模板。正常情况下在 Template7 模板中 复用模板 Partials时通用的。可以被其它模板调用。

我们可以使用以下方法注册和注销 partials :

Template7.registerPartial(name, template) - 注册 partial

Template7.unregisterPartial(name) - 注销 partial

然后我们可以使用特殊的 {{> "partialName"}} helper 来利用我们的 partials

模板:

<ul class="users">
    {{#each users}}
    {{> "user"}}
    {{/each}}
</ul>
<ul class="admins">
    {{#each admins}}
    {{> "user"}}
    {{/each}}
</ul>

注册 partial:

Template7.registerPartial('user', '<li><h2>{{firstName}} {{lastName}}</h2><p>{{bio}}</p></li>')

将上下文应用到模板:

{
    users: [
        {
            firstName: 'John',
            lastName: 'Doe',
            bio: 'Lorem ipsum dolor'
        },
        {
            firstName: 'Jane',
            lastName: 'Doe',
            bio: 'Donec sodales euismod augue'
        }
    ],
    admins: [
        {
            firstName: 'Mike',
            lastName: 'Doe',
            bio: 'Lorem ipsum dolor'
        },
        {
            firstName: 'Kate',
            lastName: 'Doe',
            bio: 'Donec sodales euismod augue'
        }
    ]
}

我们会得到以下输出:

        
<ul class="users">
    <li>
        <h2>John Doe</h2>
        <p>Lorem ipsum dolor</p>
    </li>
    <li>
        <h2>Jane Doe</h2>
        <p>Donec sodales euismod augue</p>
    </li>
</ul>
<ul class="admins">
    <li>
        <h2>Mike Doe</h2>
        <p>Lorem ipsum dolor</p>
    </li>
    <li>
        <h2>Kate Doe</h2>
        <p>Donec sodales euismod augue</p>
    </li>
</ul>    

递归 Partials

我们甚至可以 partials 来递归模板,像嵌入的注释:

// 带有一个 partial 的简单模板
var template = '{{> "comments"}}'
 
// 注册 partial
Template7.registerPartial(
    'comments', 
    '<ul>' + 
        '{{#each comments}}' +
            '<li>' +
            '<h2>{{author}}</h2>' +
            '<p>{{text}}</p>' +
            '{{#if comments}}{{> "comments"}}{{/if}}' +
            '</li>' +
        '{{/each}}' +
    '</ul>'
);
 
// 编译模板
var compiledTemplate = Template7.compile(template);
 
// 渲染模板
var output = compiledTemplate({
    comments: [
        {
            author: 'John Doe',
            text: 'Lorem ipsum dolor',
            comments: [
                {
                    author: 'Mike Doe',
                    text: 'Aliquam erat volutpat'
                },
                {
                    author: 'Kate Doe',
                    text: 'Donec eget fringilla turpis'
                }
            ]
        },
        {
            author: 'Jane Doe',
            text: 'Donec sodales euismod augue'
        }
    ]
})

输出如下:

<ul class="comments">
    <li>
        <h2>John Doe</h2>
        <p>Lorem ipsum dolor</p>
        <ul class="comments">
            <li>
                <h2>Mike Doe</h2>
                <p>Aliquam erat volutpat</p>
            </li>
            <li>
                <h2>Kate Doe</h2>
                <p>Donec eget fringilla turpis</p>
            </li>
        </ul>
    </li>
    <li>
        <h2>Jane Doe</h2>
        <p>Donec sodales euismod augue</p>
    </li>
</ul>          

性能提醒

Template7 速度很快,你可以让它在你的app中运行更快。最慢的部分是在编译/渲染过程,当你执行 Template7.compile()。因此不要多次编译相同的模板,一次就足够了:

// app 开始
 
// 在app加载或初始化时编译模板
var searchTemplate = $('script#search-template').html();
var compiledSearchTemplate = Template7.compile(searchTemplate);
 
var listTemplate = $('script#list-template').html();
var compiledListTemplate = Template7.compile(listTemplate);
 
// 现在只需要用所需的上下文执行编译模板
var html = compiledSearchTemplate({/*...some data...*/});
 
// Do something with html...