为了账号安全,请及时绑定邮箱和手机立即绑定

前端模版引擎 - artTemplate 【上】

标签:
Html5

前言

因为工作的关系,我在接手项目的时候发现以前的同事都是用 artTemplate 进行模板渲染的。鉴于它方便用于 Ajax 请求数据并渲染的操作并在项目应用颇多,所以在我开发新的项目时,也把其作为一个主要的 library 插件,同时利用 gulp 进行 artTemplate 的预编译。下面就是我在新项目的使用过程中遇到的问题总结出来的经验以及 artTemplate 的使用方法,以供参考。

1. artTemplate介绍

一个渲染性能出众模板引擎,无论在 NodeJS 还是在浏览器中都可以运行。

webp

渲染模板速度对比



特性
  • 拥有接近 JavaScript 渲染极限的的性能

  • 调试友好:语法、运行时错误日志精确到模板所在行;支持在模板文件上打断点(Webpack Loader)

  • 支持压缩输出页面中的 HTML、CSS、JS 代码

  • 支持 Express、Koa、Webpack

  • 支持模板继承与子模板

  • 兼容 EJSUnderscoreLoDash 模板语法

  • 模板编译后的代码支持在严格模式下运行

  • 支持 JavaScript 语句与模板语法混合书写

  • 支持自定义模板的语法解析规则

  • 浏览器版本仅 6KB 大小

简单来说就是把以前你通过字符串拼接,再用 js append 到 dom 然后再填充数据的方式转变为另外一种便于阅读,代码优雅,性能更快的模板引擎。


2. artTemplate版本问题

因为在工作中遇到一些问题,在上网查文档的时候发现怎么 artTemplate 会有两个:

  1. artTemplate
    你在百度搜索时,第一个应该是 jQuery 插件库网站上介绍的 artTemplate-3.0 (这个也是本司原项目使用的版本),但是你会发现怎么很多链接都报 404 根本没使用。只好上 GitHub 上搜索,第一条的是 lhywork/artTemplate 链接还是 404 ,而且 Stars 数量这么少让我一度怀疑这个项目是不是报废了,因为我看到网上说作者停止维护该项目的信息。

  2. art-template
    在漫无目的的搜索后,我又发现一个叫 art-template 的库,上面的写法跟 artTemplate 差不多,但我在用辅助方法时却发现,原项目中的过滤器:
    template.helper(name, callback)
    不能用了,现在变成了
    template.defaults.imports.dateFormat = function(date, format){/*[code..]*/};
    一度以为这是两个不同的项目,导致我只能通过别人的博客寻找解决项目问题的方法。

直到我空下来说研究一下这个 art-template 并想找到 artTemplate 的作者询问些问题(因为在 gulp 预编译 artTemplate 的过程中出现了一个大坑),才发现原来两个作者都是同一个人,这位叫 糖饼 的大哥,一位爱改名字且爱删东西的大哥。

webp

说好的后会无期呢?

在短短几个月后他又发现 artTemplate 并没有被市面上的 dom模板所彻底打败,它依然有所作为,所以他又开发了一个新的版本并改名为 art-template。同时 Branch 下的 3.1.0 README.md 文档中的链接已经失效了( 他删掉了 ),这极大的不方便接手别人项目的同学学习。不过可以在 distdemo 再次取得部分的资料。

下面我将对这两个版本一些要点进行说明与记录,并且只进行简洁语法说明,方便以后大家使用。


3. artTemplate - 3.0

这个版本应该是目前应用最多的,其有以下五种使用方法,

a. 浏览器版本

直接引入 template.js 文件,下面示例代码中将包含:

  • 基础使用 - script 标签用法

  • 不转义 html 方法

  • 打印 - {{print}}

  • 过滤器 - template.help

  • debug

  • 模板外置

  • 模板嵌套 - 子模板(include)

demo-index.html - 基本 API 用法

<!DOCTYPE HTML><html><head><meta charset="UTF-8"><title>浏览器版本-demo</title><script class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="template.js"></script>  //引入 template.js</head><body><div id="content"></div><script id="test" type="text/html">  //模板编写在 <script> 标签中,添加 id 与 type 

