元々、artTemplate のソース コード分析に関する以前のコメントを共有したかったのですが、1 年経っても見つかりませんでした。そのため、当時のテンプレート エンジンの原理を分析した後、自分で試してみる必要がありました
その時にいくつかのテンプレートエンジンを比較したのを思い出したので、記念にメモしておきたいと思います。
ここで説明した js テンプレート エンジンはネイティブ JavaScript 構文を使用しているため、php のネイティブ テンプレート エンジンと非常によく似ています。
フロントエンド テンプレート エンジンの役割は何ですか?
1. DOM 構造を生成するために演算子を使用して文字列を結合する必要がなく、代わりに 1 つの要素 (内部の HTML テンプレート) または変数 ( template) またはテンプレートファイル
が必要です。2. メンテナンスが容易で、DOM 構造が変更された場合でも、ロジック コードを変更する必要はなく、対応するテンプレート (ファイル) を変更するだけで済みます。
3. テンプレートが .tpl に似たファイルの場合は、ブラウザーでロードして保存できます。 .tpl ファイルについて言えば、キャッシュ以外のこともできます。モジュール ローダーを通じて行うこともできます.tpl をモジュールとして使用すると、オンデマンドでファイルをロードできるので、帯域幅が節約され、ページが高速化されませんか?
4. 待機など
フロントエンドテンプレートエンジンの原理は何ですか?
原理は非常に単純です: オブジェクト (データ) + テンプレート (変数を含む) -> 文字列 (html)
フロントエンド テンプレート エンジンを実装するにはどうすればよいですか?
テンプレートを解析して、辞書に従ってテンプレートを関数に変換し、その関数を呼び出してオブジェクト (データ) を渡して文字列 (html) を出力します(もちろん、具体的な詳細はコードによって異なります)
次のように:
<script><br> var trs = [<br> {name:"隐形杀手",age:29,sex:"男"},<br> {name: "索拉",age:22,sex:"男"},<br> {name:"fesyo",age:23,sex:"女"},<br> {name:"恋妖壶",age :18,sex:"男"},<br> {name:"竜崎",age:25,sex:"男"},<br> {name:"你不懂的",age:30,sex: "女"}<br> ]</p>
<p> // var html = Ice("tpl",{<br> // trs: trs,<br> // href: "http://images.jb51.net/type4.jpg"<br> / / },{<br> // title: function(){<br> // return "<p>これはビデオヘルパーを使用しています输出力代码片断</p>"<br> // }<br> // });<br> var elem = document.getElementById('tpl');<br> var tpl = elem.innerHTML;</p>
<p> var html = Ice(tpl,{<br> trs: trs,<br> href: "http://images.jb51.net/type4.jpg"<br> },{<br> title: function (){<br> return "<p>これはビデオヘルパーを使用しています出力代码片断</p>"<br> }<br> });<br> console.log(html);<br> $("#content").html(html);<br><br></script>
简单的实现:
// 模板引擎路由函数
var ice = function (id, content) {
return ice[
typeof content === 'object' ? 'render' : 'compile'
].apply(ice, arguments);
};
ice.version = '1.0.0';
// 模板配置
var iConfig = {
openTag : '<%',
closeTag : '%>'
};
var isNewEngine = !!String.prototype.trim;
// 模板缓存
var iCache = ice.cache = {};
// 辅助函数
var iHelper = {
include : function (id, data) {
return iRender(id, data);
},
print : function (str) {
return str;
}
};
// 原型继承
var iExtend = Object.create || function (object) {
function Fn () {};
Fn.prototype = object;
return new Fn;
};
// 模板编译
var iCompile = ice.compile = function (id, tpl, options) {
var cache = null;
id && (cache = iCache[id]);
if (cache) {
return cache;
}
// [id | tpl]
if (typeof tpl !== 'string') {
var elem = document.getElementById(id);
options = tpl;
if (elem) {
// [id, options]
options = tpl;
tpl = elem.value || elem.innerHTML;
} else {
//[tpl, options]
tpl = id;
id = null;
}
}
options = options || {};
var render = iParse(tpl, options);
id && (iCache[id] = render);
return render;
};
// 模板渲染
var iRender = ice.render = function (id, data, options) {
return iCompile(id, options)(data);
};
var iForEach = Array.prototype.forEach ?
function(arr, fn) {
arr.forEach(fn)
} :
function(arr, fn) {
for (var i = 0; i < arr.length; i++) {
fn(arr[i], i, arr)
}
};
// 模板解析
var iParse = function (tpl, options) {
var html = [];
var js = [];
var openTag = options.openTag || iConfig['openTag'];
var closeTag = options.closeTag || iConfig['closeTag'];
// 根据浏览器采取不同的拼接字符串策略
var replaces = isNewEngine
?["var out='',line=1;", "out+=", ";", "out+=html[", "];", "this.result=out;"]
: ["var out=[],line=1;", "out.push(", ");", "out.push(html[", "]);", "this.result=out.join('');"];
// 函数体
var body = replaces[0];
iForEach(tpl.split(openTag), function(val, i) {
if (!val) {
return;
}
var parts = val.split(closeTag);
var head = parts[0];
var foot = parts[1];
var len = parts.length;
// html
if (len === 1) {
body += replaces[3] + html.length + replaces[4];
html.push(head);
} else {
if (head ) {
// code
// 去除空格
head = head
.replace(/^\s+|\s+$/g, '')
.replace(/[\n\r]+\s*/, '')
// 输出语句
if (head.indexOf('=') === 0) {
head = head.substring(1).replace(/^[\s]+|[\s;]+$/g, '');
body += replaces[1] + head + replaces[2];
} else {
body += head;
}
body = 'line =1;';
js.push(head);
}
html
if (足) {
_foot = foot.replace(/ ^[nr] s*/g, '');
if (!_foot) {
return;
}
body = replaces[3] html.length replaces[4];
html.push(foot);
}
}
});
body = "var Render=function(data){ice.mix(this, data);try{"
body
replaces[5]
"}catch(e){ice.log ('レンドエラー: ', line, 'line');ice.log('無効なステートメント: ', js[line-1]);throw e;}};"
"var proto=Render.prototype= iExtend(iHelper);"
"ice.mix(proto, options);"
"return function(data){return new Render(data).result;};";
var render = new Function('html', 'js', 'iExtend', 'iHelper', 'options', body);
return render(html, js, iExtend, iHelper, options);
};
ice.log = function () {
if (コンソールの種類 === 'unknown') {
return;
}
var args = Array.prototype.slice.call(arguments);
console.log.apply && console.log.apply(console, args);
};
// 合并对象
Ice.mix = function (target,source) {
for (source の var key) {
if (source.hasOwnProperty(key)) {
target[ key] = ソース[key];
}
}
};
// 注册関数
Ice.on = function (name, fn) {
iHelper[name] = fn;
};
// 清除缓存
Ice.clearCache = function () {
iCache = {};
};
// 変更構成
Ice.set = function (name, value) {
iConfig[name] = value;
};
// 公開接続
if (typeof module !== 'unknown' && module.exports) {
module.exports = template;
} else {
win.ice = Ice;
}
})(ウィンドウ);