前端模版引擎 - artTemplate 【上】

前言

因為工作的關系,我在接手項目的時候發現以前的同事都是用 artTemplate 進行模板渲染的。鑒于它方便用于 Ajax 請求數據并渲染的操作并在項目應用頗多,所以在我開發新的項目時,也把其作為一個主要的 library 插件,同時利用 gulp 進行 artTemplate 的預編譯。下面就是我在新項目的使用過程中遇到的問題總結出來的經驗以及 artTemplate 的使用方法,以供參考。

1. artTemplate介紹

一個渲染性能出眾模板引擎,無論在 NodeJS 還是在瀏覽器中都可以運行。

特性
  • 擁有接近 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 的過程中出現了一個大坑),才發現原來兩個作者都是同一個人,這位叫 糖餅 的大哥,一位愛改名字且愛刪東西的大哥。

說好的后會無期呢?

在短短幾個月后他又發現 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 src="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 src="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
    }
}
字段 類型 默認值 說明
output String "./build" 編譯輸出目錄設置。如果設置為 false 則不輸出
charset String "utf-8" 模板使用的編碼(暫時只支持 utf-8)
syntax String "simple" 定義模板采用哪種語法。可選:simple、native
helpers String null 自定義輔助方法路徑
escape Boolean true 是否過濾 XSS。如果后臺給出的數據已經進行了 XSS 過濾,就可以關閉模板的過濾以提升模板渲染效率
compress Boolean true 是否壓縮 HTML 多余空白字符
type String "default" 輸出的模塊類型,可選:default、cmd、amd、commonjs
runtime String "template.js" 設置輸出的運行時名稱
alias String null 設置模塊依賴的運行時路徑(僅針對于非default的類型模塊配置字段。如果不指定模塊內部會自動使用相對 runtime 的路徑)
combo Boolean true 是否合并模板(僅針對于 default 類型的模塊)
minify Boolean true 是否輸出為壓縮的格式
cache Boolean true 是否開啟編譯緩存
verbose Boolean true 是否打印日志
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【下】》


該篇收錄于文集:前端技術棧

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,182評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,489評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,290評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,776評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,510評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,866評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,860評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,036評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,585評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,331評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,536評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,058評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,754評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,154評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,469評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,273評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,505評論 2 379

推薦閱讀更多精彩內容