{{if isAdmin}}   //条件判断写法<h1>{{title}}</h1>   //双花括号取值<ul>    /**
    循环数组数据渲染写法,等价于:
    for(var i in list){ 
       list[i] == value; // true
    }
   */

    {{each list as value i}}     
        <li>索引 {{i + 1}} :{{value}}</li>
    {{/each}}</ul>{{#value}}   //因为保护措施 artTemplate 会转义 HTML 代码为字符串输出,当你不需要转换时只需要前面加个 # 号{{print title value}}  // print 用法 - 会输出 “基本例子<span style="color:#F00">hello world!</span>”{{time | format}}   //格式化过滤器方法 {{data | 过滤器名字}}{{format(time)}}   //敢信还能这么写{{# format(time)}} //所以不转义可以和过滤器这样结合这个是不存在的空数据:{{undefined}}   //在 data 中不存在的数据 artTemplate 将会置空处理这个会报错,去除注释后自行检验://{{undefined.child}}  //但如果是数据绑定用的是点对象的方法,将会报错在 dom 中显示 {Template Error} 并在控制台中输出 bug ( ps. 想要捕获 bug 只能通过修改 artTemplate 源码的方法,因为它并有暴露 debug 接口 ){{/if}}</script><script>//数据必须是一个对象var data = {    title: '基本例子',    isAdmin: true,    list: ['文艺', '博客', '摄影', '电影', '民谣', '旅行', '吉他'],    value: '<span style="color:#F00">hello world!</span>'};//过滤器注册template.helper('format', function (date, format) {  // 对传入的data进行处理再 return 出去
  return format;
});//渲染var html = template('test', data);document.getElementById('content').innerHTML = html;</script></body></html>

compile.js - 模板外置方法

/**
大量页面希望引入同一个模板时,不可能每个页面都添加相同的 <script>
 所以需要从外部定义一个公共的 js 文件,让各个页面公用一个模板
*/
var source = '<ul>'
+    '{{each list as value i}}'
+        '<li>索引 {{i + 1}} :{{value}}</li>'
+    '{{/each}}'
+ '</ul>';

//但页面中引入公共 js 后,通过下面方法获取并渲染
var render = template.compile(source);
var html = render({
    list: ['摄影', '电影', '民谣', '旅行', '吉他']
});
document.getElementById('content').innerHTML = html;

//这样做的缺点很明显,需要通过字符串拼接的方式,不仅麻烦且不美观

———————————————————————————————————

//这里我再提供另一个方法,可以引用 html 文件作为模板,在页面中引入< link rel = "import" href = "module.html" id = "tests">模板则是一个 html 文件,module 用 <script> 包裹起来<script type="text/html" id="demo">
    <div>
        引入    </div></script>//然后我们再通过<script type="text/javascript">
        var module = document.querySelector('#tests').import.querySelector('script');        document.querySelector('head').appendChild(module);        var data = {};        var html = template('demo', data);        document.getElementById('content').innerHTML = html;</script>// rel = "import" 可能存在兼容问题,低版本的浏览器慎用。

include.html - 模板嵌套代码

<body>
    <div id="content"></div>
    <script id="test" type="text/html">
        <h1>{{title}}</h1> 
        {{include 'list'}}  // 默认使用当前数据        {{include 'list' list}}  // 可以指定数据    </script>
    <script id="list" type="text/html">
        <ul>
            {{each list as value i}}
            <li>索引 {{i + 1}} :{{value}}</li>
            {{/each}}
        </ul>
    </script>

    <script>
        var data = {            title: '嵌入子模板',            list: ['文艺', '博客', '摄影', '电影', '民谣', '旅行', '吉他']
        };        var html = template('test', data);        document.getElementById('content').innerHTML = html;    </script></body>
b. NodeJS 应用

当你想在 Node 环境下运用 artTemplate 时,可以采用下面的方法:

安装

$ cnpm install art-template@3.1.3

因为现在 art-template 已经是开发到 4.12.1 了所以想要兼容上一个版本的 artTemplate 需要下载对应版本的 npm 插件。

ps. 想查看某个插件含有多少的版本可以敲以下命令

$ cnpm view art-template versions

使用

var template = require('art-template');var data = {list: ["aui", "test"]};var html = template(__dirname + '/index/main', data);

ps. 更多配置与详情自行查看 GitHub 说明

接下来就是预编译模式,其意思是默认将整个目录的模板(html文件)压缩打包到一个名为 template.js 的脚本中,并无需引用浏览器版本的 template.js ,可直接在页面中使用它:

<script class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="tpl/build/template.js"></script><script>
    var html = template('news/list', data);  // 这里的模板 ID 就是你存放模板的路径
    document.getElementById('list').innerHTML = html;</script>
c. node工具 -  tmodjs

安装

$ cnpm install -g tmodjs

编译

tmod [模板目录] [配置参数] [模板输出目录]
示例:$ tmod ./tpl --output ./build

配置参数

插件介绍
--debug输出调试版本
--charset value定义模板编码,默认utf-8
--output value定义输出目录,默认./build
--type value定义输出模块格式,默认default,可选cmd、amd、commonjs
--no-watch关闭模板目录监控
--version显示版本号
--help显示帮助信息

插件配置

