mirror of
https://gitee.com/layui/layui.git
synced 2025-12-20 03:49:54 +08:00
feat: 重构 laytpl,增强对更多复杂模板结构的解析能力
This commit is contained in:
@@ -1,74 +1,45 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>视图模板引擎 - layui</title>
|
<title>模板引擎 - Layui</title>
|
||||||
|
<link rel="stylesheet" href="../src/css/layui.css">
|
||||||
|
<style>
|
||||||
|
.laytpl-demo{border: 1px solid #eee;}
|
||||||
|
.laytpl-demo:first-child{border-right: none;}
|
||||||
|
.laytpl-demo>textarea{position: relative; display: block; width:100%; height: 300px; padding: 11px; border: 0; box-sizing: border-box; resize: none; background-color: #fff; font-family: Courier New; font-size: 13px;}
|
||||||
|
.laytpl-demo>div:first-child{height: 32px; line-height: 32px; padding: 6px 11px; border-bottom: 1px solid #eee; background-color: #F8F9FA;}
|
||||||
|
.laytpl-demo .layui-tabs{top: -1px;}
|
||||||
|
|
||||||
<link rel="stylesheet" href="../src/css/layui.css">
|
#ID-tpl-view-body {
|
||||||
<style>
|
height: calc(100vh - 430px); overflow: auto; clear: both;
|
||||||
.laytpl-demo{border: 1px solid #EBEBEB;}
|
}
|
||||||
.laytpl-demo>textarea{position: relative; display: block; width:100%; height: 300px; padding: 11px; border: 0; box-sizing: border-box; resize: none; background-color: #fff; font-family: Courier New; font-size: 13px;}
|
#ID-tpl-view-body > div {
|
||||||
.laytpl-demo>div:first-child{height: 32px; line-height: 32px; padding: 6px 11px; border-bottom: 1px solid #EBEBEB; background-color: #F8F9FA;}
|
display: none;
|
||||||
</style>
|
}
|
||||||
</head>
|
pre {
|
||||||
<body>
|
padding: 16px; background-color: #20222A; color: #F8F9FA; font-family: 'Courier New',Consolas, monospace;
|
||||||
|
}
|
||||||
<div>
|
</style>
|
||||||
|
</head>
|
||||||
<div class="layui-row">
|
<body>
|
||||||
<div class="layui-col-xs6 laytpl-demo">
|
<div class="layui-padding-3">
|
||||||
<div>模板</div>
|
|
||||||
<textarea id="demoTPL1"><h1>{{ d.title }}</h1>
|
|
||||||
|
|
||||||
<p>转义输出(HTML):{{ d.desc }}</p>
|
|
||||||
<p>转义输出(HTML):{{= d.desc }}</p>
|
|
||||||
<p>原始输出(HTML):{{- d.desc }}</p>
|
|
||||||
{{#}}
|
|
||||||
|
|
||||||
<div class="layui-section">
|
|
||||||
<hr>
|
|
||||||
<ul>
|
|
||||||
{{# var str = "a b c";
|
|
||||||
layui.each(d.items, function(index, item){ }}
|
|
||||||
<li class="{{ index > 0 ? 'list' : '' }}">
|
|
||||||
<strong>{{ item.title }}</strong>
|
|
||||||
{{# if(item.content){ }}
|
|
||||||
<span>{{ item.content }}</span>
|
|
||||||
{{# } }}
|
|
||||||
<span>{{ item.time || '' }}</span>
|
|
||||||
{{ str }}
|
|
||||||
|
|
||||||
|
|
||||||
</li>
|
|
||||||
{{# }); if(d.items.length === 0){ }}
|
|
||||||
无数据
|
|
||||||
{{# } }}
|
|
||||||
</ul>
|
|
||||||
<hr>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{{ d.content || '' }}
|
|
||||||
{{ }} {{}}
|
|
||||||
{{ }}
|
|
||||||
|
|
||||||
\a
|
|
||||||
'12'"""""
|
|
||||||
"哈''哈"
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p>渲染时间:{{ layui.util.toDateString(new Date()) }}</p></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="layui-col-xs6 laytpl-demo">
|
|
||||||
<div class="layui-row">
|
<div class="layui-row">
|
||||||
<div class="layui-col-xs3">数据</div>
|
<div class="layui-col-xs6 laytpl-demo">
|
||||||
<div class="layui-col-xs9" style="text-align: right">
|
<div>
|
||||||
<button class="layui-btn layui-btn-sm layui-btn-primary" lay-on="createData">生成数据</button>
|
<a href="javascript:;" id="ID-tpl-src-title">
|
||||||
|
<cite>模板</cite>
|
||||||
|
<i class="layui-icon layui-icon-down layui-font-12"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<textarea id="ID-tpl-src"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="layui-col-xs6 laytpl-demo">
|
||||||
<textarea id="demoData1">
|
<div class="layui-row">
|
||||||
|
<div>数据</div>
|
||||||
|
</div>
|
||||||
|
<textarea id="ID-tpl-data">
|
||||||
{
|
{
|
||||||
"title": "标题",
|
"title": "标题",
|
||||||
"desc": "<a href=\"\" style=\"color:blue;\">一段描述</a>",
|
"desc": "<a href=\"\" style=\"color:blue;\">一段描述</a>",
|
||||||
@@ -91,156 +62,322 @@
|
|||||||
{"title": "list 3"}
|
{"title": "list 3"}
|
||||||
]
|
]
|
||||||
}</textarea>
|
}</textarea>
|
||||||
</div>
|
|
||||||
<div class="layui-col-xs12 laytpl-demo">
|
|
||||||
<div class="layui-row">
|
|
||||||
<div class="layui-col-xs4">视图</div>
|
|
||||||
<div class="layui-col-xs4" style="text-align: center">
|
|
||||||
<button class="layui-btn layui-btn-sm layui-btn-primary" lay-on="test1">性能测试</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-col-xs4" style="text-align: right">
|
<div class="layui-col-xs12 laytpl-demo" style="border-top: none;">
|
||||||
<span id="demoViewTime"></span>
|
<div class="layui-row">
|
||||||
|
<div class="layui-col-xs4 layui-tabs" id="ID-tpl-view-header">
|
||||||
|
<ul class="layui-tabs-header">
|
||||||
|
<li>视图</li>
|
||||||
|
<li>源码</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="layui-col-xs4" style="text-align: center">
|
||||||
|
<button class="layui-btn layui-btn-sm layui-btn-border" lay-on="test">性能测试</button>
|
||||||
|
</div>
|
||||||
|
<div class="layui-col-xs4" style="text-align: right">
|
||||||
|
<span class="layui-badge" id="ID-tpl-time"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="ID-tpl-view-body">
|
||||||
|
<div class="layui-show layui-padding-3 layui-text" id="ID-tpl-view"></div>
|
||||||
|
<div><pre id="ID-tpl-view-code"></pre></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-padding-sm" id="demoView1"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<script type="type/html" template id="demoTplCommon">
|
|
||||||
公共模板 - {{ d.title }}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script type="type/html" template id="demoTplList">
|
|
||||||
{{# if(d.items && d.items.length > 0){ }}
|
|
||||||
<ul>
|
|
||||||
{{# layui.each(d.items, function(index, item){ }}
|
|
||||||
<li><strong>{{ item.title }}</strong>{{ laytpl.include('demoTplList', {items: item.child}) }}</li>
|
|
||||||
{{# }); }}
|
|
||||||
</ul>
|
|
||||||
{{# } }}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script type="type/html" template id="laytplTestTpl">
|
<script type="text/html" id="ID-tpl-template-modern">
|
||||||
{{# for(var i = 0; i < d.items.length; i++){ }}
|
<h2>
|
||||||
第{{= d.items[i].index }}个,Name: {{- d.items[i].name }} Number: {{= d.items[i].number }}
|
{{= d.title }} - {{= d.title ? '#' : '' }}
|
||||||
{{# } }}
|
{{ if(true){ }}AAAA{{='A'}}{{ } }}
|
||||||
</script>
|
</h2>
|
||||||
|
{{- include('ID-tpl-template-common', {title: '头部'}) }}
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<p>转义输出:{{= d.desc }}</p>
|
||||||
|
<p>原文输出:{{- d.desc }}</p>
|
||||||
|
|
||||||
|
{{# 这是一段注释。仅在模板中显示,不在视图中输出 }}
|
||||||
|
|
||||||
|
{{!
|
||||||
|
这是一段不进行模板解析的区域,可显示原始界定符:
|
||||||
|
{{ let a = 0; }}、{{= escape }}、{{- source }}、{{# comments }}、{{! ignore !}}
|
||||||
|
!}}
|
||||||
|
|
||||||
|
{{# 空主体测试 }}
|
||||||
|
{{}} {{ }} {{ }} {{= }} {{=}} {{= }}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{{- !0 ? '<br>循环输出:' : '' }}
|
||||||
|
<hr>
|
||||||
|
<ul>
|
||||||
|
{{
|
||||||
|
var str = "一级列表 a \\ b c";
|
||||||
|
d.items.forEach(function(value, index) {
|
||||||
|
}}
|
||||||
|
<li class="{{= index > 0 ? 'list' : '' }}">
|
||||||
|
<strong>{{= value.title }}</strong>
|
||||||
|
{{ if(value.content){ }}
|
||||||
|
<span>{{= value.content }}</span>
|
||||||
|
{{ } }}
|
||||||
|
<span>{{= value.time || '' }}</span>
|
||||||
|
{{= str }}
|
||||||
|
{{- include('ID-tpl-template-list', { items: value.child }) }}
|
||||||
|
</li>
|
||||||
|
{{ }); }}
|
||||||
|
{{ if(d.items.length === 0){ }}
|
||||||
|
无数据
|
||||||
|
{{ } }}
|
||||||
|
</ul>
|
||||||
|
<hr>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="../src/layui.js"></script>
|
<div>
|
||||||
<script>
|
{{= d.content || '' }}
|
||||||
layui.use(['laytpl', 'util'], function(){
|
\反斜杠 | '单引号' "双引号" ""''"" | "左双右单' | '左单右双"
|
||||||
var laytpl = layui.laytpl;
|
</div>
|
||||||
var util = layui.util;
|
|
||||||
var $ = layui.$;
|
|
||||||
|
|
||||||
// 获取模板和数据
|
|
||||||
var get = function(type){
|
|
||||||
return {
|
|
||||||
template: $('#demoTPL1').val(), // 获取模板
|
|
||||||
data: function(){ // 获取数据
|
|
||||||
try {
|
|
||||||
return JSON.parse($('#demoData1').val());
|
|
||||||
} catch(e){
|
|
||||||
$('#demoView1').html(e);
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
var data = get();
|
|
||||||
|
|
||||||
// 耗时计算
|
|
||||||
var startTime = new Date().getTime(), timer = function(startTime, title){
|
|
||||||
var endTime = new Date().getTime();
|
|
||||||
$('#demoViewTime').html((title || '模板解析耗时:')+ (endTime - startTime) + 'ms');
|
|
||||||
};
|
|
||||||
|
|
||||||
// 全局设置
|
<p>渲染时间:{{= layui.util.toDateString(new Date()) }}</p>
|
||||||
/*laytpl.config({
|
</script>
|
||||||
open: '<%',
|
|
||||||
close: '%>'
|
|
||||||
});*/
|
|
||||||
|
|
||||||
// 渲染模板
|
<script type="text/html" id="ID-tpl-template-common">
|
||||||
var thisTpl = laytpl(data.template);
|
公共模板 - {{= d.title }}
|
||||||
|
</script>
|
||||||
|
|
||||||
// 执行渲染
|
<script type="text/html" id="ID-tpl-template-list">
|
||||||
thisTpl.render(data.data, function(view){
|
{{ if(d.items && d.items.length > 0){ }}
|
||||||
timer(startTime);
|
<ul>
|
||||||
$('#demoView1').html(view);
|
{{ layui.each(d.items, function(index, item){ }}
|
||||||
});
|
<li>
|
||||||
|
<strong>{{= item.title }}</strong>
|
||||||
// 编辑
|
{{- include('ID-tpl-template-list', {items: item.child}) }}
|
||||||
$('.laytpl-demo textarea').on('input', function(){
|
</li>
|
||||||
var data = get();
|
{{ }); }}
|
||||||
if(!data.data) return;
|
</ul>
|
||||||
|
{{ } }}
|
||||||
// 计算模板渲染耗时
|
</script>
|
||||||
var startTime = new Date().getTime();
|
|
||||||
|
<script type="text/html" id="ID-tpl-template-test">
|
||||||
// 若模板有变化,则重新解析模板;若模板没变,数据有变化,则从模板缓存中直接渲染(效率大增)
|
{{ for (var i = 0; i < d.items.length; i++) { }}
|
||||||
if(this.id === 'demoTPL1'){
|
第 {{= d.items[i].index }} 个,Name: {{- d.items[i].name }} Number: {{= d.items[i].number }}
|
||||||
thisTpl.parse(data.template, data.data); // 解析模板
|
{{ } }}
|
||||||
}
|
</script>
|
||||||
|
|
||||||
// 执行渲染
|
<!-- 旧版本模板 -->
|
||||||
thisTpl.render(data.data, function(view){
|
<script type="text/html" id="ID-tpl-template-legacy">
|
||||||
timer(startTime);
|
<h2>
|
||||||
$('#demoView1').html(view);
|
{{= d.title }} - {{= d.title ? '#' : '' }}
|
||||||
});
|
{{# if(true){ }}AAAA{{='A'}}{{# } }}
|
||||||
});
|
</h2>
|
||||||
|
|
||||||
// 事件
|
<hr>
|
||||||
util.on({
|
|
||||||
// 性能测试
|
<p>转义输出:{{ d.desc }}</p>
|
||||||
test1: function(){
|
<p>转义输出:{{= d.desc }}</p>
|
||||||
var dataLen = 1000 // 数据量
|
<p>原文输出:{{- d.desc }}</p>
|
||||||
var renderTimes = 1000; // 渲染次数
|
|
||||||
|
{{}} {{ }} {{ }} {{= }} {{=}} {{= }}
|
||||||
// 初始化数据
|
|
||||||
var data = {
|
<div>
|
||||||
title: '性能测试',
|
<ul>
|
||||||
items: function(items){
|
{{# var str = "a b c"; }}
|
||||||
for(var i = 0; i < dataLen; i++){
|
{{# layui.each(d.items, function(index, item) { }}
|
||||||
items.push({
|
<li class="{{= index > 0 ? 'list' : '' }}">
|
||||||
index: i
|
<strong>{{= item.title }}</strong>
|
||||||
,name: '<strong style="color: red;">张三</strong>'
|
{{# if(item.content){ }}
|
||||||
,number: 100+i
|
<span>{{= item.content }}</span>
|
||||||
|
{{# } }}
|
||||||
|
<span>{{= item.time || '' }}</span>
|
||||||
|
{{ str }}
|
||||||
|
</li>
|
||||||
|
{{# }); }}
|
||||||
|
{{# if (d.items.length === 0) { }}
|
||||||
|
无数据
|
||||||
|
{{# } }}
|
||||||
|
</ul>
|
||||||
|
<hr>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{{= d.content || '' }}
|
||||||
|
\反斜杠 | '单引号' "双引号" ""''"" | "左双右单' | '左单右双"
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>渲染时间:{{ layui.util.toDateString(new Date()) }}</p>
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="../src/layui.js"></script>
|
||||||
|
<script>
|
||||||
|
layui.use(['laytpl', 'util', 'tabs', 'dropdown'], function() {
|
||||||
|
var laytpl = layui.laytpl;
|
||||||
|
var util = layui.util;
|
||||||
|
var tabs = layui.tabs;
|
||||||
|
var dropdown = layui.dropdown;
|
||||||
|
var $ = layui.$;
|
||||||
|
|
||||||
|
// 默认设置
|
||||||
|
laytpl.config({
|
||||||
|
delimiterStyle: 'modern' // 初始采用新版界定符风格
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取模板和数据
|
||||||
|
var getData = function(type) {
|
||||||
|
return {
|
||||||
|
template: $('#ID-tpl-src').val(), // 获取模板
|
||||||
|
data: function(){ // 获取数据
|
||||||
|
try {
|
||||||
|
return JSON.parse($('#ID-tpl-data').val());
|
||||||
|
} catch(e){
|
||||||
|
$('#ID-tpl-view').html(e);
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 视图渲染
|
||||||
|
var renderView = function(html, startTime) {
|
||||||
|
timer(startTime);
|
||||||
|
$('#ID-tpl-view').html(html);
|
||||||
|
$('#ID-tpl-view-code').html(util.escape(html));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 生成模板
|
||||||
|
var createTemplate = function(opts) {
|
||||||
|
opts = $.extend({
|
||||||
|
delimiterStyle: 'modern'
|
||||||
|
}, opts);
|
||||||
|
|
||||||
|
// 初始化模板
|
||||||
|
var elem = $('#ID-tpl-template-'+ opts.delimiterStyle);
|
||||||
|
$('#ID-tpl-src').val(elem.html());
|
||||||
|
|
||||||
|
return opts;
|
||||||
|
};
|
||||||
|
var tplConfig = createTemplate();
|
||||||
|
var data = getData();
|
||||||
|
|
||||||
|
// 耗时计算
|
||||||
|
var timer = function(startTime, title) {
|
||||||
|
var endTime = new Date();
|
||||||
|
$('#ID-tpl-time').html((title || '模板解析耗时:')+ (endTime - startTime) + 'ms');
|
||||||
|
};
|
||||||
|
var startTime = new Date();
|
||||||
|
|
||||||
|
// 创建一个模板实例
|
||||||
|
var thisTpl = laytpl(data.template, {
|
||||||
|
condense: false, // 不处理连续空白符,即保留模板原始结构
|
||||||
|
delimiterStyle: tplConfig.delimiterStyle
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始渲染
|
||||||
|
thisTpl.render(data.data, function(html) {
|
||||||
|
renderView(html, startTime);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 编辑
|
||||||
|
$('.laytpl-demo textarea').on('input', function() {
|
||||||
|
var data = getData();
|
||||||
|
var startTime = new Date();
|
||||||
|
|
||||||
|
// 若模板有变化,则重新编译模板
|
||||||
|
if (this.id === 'ID-tpl-src') {
|
||||||
|
thisTpl.compile(data.template);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 若模板没变,数据有变化,则从模板缓存中直接渲染数据(效率大增)
|
||||||
|
thisTpl.render(data.data, function(html) {
|
||||||
|
renderView(html, startTime);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 事件
|
||||||
|
util.on({
|
||||||
|
// 性能测试
|
||||||
|
test: function() {
|
||||||
|
var dataLen = 1000 // 数据量
|
||||||
|
var renderTimes = 1000; // 渲染次数
|
||||||
|
|
||||||
|
// 初始化数据
|
||||||
|
var data = {
|
||||||
|
title: '性能测试',
|
||||||
|
items: function(items) {
|
||||||
|
for (var i = 0; i < dataLen; i++) {
|
||||||
|
items.push({
|
||||||
|
index: i,
|
||||||
|
name: '<strong style="color: red;">张三</strong>',
|
||||||
|
number: 100+i
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}([])
|
||||||
|
};
|
||||||
|
|
||||||
|
// 模板
|
||||||
|
var startTime = new Date();
|
||||||
|
for (var j = 0; j < renderTimes; j++) {
|
||||||
|
var template = document.getElementById('ID-tpl-template-test').innerHTML;
|
||||||
|
var html = laytpl(template).render(data);
|
||||||
|
}
|
||||||
|
renderView(html, startTime);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 局部自定义标签符
|
||||||
|
laytpl(`
|
||||||
|
<% var job = ["前端工程师"]; %>
|
||||||
|
<%= d.name %>是一名<%= job[d.index] %>。
|
||||||
|
`, {
|
||||||
|
open: '<%',
|
||||||
|
close: '%>'
|
||||||
|
}).render({
|
||||||
|
name: '张三',
|
||||||
|
index: 0
|
||||||
|
}, function(str) {
|
||||||
|
console.log(str); // 张三是一名前端工程师。
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// 由于 2.10.0 component 存在一个重载的问题,此处暂时手工解除 tabs 元素上的 id 标记
|
||||||
|
$('#ID-tpl-view-header').removeAttr('lay-tabs-id');
|
||||||
|
|
||||||
|
// 视图结果 tabs
|
||||||
|
tabs.render({
|
||||||
|
elem: '#ID-tpl-view-header',
|
||||||
|
body: ['#ID-tpl-view-body', '>div']
|
||||||
|
});
|
||||||
|
|
||||||
|
// 切换模板
|
||||||
|
dropdown.render({
|
||||||
|
elem: '#ID-tpl-src-title',
|
||||||
|
data: [{
|
||||||
|
title: '新版本模板',
|
||||||
|
delimiterStyle: 'modern'
|
||||||
|
}, {
|
||||||
|
title: '旧版本模板',
|
||||||
|
delimiterStyle: 'legacy'
|
||||||
|
}],
|
||||||
|
click: function(obj){
|
||||||
|
createTemplate({
|
||||||
|
delimiterStyle: obj.delimiterStyle
|
||||||
|
});
|
||||||
|
this.elem.children('cite').html(obj.title);
|
||||||
|
|
||||||
|
// 同步设置界定符风格
|
||||||
|
thisTpl.config.delimiterStyle = obj.delimiterStyle;
|
||||||
|
|
||||||
|
var data = getData();
|
||||||
|
var startTime = new Date();
|
||||||
|
|
||||||
|
// 重新渲染
|
||||||
|
thisTpl.compile(data.template).render(data.data, function(html) {
|
||||||
|
renderView(html, startTime);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return items;
|
})
|
||||||
}([])
|
});
|
||||||
};
|
</script>
|
||||||
|
</body>
|
||||||
// 模板
|
|
||||||
var startTime = new Date();
|
|
||||||
for(var j = 0; j < renderTimes; j++){
|
|
||||||
var template = document.getElementById('laytplTestTpl').innerHTML;
|
|
||||||
var html = laytpl(template).render(data);
|
|
||||||
}
|
|
||||||
timer(startTime, '本次测试耗时:');
|
|
||||||
$('#demoView1').html(html);
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 自定义标签符
|
|
||||||
laytpl(`
|
|
||||||
<%# var job = ["前端工程师"]; %>
|
|
||||||
<%= d.name %>是一名<%= job[d.type] %>。
|
|
||||||
`, {
|
|
||||||
open: '<%',
|
|
||||||
close: '%>'
|
|
||||||
}).render({
|
|
||||||
name: '张三',
|
|
||||||
type: 0
|
|
||||||
}, function(str){
|
|
||||||
console.log(str); // 张三是一名前端工程师。
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,162 +1,470 @@
|
|||||||
/**
|
/**
|
||||||
* laytpl 轻量模板引擎
|
* laytpl
|
||||||
|
* 轻量级通用模板引擎
|
||||||
*/
|
*/
|
||||||
|
|
||||||
layui.define(function(exports){
|
(function(global) {
|
||||||
"use strict";
|
'use strict';
|
||||||
|
|
||||||
// 默认属性
|
var MOD_NAME = 'laytpl';
|
||||||
var config = {
|
|
||||||
open: '{{', // 标签符前缀
|
|
||||||
close: '}}' // 标签符后缀
|
|
||||||
};
|
|
||||||
|
|
||||||
// 模板工具
|
// 实例接口
|
||||||
var tool = {
|
var thisModule = function() {
|
||||||
escape: function(html){
|
|
||||||
var exp = /[<"'>]|&(?=#[a-zA-Z0-9]+)/g;
|
|
||||||
if(html === undefined || html === null) return '';
|
|
||||||
|
|
||||||
html += '';
|
|
||||||
if(!exp.test(html)) return html;
|
|
||||||
|
|
||||||
return html.replace(/&(?!#?[a-zA-Z0-9]+;)/g, '&')
|
|
||||||
.replace(/</g, '<').replace(/>/g, '>')
|
|
||||||
.replace(/'/g, ''').replace(/"/g, '"');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 内部方法
|
|
||||||
var inner = {
|
|
||||||
exp: function(str){
|
|
||||||
return new RegExp(str, 'g');
|
|
||||||
},
|
|
||||||
// 错误提示
|
|
||||||
error: function(e, source){
|
|
||||||
var error = 'Laytpl Error: ';
|
|
||||||
typeof console === 'object' && console.error(error + e + '\n'+ (source || ''));
|
|
||||||
return error + e;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// constructor
|
|
||||||
var Class = function(template, options){
|
|
||||||
var that = this;
|
var that = this;
|
||||||
that.config = that.config || {};
|
var options = that.config;
|
||||||
that.template = template;
|
return {
|
||||||
|
config: options,
|
||||||
|
|
||||||
// 简单属性合并
|
/**
|
||||||
var extend = function(obj){
|
* 渲染模板
|
||||||
for(var i in obj){
|
* @param {Object} data - 模板数据
|
||||||
that.config[i] = obj[i];
|
* @param {Function} callback - 回调函数
|
||||||
|
* @returns {string} 渲染后的模板
|
||||||
|
*/
|
||||||
|
render: function(data, callback) {
|
||||||
|
options.data = data
|
||||||
|
var html = that.render();
|
||||||
|
|
||||||
|
// 如果传入目标元素选择器,则直接将模板渲染到目标元素中
|
||||||
|
if (options.target) {
|
||||||
|
var elem = document.querySelector(options.target);
|
||||||
|
if (elem) {
|
||||||
|
elem.innerHTML = html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
return typeof callback === 'function'
|
||||||
|
? (callback(html), this)
|
||||||
|
: html;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编译新的模板
|
||||||
|
* @param {string} template - 模板
|
||||||
|
* @returns {this}
|
||||||
|
*/
|
||||||
|
compile: function(template) {
|
||||||
|
options.template = template;
|
||||||
|
delete that.compilerCache; // 清除模板缓存
|
||||||
|
// that.compile(template);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模板编译错误事件
|
||||||
|
* @param {Function} callback
|
||||||
|
* @returns {this}
|
||||||
|
*/
|
||||||
|
error: function(callback) {
|
||||||
|
callback && (options.error = callback);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以下为兼容旧版本相关方法
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 解析并渲染模板
|
||||||
|
parse: function(template, data) {
|
||||||
|
return this.compile(template).render(data);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
extend(config);
|
|
||||||
extend(options);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 标签正则
|
// 模板内部变量
|
||||||
Class.prototype.tagExp = function(type, _, __){
|
var vars = {
|
||||||
var options = this.config;
|
// 字符转义
|
||||||
var types = [
|
escape: function(html) {
|
||||||
'#([\\s\\S])+?', // js 语句
|
var exp = /[<"'>]|&(?=#[a-zA-Z0-9]+)/g;
|
||||||
'([^{#}])*?' // 普通字段
|
if (html === undefined || html === null) return '';
|
||||||
][type || 0];
|
html = ''+ html;
|
||||||
|
if (!exp.test(html)) return html;
|
||||||
return inner.exp((_||'') + options.open + types + options.close + (__||''));
|
return html.replace(exp, function(str) {
|
||||||
|
return '&#'+ str.charCodeAt(0) + ';';
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 模版解析
|
// 组件工具类方法
|
||||||
Class.prototype.parse = function(template, data){
|
var tools = {
|
||||||
|
regex: function(str, mod) {
|
||||||
|
return new RegExp(str, mod || 'g');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误提示
|
||||||
|
* @param {string} e - 原始错误信息
|
||||||
|
* @param {Object} opts - 自定义选项
|
||||||
|
* @param {Function} error - 错误回调
|
||||||
|
* @returns {string} - 错误提示
|
||||||
|
*/
|
||||||
|
error: function(e, opts, error) {
|
||||||
|
opts = opts || {};
|
||||||
|
opts = Object.assign({
|
||||||
|
debug: '',
|
||||||
|
message: 'Laytpl '+ (opts.type || '') +'Error: ' + e
|
||||||
|
}, opts);
|
||||||
|
|
||||||
|
// 向控制台输出错误信息
|
||||||
|
typeof console === 'object' && console.error(opts.message, '\n', opts.debug, '\n', opts);
|
||||||
|
typeof error === 'function' && error(opts); // 执行错误回调
|
||||||
|
return opts.message; // 向视图返回错误提示
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 默认配置
|
||||||
|
var config = {
|
||||||
|
open: '{{', // 开头界定符
|
||||||
|
close: '}}', // 闭合界定符
|
||||||
|
cache: true, // 是否开启模板缓存,以便下次渲染时不重新编译模板
|
||||||
|
condense: true, // 是否压缩模板空白符,如:将多个连续的空白符压缩为单个空格
|
||||||
|
delimiterStyle: '' // 界定符风格。默认采用 < 2.11 的风格,设置 modern 则采用 2.11+ 风格
|
||||||
|
};
|
||||||
|
|
||||||
|
// 构造器
|
||||||
|
var Class = function(template, options) {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
// 选项合并
|
||||||
|
options = that.config = Object.assign({
|
||||||
|
template: template
|
||||||
|
}, config, options);
|
||||||
|
|
||||||
|
// 当前实例的模板内工具
|
||||||
|
that.vars = Object.assign({
|
||||||
|
/**
|
||||||
|
* 引用外部模板。若在 Node.js 环境,可通过重置该方法实现模板文件导入
|
||||||
|
* @param {string} id - 模板 ID
|
||||||
|
* @param {Object} data - 模板数据
|
||||||
|
* @returns {string} 模板渲染后内容
|
||||||
|
*/
|
||||||
|
include: function(id, data) {
|
||||||
|
var elem = document.getElementById(id);
|
||||||
|
var template = elem ? elem.innerHTML : '';
|
||||||
|
return template ? that.render(template, data) : '';
|
||||||
|
}
|
||||||
|
}, vars);
|
||||||
|
|
||||||
|
// 编译模板
|
||||||
|
that.compile(options.template);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染
|
||||||
|
* @param {Object} template - 模板
|
||||||
|
* @param {Object} data - 数据
|
||||||
|
* @returns {string} 渲染后的模板内容
|
||||||
|
*/
|
||||||
|
Class.prototype.render = function(template, data) {
|
||||||
|
var that = this;
|
||||||
|
var options = that.config;
|
||||||
|
|
||||||
|
// 获得模板渲染函数
|
||||||
|
var compiler = template ? that.compile(template) : (
|
||||||
|
that.compilerCache || that.compile(options.template)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 获取渲染后的字符
|
||||||
|
var html = function() {
|
||||||
|
data = data || options.data || {};
|
||||||
|
try {
|
||||||
|
return compiler(data);
|
||||||
|
} catch(e) {
|
||||||
|
template = template || options.template;
|
||||||
|
return tools.error(e, {
|
||||||
|
debug: that.checkErrorArea(template, data),
|
||||||
|
template: template,
|
||||||
|
type: 'Render'
|
||||||
|
}, options.error);
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
// 缓存编译器
|
||||||
|
if (options.cache && !template) {
|
||||||
|
that.compilerCache = compiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html; // 返回渲染后的字符
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编译模板
|
||||||
|
* @param {string} template - 原始模板
|
||||||
|
* @returns {Function} 模板编译器,用于后续数据渲染
|
||||||
|
*/
|
||||||
|
Class.prototype.compile = function(template) {
|
||||||
var that = this;
|
var that = this;
|
||||||
var options = that.config;
|
var options = that.config;
|
||||||
var source = template;
|
var source = template;
|
||||||
var jss = inner.exp('^'+ options.open +'#', '');
|
var openDelimiter = options.open;
|
||||||
var jsse = inner.exp(options.close +'$', '');
|
var closeDelimiter = options.close;
|
||||||
|
var condense = options.condense;
|
||||||
|
var regex = tools.regex;
|
||||||
|
const placeholder = '\u2028'; // Unicode 行分隔符
|
||||||
|
|
||||||
// 模板必须为 string 类型
|
// console.log('compile');
|
||||||
if(typeof template !== 'string') return template;
|
|
||||||
|
|
||||||
// 正则解析
|
// 若无模板
|
||||||
template = template.replace(/\s+|\r|\t|\n/g, ' ')
|
if (!template) {
|
||||||
.replace(inner.exp(options.open +'#'), options.open +'# ')
|
return function() {
|
||||||
.replace(inner.exp(options.close +'}'), '} '+ options.close).replace(/\\/g, '\\\\')
|
|
||||||
|
|
||||||
// 不匹配指定区域的内容
|
|
||||||
.replace(inner.exp(options.open + '!(.+?)!' + options.close), function(str){
|
|
||||||
str = str.replace(inner.exp('^'+ options.open + '!'), '')
|
|
||||||
.replace(inner.exp('!'+ options.close), '')
|
|
||||||
.replace(inner.exp(options.open + '|' + options.close), function(tag){
|
|
||||||
return tag.replace(/(.)/g, '\\$1')
|
|
||||||
});
|
|
||||||
return str
|
|
||||||
})
|
|
||||||
|
|
||||||
// 匹配 JS 语法
|
|
||||||
.replace(/(?="|')/g, '\\').replace(that.tagExp(), function(str){
|
|
||||||
str = str.replace(jss, '').replace(jsse, '');
|
|
||||||
return '";' + str.replace(/\\(.)/g, '$1') + ';view+="';
|
|
||||||
})
|
|
||||||
|
|
||||||
// 匹配普通输出语句
|
|
||||||
.replace(that.tagExp(1), function(str){
|
|
||||||
var start = '"+laytpl.escape(';
|
|
||||||
if(str.replace(/\s/g, '') === options.open + options.close){
|
|
||||||
return '';
|
return '';
|
||||||
}
|
};
|
||||||
str = str.replace(inner.exp(options.open + '|' + options.close), '');
|
}
|
||||||
if(/^=/.test(str)){
|
|
||||||
str = str.replace(/^=/, '');
|
|
||||||
} else if(/^-/.test(str)){
|
|
||||||
str = str.replace(/^-/, '');
|
|
||||||
start = '"+(';
|
|
||||||
}
|
|
||||||
return start + str.replace(/\\(.)/g, '$1') + ')+"';
|
|
||||||
});
|
|
||||||
|
|
||||||
template = '"use strict";var view = "' + template + '";return view;';
|
/**
|
||||||
|
* 完整界定符正则
|
||||||
|
* @param {string[]} cores - 界定符内部核心表达式,含:前置、主体、后置
|
||||||
|
* @param {Object} sides - 界定符两侧外部表达式
|
||||||
|
* @returns {RegExp}
|
||||||
|
*/
|
||||||
|
var delimRegex = function(cores, sides) {
|
||||||
|
var arr = [
|
||||||
|
'(?:'+ openDelimiter + (cores[0] || '') +'\\s*)', // 界定符前置
|
||||||
|
'('+ (cores[1] || '[\\s\\S]') +'*?)', // 界定符主体
|
||||||
|
'(?:\\s*'+ (cores[2] || '') + closeDelimiter +')' // 界定符后置
|
||||||
|
];
|
||||||
|
sides = sides || {};
|
||||||
|
sides.before && arr.unshift(sides.before); // 界定符前面的表达式
|
||||||
|
sides.after && arr.push(sides.after); // 界定符后面的表达式
|
||||||
|
return regex(arr.join(''));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 匹配非输出类型界定符两侧的换行符和空白符,避免渲染后占用一行
|
||||||
|
var sidesRegex = condense ? ['', ''] : ['(?:(?:\\n)*\\s*)', '(?:\\s*?)'];
|
||||||
|
var delimSides = {
|
||||||
|
before: sidesRegex[0],
|
||||||
|
after: sidesRegex[1]
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理多余符号
|
||||||
|
* @param {string} body - 界定符主体字符
|
||||||
|
* @param {boolean} nowrap - 是否强制不换行
|
||||||
|
* @returns {string} 清理后的字符
|
||||||
|
*/
|
||||||
|
var clear = function(body, nowrap) {
|
||||||
|
if (!condense) {
|
||||||
|
// 还原语句中的 Unicode 行分隔符
|
||||||
|
body = body.replace(regex(placeholder), nowrap ? '' : '\n');
|
||||||
|
}
|
||||||
|
body = body.replace(/\\(\\|")/g, '$1'); // 去除多余反斜杠
|
||||||
|
return body;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 纠正界定符结构
|
||||||
|
var correct = function(tpl) {
|
||||||
|
return tpl.replace(regex('([}\\]])'+ closeDelimiter), '$1 '+ closeDelimiter);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 模板解析
|
||||||
|
var parse = that.parse = function(tpl) {
|
||||||
|
tpl = tpl || '';
|
||||||
|
if (!tpl) return tpl;
|
||||||
|
|
||||||
|
// 压缩连续空白符
|
||||||
|
if (condense) {
|
||||||
|
tpl = tpl.replace(/\t/g, ' ').replace(/\s+/g, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始整理
|
||||||
|
tpl = correct(tpl) // 纠正界定符
|
||||||
|
.replace(/(?=\\|")/g, '\\') // 转义反斜杠和双引号
|
||||||
|
.replace(/\r?\n/g, condense ? '' : placeholder); // 整理换行符
|
||||||
|
|
||||||
|
// 忽略界定符 - 即区域中的内容不进行界定符解析
|
||||||
|
tpl = tpl.replace(delimRegex(['!', '', '!'], delimSides), function(str, body) {
|
||||||
|
body = body.replace(regex(openDelimiter + '|' + closeDelimiter), function(tag) {
|
||||||
|
return tag.replace(/(?=.)/g, '\\');
|
||||||
|
});
|
||||||
|
return body;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 模板字符拼接
|
||||||
|
var strConcatenation = function(body) {
|
||||||
|
// 通过对 20k+ 行的模板进行编译测试, 发现 Chrome `+=` 性能竟优于 `push`
|
||||||
|
// 1k 次循环 + 1k 行数据量进行模板编译+渲染,发现 `+=` 性能仍然优于 `push`
|
||||||
|
// 考虑可能是 V8 做了 Ropes 结构优化? 或跟模板采用「静态拼接」的实现有关(可能性更大)
|
||||||
|
return ['";', body, '__laytpl__+="'].join('\n');
|
||||||
|
// return ['");', body, '__laytpl__.push("'].join('\n');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 解析「输出类型」界定符
|
||||||
|
var output = function(str, delimiter, body) {
|
||||||
|
var _escape;
|
||||||
|
|
||||||
|
if (!body) return '';
|
||||||
|
body = clear(body, true);
|
||||||
|
|
||||||
|
// 输出方式
|
||||||
|
if (delimiter === '-') { // 原文输出,即不对 HTML 原文进行转义
|
||||||
|
_escape = '';
|
||||||
|
} else { // 转义输出
|
||||||
|
_escape = '_escape';
|
||||||
|
}
|
||||||
|
|
||||||
|
return body ? strConcatenation(
|
||||||
|
'__laytpl__+='+ _escape +'('+ body +');'
|
||||||
|
// '__laytpl__.push('+ _escape +'('+ body +'));'
|
||||||
|
) : '';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 解析「语句类型」界定符
|
||||||
|
var statement = function(str, body) {
|
||||||
|
if (!body) return '';
|
||||||
|
body = clear(body);
|
||||||
|
return strConcatenation(body);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 界定符风格
|
||||||
|
if (options.delimiterStyle === 'modern') { // 2.11+ 版本风格
|
||||||
|
// 注释界定符 - 仅在模板中显示,不进行解析,也不在视图中输出
|
||||||
|
tpl = tpl.replace(delimRegex(['#'], delimSides), '');
|
||||||
|
// 输出类型界定符
|
||||||
|
tpl = tpl.replace(delimRegex(['(=|-)']), output);
|
||||||
|
// 语句类型界定符
|
||||||
|
tpl = tpl.replace(delimRegex([], delimSides), statement);
|
||||||
|
} else { // < 2.11 版本风格
|
||||||
|
// 语句类型界定符
|
||||||
|
tpl = tpl.replace(delimRegex(['#'], delimSides), statement);
|
||||||
|
// 输出类型界定符
|
||||||
|
tpl = tpl.replace(delimRegex(['(=|-)*']), output);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 恢复换行符
|
||||||
|
if (!condense) {
|
||||||
|
tpl = tpl.replace(regex(placeholder), '\\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
return tpl;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 创建模板编译器
|
||||||
|
var createCompiler = that.createCompiler = function(template) {
|
||||||
|
var codeBuilder = [
|
||||||
|
'function(d){',
|
||||||
|
'"use strict";',
|
||||||
|
'var __laytpl__="",'+
|
||||||
|
function() { // 内部变量
|
||||||
|
// 内部方法
|
||||||
|
var arr = [];
|
||||||
|
for (var key in that.vars) {
|
||||||
|
arr.push(((key === 'escape' ? '_' : '') + key) +'=laytpl.'+ key);
|
||||||
|
}
|
||||||
|
return arr.join(',');
|
||||||
|
}() + ';',
|
||||||
|
'__laytpl__="'+ parse(template) +'";',
|
||||||
|
'return __laytpl__;',
|
||||||
|
// '__laytpl__.push("'+ parse(template) +'");',
|
||||||
|
// 'return __laytpl__.join("");',
|
||||||
|
'};'
|
||||||
|
].join('\n');
|
||||||
|
// console.log(codeBuilder);
|
||||||
|
return new Function('laytpl', 'return '+ codeBuilder)(that.vars);
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/**
|
return createCompiler(template); // 返回编译器
|
||||||
* 请注意: 开发者在使用模板语法时,需确保模板中的 JS 语句不来自于页面用户输入。
|
|
||||||
* 即模板中的 JS 语句必须在页面开发者自身的可控范围内,否则请避免使用该模板解析。
|
|
||||||
*/
|
|
||||||
that.cache = template = new Function('d, laytpl', template);
|
|
||||||
return template(data, tool);
|
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
delete that.cache;
|
delete that.compilerCache;
|
||||||
return inner.error(e, source);
|
return function() {
|
||||||
|
return tools.error(e, {
|
||||||
|
debug: that.checkErrorArea(source),
|
||||||
|
template: source,
|
||||||
|
type: 'Compile'
|
||||||
|
}, options.error);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 数据渲染
|
/**
|
||||||
Class.prototype.render = function(data, callback){
|
* 校验出错区域
|
||||||
data = data || {};
|
* @param {string} source - 原始模板
|
||||||
|
* @param {Object} data - 数据
|
||||||
|
* @returns {string} 出错区域的模板碎片
|
||||||
|
*/
|
||||||
|
Class.prototype.checkErrorArea = function(source, data) {
|
||||||
var that = this;
|
var that = this;
|
||||||
var result = that.cache ? that.cache(data, tool) : that.parse(that.template, data);
|
var srcs = source.split(/\n/g);
|
||||||
|
var validLine = -1; // 有效行
|
||||||
|
|
||||||
// 返回渲染结果
|
// 逐行查找
|
||||||
typeof callback === 'function' && callback(result);
|
var i = 0;
|
||||||
return result;
|
var str = '';
|
||||||
};
|
var len = srcs.length;
|
||||||
|
for (; i < len; i++) {
|
||||||
// 创建实例
|
str += srcs[i];
|
||||||
var laytpl = function(template, options){
|
try {
|
||||||
return new Class(template, options);
|
data
|
||||||
};
|
? that.createCompiler(str)(data)
|
||||||
|
: new Function('"use strict";_laytpl__="'+ that.parse(str) +'";');
|
||||||
// 配置全局属性
|
validLine = i;
|
||||||
laytpl.config = function(options){
|
} catch(e) {
|
||||||
options = options || {};
|
continue;
|
||||||
for(var i in options){
|
}
|
||||||
config[i] = options[i];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 呈现模板出错大致区域
|
||||||
|
var errorArea = function(errLine) {
|
||||||
|
var arr = [];
|
||||||
|
var addLine = 3; // 错误行上下延伸的行数
|
||||||
|
var i = 0;
|
||||||
|
var len = srcs.length;
|
||||||
|
|
||||||
|
if (errLine < 0) errLine = 0;
|
||||||
|
if (errLine > len - 1) errLine = len - 1;
|
||||||
|
|
||||||
|
i = errLine - addLine;
|
||||||
|
if (i < 0) i = 0;
|
||||||
|
|
||||||
|
for (; i < len; i++) {
|
||||||
|
arr.push((i == errLine ? '? ' : ' ') +(i + 1)+ '| '+ srcs[i]);
|
||||||
|
if (i >= errLine + addLine) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '\n'+ arr.join('\n');
|
||||||
|
};
|
||||||
|
|
||||||
|
return errorArea(validLine + 1); // 有效行的下一行即为出错行
|
||||||
};
|
};
|
||||||
|
|
||||||
laytpl.v = '2.0.0';
|
/**
|
||||||
|
* 创建实例
|
||||||
|
* @param {string} template - 模板
|
||||||
|
* @param {Object} options - 选项
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
var laytpl = function(template, options) {
|
||||||
|
var inst = new Class(template, options);
|
||||||
|
return thisModule.call(inst);
|
||||||
|
};
|
||||||
|
|
||||||
// export
|
/**
|
||||||
exports('laytpl', laytpl);
|
* 扩展模板内部变量
|
||||||
});
|
* @param {Object} settings - 扩展内部变量,变量值通常为函数
|
||||||
|
*/
|
||||||
|
laytpl.extendVars = function(settings) {
|
||||||
|
Object.assign(vars, settings);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置默认配置
|
||||||
|
* @param {Object} options - 选项
|
||||||
|
*/
|
||||||
|
laytpl.config = laytpl.set = function(options) {
|
||||||
|
Object.assign(config, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 输出接口
|
||||||
|
typeof module === 'object' && typeof exports === 'object'
|
||||||
|
? module.exports = laytpl // CommonJS
|
||||||
|
: ( // 浏览器
|
||||||
|
typeof layui === 'object' ? layui.define(function(exports) { // Layui
|
||||||
|
exports(MOD_NAME, laytpl);
|
||||||
|
}) : (
|
||||||
|
typeof define === 'function' && define.amd ? define(function() { // RequireJS
|
||||||
|
return laytpl;
|
||||||
|
}) : global.laytpl = laytpl // 单独引入
|
||||||
|
)
|
||||||
|
);
|
||||||
|
})(this);
|
||||||
Reference in New Issue
Block a user