Framework7学习笔记(三):高级
Template7是一个移动优先(mobile-first)的模板引擎,其使用Handlebars风格的语法。
T7非常轻量,速度很快。F7已经内置T7,无需包含额外的JS文件。T7也可以单独使用。
使用T7时,性能瓶颈会出现在编译阶段——把字符串编译为纯粹的JavaScript函数时,亦即调用 Template7.compile() 时。因此,不要编译同一个模板多次。
- {{title}} 输出当前上下文中title变量的值
- {{../title}} 输出父上下文中title变量的值
- {{../../title}} 输出父父上下文中title变量的值
- {{this}} 输出当前上下文对象
- {{person.name}} 输出当前上下文中person变量name属性的值
- {{../person.name}} 输出父上下文中person变量name属性的值
- {{@index}} 访问额外数据变量,这些数据变量可以在助手中访问
- {{#helperName}} 块表达式开始
- {{/helperName}} 块表达式结束
- {{else}} 反转块表达式开始
- {{#helperName name="value"}} 块表达式开始,提供一个哈希参数(Hash argument),此参数的名/值分别为name/value
举例: {{join myArray delimiter=", "}} 表示知晓join助手,传递上下文变量myArray以及哈希参数delimiter。
用于遍历传入的数组的元素/对象的属性。在此助手内部,可以访问额外变量:
- @index 仅在遍历数组时有效,当前迭代的索引
- @first 仅在遍历数组时有效,当前是否第一个元素
- @last 仅在遍历数组时有效,当前是否最后一个元素
- @key 仅在遍历对象时有效,当前属性的名称
遍历数组的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<script type="text/template7" id="example"> <p>People list: </p> <ul> {{#each people}} <li>{{firstName}} {{lastName}}</li> {{else}} <li>No people</li> {{/each}} </ul> </script> <script type="text/javascript"> app.templates.example( { people : [ { firstName : 'Alex', lastName : 'Wong' }, { firstName : 'Meng', lastName : 'Lee' }, ] } ); </script> <!-- 输出如下: --> <p>People list: </p> <ul> <li>Alex Wong</li> <li>Meng Lee</li> </ul> <script type="text/javascript"> app.templates.example( {} ); </script> <!-- 输出如下: --> <p>People list: </p> <ul> <li>No people</li> </ul> |
遍历对象的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<script type="text/template7" id="example"> <p>People list: </p> <ul> {{#each alex}} <li>{{@key}}: {{this}}</li> {{/each}} </ul> </script> <script type="text/javascript"> app.templates.example( { alex : { firstName : 'Alex' lastName : 'Wong' } } ); </script> <!-- 输出如下: --> <p>People list:</p> <ul> <li>firstName: Alex</li> <li>lastName: Wong</li> </ul> |
用于进行分支判断,只有传入的上下文变量不是false时,才会渲染if分支中的内容。下面是一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 |
<script type="text/template7" id="example"> <a href="#" {{#if active}}class="active"{{/if}}>{{title}}</a> </script> <script type="text/javascript"> app.templates.example( { active : true, title : 'Link' } ); </script> <!-- 输出如下: --> <a href="#" class="active">Link</a> |
与上一个助手类似,但是在传入的上下文变量是false时,渲染unless分支的内容。
用于修改上下文对象。下面是一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<script type="text/template7" id="example"> {{#with a}} <a href="#" {{#if active}}class="active"{{/if}}>{{title}}</a> {{/with}} </script> <script type="text/javascript"> app.templates.example( { a : { active : true, title : 'Link' } } ); </script> <!-- 输出如下: --> <a href="#" class="active">Link</a> |
如果块表达式中的助手名称是上下文中的变量,则效果与 {{#each variableName}} 一致。
基于分隔符把数组元素连接为字符串。
返回转移后的HTML字符串,HTML特殊字符被转写为 &**; 形式。
立即执行一个JavaScript表达式,并把返回值输出。
要访问当前上下文对象,必须使用 this 。
T7允许通过下面的方法注册新的助手:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/** * @param name 助手的名称 * @param helper 助手函数,用于处理上下文 */ Template7.registerHelper(name, helper); /** * 助手函数规格,在此函数中,this指向表达式的执行上下文 * @param condition 包含传递进来的上下文或条件,例如{{#if true}}中condition为true * @param options 包含若干属性和方法: * hash 传递给助手的哈希参数对象 * fn 调用此方法,把块表达式的内容传递给编译器处理 * inverse 调用此方法,把反转块表达式({{else}})的内容传递给编译器处理 * data 包含额外的表达式数据,例如@index * @return 可选,助手输出的内容 */ function helper(condition, options) {} |
若要解除注册,可以调用: Template7.unregisterHelper(name) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/* 简单的join助手 */ Template7.registerHelper( 'join', function( arr, options ) { if ( typeof arr === 'function' ) arr = arr.call( this ); //返回值直接输出到助手在模板中的位置 return arr.join( options.hash.delimiter ); } ); /* ifeq助手:{{#ifeq var1 var2}}..{{else}}..{{/if}} */ Template7.registerHelper( 'ifeq', function( var1, var2, options ) { if ( var1 == var2 ) { return options.fn( this, options.data ); } else { return options.inverse( this, options.data ); } } ); |
全局上下文可以在任何地方访问,使用 Template7.global 可以指定全局上下文,例如:
1 2 3 4 5 |
Template7.global = { os : 'iOS', browser : 'Chrome', username : 'Alex' }; |
访问全局上下文,必须使用前缀 {{@global.}} 。
某些时候需要访问传递给模板的根上下文对象,此时可以使用 {{@root}} 变量。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<script type="text/template7" id="example"> {{#each persons}} <h2>{{name}}</h2> {{#if @root.showHobby}} <h3>Hobby:</h3> <ul> {{#each hobby}} <li>{{this}}</li> {{/each}} </ul> {{/if}} {{/each}} </script> <script type="text/javascript"> app.templates.example( { persons : [ { name : 'Alex', hobby : [ 'Clectronics', 'Biology' ] }, { name : 'Meng', hobby : [ 'Accountancy' ] }, ], showHobby : true } ); </script> |
在特殊标签中定义的模板,可以被F7自动编译:
1 2 3 4 5 6 7 |
<!-- type="text/template7" 告诉F7这是一个T7模板,可以被自动编译 id="myTemplate" 指定模板的标识符,后续可以依据此标识符访问 --> <script type="text/template7" id="iTemplate"> <p>Hello, my name is {{name}} and i am {{age}} years old</p> </script> |
需要在应用初始化参数中启用自动编译功能:
1 2 3 4 |
var app = new Framework7( { // 在初始化阶段自动编译模板 precompileTemplates : true, } ); |
所有编译好的模板会存放到对象 Template7.templates 中, app.templates 是此对象的别名。在应用初始化后,可以这样使用模板:
1 2 3 4 |
var iHTML = Template7.templates.iTemplate( { name : 'Alex Wong', age : 30 } ); |
片段就是普通模板,只是它们供其它模板调用。通过片段,可以实现模板重用。通过下面的方法可以注册/解除注册片段:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/** * 注册片段 * * @param name 片段的名称 * @param template 片段的内容,字符串 */ Template7.registerPartial( name, template ); /** * 解除注册片段 * * @param name 片段的名称 */ Template7.unregisterPartial( name ); |
通过特殊的助手: {{> "partialName"}} 可以调用既有的片段。
T7片段的一个强大特性是,片段可以调用自身。下面是一个嵌套评论(针对评论的回复)的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
/* 此模板只包含一个对片段的调用 */ var template = '{{> "comments"}}'; /* 注册片段 */ 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 : 'Meng Lee', text : 'Good article!', comments : [ { author : 'Alex Wong', text : 'Thanks!' }, { author : 'CaiCai Wong', text : 'Very funny.' } ] }, { author : 'WenJun Wong', text : 'Not good!' } ] } ); |
输出结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<ul class="comments"> <li> <h2>Meng Lee</h2> <p>Good article!</p> <ul class="comments"> <li> <h2>Alex Wong</h2> <p>Thanks!</p> </li> <li> <h2>CaiCai Wong</h2> <p>Very funny.</p> </li> </ul> </li> <li> <h2>WenJun Wong</h2> <p>Not good!</p> </li> </ul> |
在Framework7学习笔记(一)中我们提到过F7处理页面的4种方式,最后一种Template7页面在这里讲解。
F7允许把新的Ajax页面、Dynamic页面作为Template7模板来解析,要启用此功能,需要提供应用初始化参数:
1 2 3 |
var myApp = new Framework7({ template7Pages: true }); |
要解析模板,必须提供必要的上下文(数据)对象。可以通过应用初始化参数template7Data设置全局的上下文:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
var myApp = new Framework7({ template7Pages: true, //指定上下文对象 template7Data: { // 下面这个上下文专用于URL为about.html的页面(模板) 'url:about.html': { name: 'John Doe', age: 38, company: 'Apple', position: 'Developer' }, // 下面这个上下文专用于名称为contacts(data-page="contacts")的页面(模板) 'page:contacts': { tel: '(999)-111-22-33', email: 'contact@john.doe' }, // 简单数据对象(Plain data object) 'languages': { 'frontend': [ { name:'JavaScript', desc: 'Most commonly used as part of web browsers' }, { name:'CSS' ,desc: 'Style sheet language used for describing the look and formatting of a document'}, ], 'backend': [ { name: 'PHP', desc: 'Server-side scripting language designed for web development' }, { name: 'Ruby', desc: 'Dynamic, reflective, object-oriented, general-purpose programming language'} ] } } }); |
可以看到,上下文对象分为三种:
- url:*,用于特定URL的页面(模板)的上下文
- page:*,用于特定名称页面(模板)的上下文
- 简单数据对象:用于提供可供不同页面(模板)重用的、定制化的上下文
在任何时候你都可以修改模板上下文对象,你可以通过全局对象 Templates.data 或者其别名 app.template7Data 修改之。
template7Data中提供的简单数据对象,用于提供复杂的、定制化的上下文。利用普通链接和 data-context-name 属性,你可以指定该链接要加载的页面(模板)所使用的上下文对象,此对象可以是一个简单数据对象,或者其点号导航的子属性([]语法不支持)。
下面的代码示意了如何使用简单数据对象作为模板上下文:
1 2 3 4 5 6 |
<!-- 模板languages.html将使用上下文app.template7Data.languages来渲染 --> <a href="languages.html" class="item-link item-content" data-context-name="languages"> <div class="item-inner"> <div class="item-title">Languages</div> </div> </a> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<div class="page" data-page="languages"> <div class="page-content"> <div class="content-block-title">Frontend</div> <div class="list-block"> <ul> <!-- 遍历app.template7Data.languages.frontend --> {{#each this.frontend}} <li> <!-- 嵌套的模板,支持基于点号导航(languages.frontend.1是合法的)来访问子对象 --> <a href="language-details.html" class="item-link item-content" data-context-name="languages.frontend.{{@index}}"> <div class="item-inner"> <div class="item-title">{{this.name}}</div> </div> </a> </li> {{/each}} </ul> </div> </div> </div> |
1 2 3 4 5 6 7 8 |
<div class="page" data-page="language-details"> <div class="page-content"> <div class="content-block-title">{{name}}</div> <div class="content-block"> <p>{{desc}}</p> </div> </div> </div> |
你也可以通过导航/路由 API来指定上下文对象:
1 2 3 4 |
mainView.router.load({ url: 'language-details.html', contextName: 'languages.frontend.0' }) |
除了通过template7Data指定的全局上下文以外,你还可以使用即时指定的定制上下文。HTML方式:
1 2 |
<!-- 赋值串行化的JSON对象给data-context属性 --> <a href='contacts.html' data-context='{"tel": "(999)-111-22-33", "email": "contact@john.doe"}'>Contacts</a> |
JavaScript方式:
1 2 3 4 5 6 7 |
mainView.router.load( { url : 'contacts.html', context : { tel : '(999)-111-22-33', email : 'contact@john.doe' } } ); |
F7会自动编译 <script id="templateName" type="text/template7"> 声明的模板。要将其作为Template7页面使用,可以通过HTML方式:
1 2 |
<a href="#" data-template="templateName"></a> <a href="#" data-template="templateName" data-context='{"name": "value"}'></a> |
或者JavaScript方式:
1 2 3 4 5 6 |
mainView.router.load( { template : Template7.templates.templateName, context : { name : 'value' } } ); |
特殊的上下文属性 url_query 用于访问URL上的GET参数:
1 |
<a href="person.html?firstname=Alex&lastname=Wong&age=30">Alex</a> |
1 2 3 4 5 |
<div class="page" data-page="person"> <div class="page-content"> <p>Hello, my name is {{url_query.firstname}} {{url_query.lastname}}. I am {{url_query.age}} years old.</p> </div> </div> |
Framework7提供了简单易用的插件API,利用这些API你可以扩展自己的F7插件。
只需要为 Framework7.prototype.plugins 添加一个属性,即可注册新插件。例如:
1 2 3 4 5 6 7 8 |
/** * myPlugin 新插件的名字 * app 已初始化的F7应用对象 * params 可选的插件参数 */ Framework7.prototype.plugins.myPlugin = function( app, params ) { //插件代码 }; |
要引用新插件,只需要将其JS文件包含到HTML中,位于F7主JS文件之后:
1 2 |
<script src="path/to/framework7.js"></script> <script src="path/to/framework7.myplugin.js"></script> |
在F7应用初始化的时候,可以向插件传递参数:
1 2 3 4 5 6 7 8 9 |
var myApp = new Framework7( { pushState : true, /* * 为myPlugin传递参数 */ myPlugin : { foo : 'bar' } } ); |
F7插件系统包含三种扩展机制:
- hooks:与通常的回调类似,由F7在框架的很多核心部分调用。可以在特定时刻/场景执行插件代码、处理数据
- prevents:用于阻止F7默认行为,例如阻止默认页面动画而使用自己的
- processes:类似于预处理器,每个处理方法可以接收被处理数据
插件函数的返回值必须是一个对象,其包括hooks、prevents和processes属性。例如:
1 2 3 4 5 |
return { hooks : { appInit : function(){} } }; |
可用的钩子包括:
钩子 | 说明 |
appInit | 当应用完全初始化后调用 |
navbarInit (navbar, pageData) | 和navbarInit事件一样 |
pageInit (pageData) | 和navbarInit事件/回调一样 |
pageBeforeInit (pageData) | 和pageBeforeInit事件/回调一样 |
pageBeforeAnimation (pageData) | 和pageBeforeAnimation事件/回调一样 |
pageAfterAnimation (pageData) | 和pageAfterAnimation事件/回调一样 |
pageBeforeRemove (pageData) | 和pageBeforeRemove事件/回调一样 |
addView (view) | 当用户通过app.addView()添加新的视图时触发,入参是已实例化的视图对象 |
loadPage (view, url, content) | 在新页面开始被加载时调用,此时页面尚未加入到DOM中 |
goBack (view, url, preloadOnly) | 在返回操作开始前调用 |
swipePanelSetTransform (views, panel, percentage) | 侧面板滑出/滑入的过程中调用 |
Leave a Reply