插件的配置则是写在 package.json 文件中:

{    "name": "template",    "version": "1.0.0",    "dependencies": {        "tmodjs": "1.0.0"
    },    "tmodjs-config": {        "output": "./build",        "charset": "utf-8",        "syntax": "simple",        "helpers": null,        "escape": true,        "compress": true,        "type": "default",        "runtime": "template.js",        "combo": true,        "minify": true,        "cache": false
    }
}
字段类型默认值说明
outputString"./build"编译输出目录设置。如果设置为 false 则不输出
charsetString"utf-8"模板使用的编码(暂时只支持 utf-8)
syntaxString"simple"定义模板采用哪种语法。可选:simple、native
helpersStringnull自定义辅助方法路径
escapeBooleantrue是否过滤 XSS。如果后台给出的数据已经进行了 XSS 过滤,就可以关闭模板的过滤以提升模板渲染效率
compressBooleantrue是否压缩 HTML 多余空白字符
typeString"default"输出的模块类型,可选:default、cmd、amd、commonjs
runtimeString"template.js"设置输出的运行时名称
aliasStringnull设置模块依赖的运行时路径(仅针对于非default的类型模块配置字段。如果不指定模块内部会自动使用相对 runtime 的路径)
comboBooleantrue是否合并模板(仅针对于 default 类型的模块)
minifyBooleantrue是否输出为压缩的格式
cacheBooleantrue是否开启编译缓存
verboseBooleantrue是否打印日志
d. Grunt插件 -  grunt-tmod

@Jsonzhang 开发

安装

$ cnpm install grunt-tmod --save-dev//加载插件grunt.loadNpmTasks('grunt-tmod');

使用

module.exports = function(grunt){

    grunt.initConfig({        tmod: {            template: {                src: './tpl/**/*.html',                dest: './dist/template.js',                options: {                    combo: true,                    base: './tpl/src'  // 这个什么意思看下面的 Gulp 说明,很重要
                } 
            }
        }
    });


    grunt.loadNpmTasks('grunt-tmod');

    grunt.registerTask('default', ['tmod']);

};//更多查看 GitHub 项目地址
e. Gulp插件 -  gulp-tmod

@lichunqiang 开发

安装

$ cnpm install gulp-tmod --save-dev

使用

var tmodjs = require('gulp-tmod');//编译功能代码gulp.task('art', function() {    var stream = gulp.src(src.common.template)
        .pipe(tmod({            templateBase: 'app/src/common/template'
        }))
        .pipe(gulp.dest(dist.common.template));    console.log("artTemplate编译完成!");    return stream;
});

由于本人目前就是采用该方法,所以理解较深,使用过程中有几点的需要注意的问题稍微跟大家提及一下,其他方法也可以参照使用:

  1. templateBase
    这里的 templateBase 是替换掉生成的 template.js 文件中模板ID的,比如我们的模板是 “app/src/common/template/demo.html” ,这时候引用改模板是这样使用的

//默认去掉 .html 后缀var html = template('app/src/common/template/demo', data);document.getElementById('content').innerHTML = html;

当我们设置了 base 值

 .pipe(tmod({            templateBase: 'app/src/common/template'
        }))// 引用时只需要var html = template('demo', data);document.getElementById('content').innerHTML = html;
  1. 监察的写法
    利用 Gulp 的 watch API

//artTemplate
    var watcherArt = gulp.watch('app/src/common/template/**/**.html', ['art']);
    watcherArt.on('change', function(event) {        console.log('该模板 ' + event.path + ' was ' + event.type + ', 执行artTemplate编译...');
    });
  1. 当编译完成后,template.js 却没有任何改变 ( 绝对重点
    这个是很危险的一个坑,它不会在控制台中输出 bug ,而是正常的完成编译。让你一直以为是插件,还是环境什么的问题,这曾经浪费我大量的时间找 bug 。后来有一个报 bug 的文件一闪而过,我到现在都不知道它是在什么情况下才会生成。

不过你只要记住!它编译后没改变文件的原因只有一个,就是你丫的模板 html
的语法写错了!

重新检查一下,再次编译吧!

ps. 模板的 html 文件就是简单的 dom 不需要干别的事情,例:

<section class="module-main">
    <header>
        <span class="title-circle title-circle-blue"></span>{{title}}        <div class="triangle-btn showArticle">
            <span class="mui-icon mui-icon-arrowdown "></span>
        </div>
    </header>
    <div class="article">
        {{data}}    </div></section>

我看这篇幅已经很长了,所以决定分为上下两篇~

下篇《 前端模版引擎 - art-template【下】》



作者:Max_Law
链接:https://www.jianshu.com/p/b5dffff259be


点击查看更多内容
1人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消