mirror of https://github.com/layui/layui
feat: 重构 laytpl,增强对更多复杂模板结构的解析能力 (#2577)
* feat: 重构 laytpl,增强对更多复杂模板结构的解析能力 * Squashed commit of the following: commitpull/2560/head6884f80378
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Wed Mar 19 14:45:58 2025 +0800 release: v2.10.1 commit8d643ad6dc
Merge:5521e48c
213fe5a2
Author: corededitor <107152508+corededitor@users.noreply.github.com> Date: Wed Mar 19 14:24:50 2025 +0800 feat: Merge pull request #2566 from layui/feat/component feat: 优化 component, tabs 若干功能 commit213fe5a209
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Wed Mar 19 14:15:39 2025 +0800 docs: 添加 component 文档中实验性选项标记 commit5521e48c05
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Wed Mar 19 14:10:46 2025 +0800 fix: 修复 `body` 初始 `line-height` 无效的问题 (#2569) commit8c7cf0f606
Author: 青崖 <33601030+bxjt123@users.noreply.github.com> Date: Wed Mar 19 14:10:08 2025 +0800 优化 checkbox 标签风格选中且禁用时的显示 (#2563) commit23b21254d4
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Wed Mar 19 14:04:44 2025 +0800 docs: Squashed commit of the following: commit95a0503f41
Merge:e6eb86ba
87ba4c43
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Wed Mar 19 14:03:13 2025 +0800 Merge branch 'main' into 2.x commite6eb86bacb
Author: morning-star <26325820+Sight-wcg@users.noreply.github.com> Date: Wed Mar 19 14:02:05 2025 +0800 docs(slider): 修正错别字 (#2578) commit46f7a9783e
Merge:df1fc4f4
c204590a
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Fri Mar 14 19:07:50 2025 +0800 Merge branch 'main' into 2.x commitdf1fc4f419
Author: itletu <itletu@163.com> Date: Mon Mar 10 13:54:06 2025 +0800 docs: 更正 class 公共类文档错误 (#2544) | layui-border-box | 设置元素及其所有子元素均为 `box-sizing: border-box` 模型的容器 | commit87ba4c4394
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Wed Mar 19 13:41:33 2025 +0800 docs(version): 优化 2.9.x 锚点 commita0f533f0fd
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Wed Mar 19 13:37:29 2025 +0800 docs: 修复 tabs 文档示例异常问题 commit0f0584e2ed
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Wed Mar 19 13:35:00 2025 +0800 docs: 修复 tabs 文档中自定义事件示例重新点击 Preview 失效的问题 commit172957d243
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Wed Mar 19 13:29:09 2025 +0800 docs: 优化文档中的用词细节 (#2571) commit094be4ddcc
Author: letianpailove <113023596+letianpailove@users.noreply.github.com> Date: Wed Mar 19 13:28:46 2025 +0800 fix: 更正 class 公共类文档错误 (#2562) commit53ded26cb9
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Sun Mar 16 00:15:25 2025 +0800 fix: 优化 tabs 重载时未按照传入的 closable 正确渲染可关闭状态 commitbd892bf87e
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Sun Mar 16 00:15:17 2025 +0800 feat(component): 新增 cache 原型方法,用于元素缓存操作 commit6ccc5a453d
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Sun Mar 16 00:14:18 2025 +0800 fix(component): 优化元素 lay-options 属性上的配置在重载时的优先级 commit79b0a56f50
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Sun Mar 16 00:13:55 2025 +0800 fix(component): 修复 reload 时传入的选项未正确合并的问题 * refactor(laytpl): 优化代码细节 * docs: 重写 laytpl 模块文档
parent
b4fbb28a04
commit
169f6ff9b8
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -25,7 +25,7 @@ toc: true
|
|||
|
||||
`MOD_NAME.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
<h3 id="options" lay-toc="{level: 2, hot: true}">属性</h3>
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ layui.use(function(){
|
|||
- 在元素外层设置 `class="layui-carousel"` 来定义一个轮播容器
|
||||
- 在元素内层设置属性 `carousel-item` 用来定义条目容器
|
||||
|
||||
<h3 id="demo-config" lay-toc="{level: 2, hot: true}">属性配置预览</h3>
|
||||
<h3 id="demo-config" lay-toc="{level: 2, hot: true}">可选项预览</h3>
|
||||
|
||||
<pre class="layui-code" lay-options="{preview: true, codeStyle: 'height: 515px;', layout: ['preview', 'code'], tools: ['full'], done: function(obj){
|
||||
obj.render();
|
||||
|
@ -215,4 +215,4 @@ layui.use(function(){
|
|||
});
|
||||
</script>
|
||||
</textarea>
|
||||
</pre>
|
||||
</pre>
|
||||
|
|
|
@ -33,7 +33,7 @@ div[carousel-item]>*:nth-child(2n+1){background-color: #16baaa;}
|
|||
|
||||
`carousel.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该方法返回一个实例对象,包含操作当前实例的相关方法成员。
|
||||
|
||||
|
@ -46,7 +46,7 @@ console.log(inst); // 得到当前实例对象
|
|||
|
||||
`inst.reload(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
通过渲染返回的实例对象,可获得重载方法,用于实现对实例的属性重载。
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ toc: true
|
|||
|
||||
`var codeInst = layui.code(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)。
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)。
|
||||
|
||||
其中 `codeInst` <sup>2.8.17+</sup> 即实例返回的对象,包含对当前实例进行重载等方法成员,如:
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ toc: true
|
|||
|
||||
`colorpicker.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
<br>注 <sup>2.7+</sup> : 除 `elem` 属性外,其他基础属性也可以直接写在元素的 `lay-options="{}"` 属性中。
|
||||
|
||||
```
|
||||
|
|
|
@ -53,7 +53,7 @@ CONST: {
|
|||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>isRenderWithoutElem</td>
|
||||
<td>isRenderWithoutElem <br><sup>实验性</sup></td>
|
||||
<td>
|
||||
|
||||
渲染是否无需指定目标元素。即无需设置 `elem` 选项,一般用于渲染即显示的组件类型。
|
||||
|
@ -67,7 +67,7 @@ CONST: {
|
|||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>isRenderOnEvent</td>
|
||||
<td>isRenderOnEvent <br><sup>实验性</sup></td>
|
||||
<td>
|
||||
|
||||
渲染是否由事件触发。如 `dropdown` 这类通过点击触发的组件,那么应该设置为 `true`;而诸如 `tabs` 这类初始即展示的组件,则应该设置为 `false`。*推荐根据组件类型始终显式设置对应值*。
|
||||
|
@ -81,7 +81,7 @@ CONST: {
|
|||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>isDeepReload</td>
|
||||
<td>isDeepReload <br><sup>实验性</sup></td>
|
||||
<td>
|
||||
|
||||
组件重载时是否允许深度重载,即对重载时选项进行深度合并。
|
||||
|
|
|
@ -17,7 +17,7 @@ toc: true
|
|||
|
||||
`layui.component(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该方法返回一个对象,包含用于组件对外的基础接口,如:组件渲染、重载、事件操作,及构造函数等等。用法示例:
|
||||
|
||||
|
@ -45,7 +45,7 @@ layui.define('component', function(exports) {
|
|||
});
|
||||
```
|
||||
|
||||
<h3 id="options" lay-toc="{level: 2}">属性配置</h3>
|
||||
<h3 id="options" lay-toc="{level: 2}">属性选项</h3>
|
||||
|
||||
<div>
|
||||
{{- d.include("/component/detail/options.md") }}
|
||||
|
@ -78,7 +78,7 @@ layui.define('component', function(exports) {
|
|||
|
||||
| 选项 | 描述 |
|
||||
| --- | --- |
|
||||
| elem | 件渲染指定的目标元素选择器或 DOM 对象 |
|
||||
| elem | 组件渲染指定的目标元素选择器或 DOM 对象 |
|
||||
| id | 组件渲染的唯一实例 ID |
|
||||
| show | 是否初始即渲染组件。通常结合创建组件设定的 `isRenderOnEvent` 选项决定是否启用 |
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ toc: true
|
|||
|
||||
`dropdown.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
<br>注 <sup>2.8+</sup> : 除 `elem` 属性外,其他基础属性也可以直接写在元素的 `lay-options="{}"` 属性中。
|
||||
|
||||
```
|
||||
|
@ -111,7 +111,7 @@ dropdown.render({
|
|||
`dropdown.reload(id, options);`
|
||||
|
||||
- 参数 `id` : 组件渲染时定义的 `id` 属性值
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该方法用于对下拉菜单进行完整重载,所有属性均可参与到重载中。
|
||||
|
||||
|
@ -177,4 +177,4 @@ dropdown.render({
|
|||
});
|
||||
// 打开对应的组件面板
|
||||
dropdown.open('test');
|
||||
```
|
||||
```
|
||||
|
|
|
@ -24,10 +24,10 @@ toc: true
|
|||
|
||||
`util.fixbar(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
<h2 id="options" lay-toc="{level: 2, hot: true}">属性</h2>
|
||||
|
||||
<div>
|
||||
{{- d.include("/fixbar/detail/options.md") }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -33,7 +33,7 @@ toc: true
|
|||
|
||||
`flow.load(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
信息流是分页的另一种表现形式,新加载的内容不替换原有的内容,而是随着滚动条滚动而追加显示。[#详见示例](#examples)
|
||||
|
||||
|
@ -47,7 +47,7 @@ toc: true
|
|||
|
||||
`flow.lazyimg(options);`
|
||||
|
||||
- 参数 `options` : 属性配置项。可选项见下表。
|
||||
- 参数 `options` : 属性选项。可选项见下表。
|
||||
|
||||
| 属性名 | 描述 |
|
||||
| --- | --- |
|
||||
|
|
|
@ -448,7 +448,7 @@ form.on('select(test)', function(data){
|
|||
|
||||
`form.set(options);`
|
||||
|
||||
- 参数 `options` : 全局属性配置项。详见下表:
|
||||
- 参数 `options` : 全局属性选项。详见下表:
|
||||
|
||||
| 属性名 | 描述 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
|
|
|
@ -42,7 +42,7 @@ toc: true
|
|||
|
||||
`laydate.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
<br>注 <sup>2.8+</sup> : 除 `elem` 属性外,其他基础属性也可以直接写在元素的 `lay-options="{}"` 属性中。
|
||||
|
||||
```
|
||||
|
@ -77,7 +77,7 @@ layui.use(function(){
|
|||
`laydate.hint(id, opts);`
|
||||
|
||||
- 参数 `id` : 组件渲染时定义的 `id` 属性值
|
||||
- 参数 `opts` : 该方法支持的属性可选项,详见下表
|
||||
- 参数 `opts` : 该方法支持的属性选项,详见下表
|
||||
|
||||
| opts | 描述 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
|
|
|
@ -56,7 +56,7 @@ toc: true
|
|||
|
||||
`layer.open(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
打开弹层的核心方法,其他不同类型的弹出方法均为该方法的二次封装。
|
||||
|
||||
|
@ -79,7 +79,7 @@ var index = layer.open({
|
|||
`layer.alert(content, options, yes);`
|
||||
|
||||
- 参数 `content` : 弹出内容
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
- 参数 `yes` : 点击确定后的回调函数
|
||||
|
||||
该方法用于弹出 `dialog` 类型信息框(`type: 0`),参数自动向左补位。
|
||||
|
@ -106,7 +106,7 @@ layer.alert('不开启图标', function(index){
|
|||
`layer.confirm(content, options, yes, cancel);`
|
||||
|
||||
- 参数 `content` : 弹出内容
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
- 参数 `yes` : 点击确定后的回调函数
|
||||
- 参数 `cancel` : 点击第二个按钮(默认「取消」)后的回调函数
|
||||
|
||||
|
@ -133,7 +133,7 @@ layer.confirm('确定吗?', function(index){
|
|||
`layer.msg(content, options, end);`
|
||||
|
||||
- 参数 `content` : 弹出内容
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
- 参数 `end` : 提示框关闭后的回调函数
|
||||
|
||||
该方法用于弹出 `dialog` 类型提示框(`type: 0`),默认 `3` 秒后自动关闭。参数自动向左补位。
|
||||
|
@ -162,7 +162,7 @@ layer.msg('提示框', {
|
|||
`layer.load(icon, options);`
|
||||
|
||||
- 参数 `icon` : 加载图标风格,支持 `0-2` 可选值
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该方法用于弹出 `load` 类型加载层(`type: 3`)。
|
||||
|
||||
|
@ -184,7 +184,7 @@ layer.close(index);
|
|||
|
||||
- 参数 `content` : 弹出内容
|
||||
- 参数 `elem` : 吸附的目标元素选择器或对象
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该方法用于弹出 `tips` 类型贴士层(`type: 4`),默认 `3` 秒后自动关闭。
|
||||
|
||||
|
@ -206,7 +206,7 @@ layer.tips('显示在目标元素上方', '#id', {
|
|||
|
||||
`layer.prompt(options, yes);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。除了支持 [基础属性](#options) 之外,还支持下表私有属性:
|
||||
- 参数 `options` : 基础属性选项。除了支持 [基础属性](#options) 之外,还支持下表私有属性:
|
||||
|
||||
| 私有属性 | 描述 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
|
@ -243,7 +243,7 @@ layer.prompt({
|
|||
|
||||
`layer.photos(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。除了支持 [基础属性](#options) 之外,还支持下表私有属性:
|
||||
- 参数 `options` : 基础属性选项。除了支持 [基础属性](#options) 之外,还支持下表私有属性:
|
||||
|
||||
| 私有属性 | 描述 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
|
@ -312,7 +312,7 @@ layui.use(function(){
|
|||
|
||||
`layer.tab(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。除了支持 [基础属性](#options) 之外,还支持下表私有属性:
|
||||
- 参数 `options` : 基础属性选项。除了支持 [基础属性](#options) 之外,还支持下表私有属性:
|
||||
|
||||
| 私有属性 | 描述 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
|
@ -405,7 +405,7 @@ layer.closeLast(['dialog', 'page']); // 关闭最近一次打开的信息框或
|
|||
|
||||
`layer.config(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该方法用于全局设置弹层的默认基础属性。
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ toc: true
|
|||
|
||||
`laypage.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
<h2 id="options" lay-toc="{level: 2, hot: true}">属性</h2>
|
||||
|
||||
|
@ -34,4 +34,4 @@ toc: true
|
|||
|
||||
## 小贴士
|
||||
|
||||
laypage 组件只负责分页本身的逻辑,具体的数据请求及对应的视图渲染需要另外去完成。laypage 不仅能应用在一般的异步分页上,还可直接对一段已知数据进行分页展现,如 table 组件的分页就是采用的 laypage。
|
||||
laypage 组件只负责分页本身的逻辑,具体的数据请求及对应的视图渲染需要另外去完成。laypage 不仅能应用在一般的异步分页上,还可直接对一段已知数据进行分页展现,如 table 组件的分页就是采用的 laypage。
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
<table class="layui-table">
|
||||
<colgroup>
|
||||
<col width="150">
|
||||
<col>
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>标签</th>
|
||||
<th>描述</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{{!
|
||||
<tr>
|
||||
<td>{{= }}</td>
|
||||
<td>
|
||||
|
||||
转义输出。若字段存在 HTML,将进行转义。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{- }} <sup>2.8+</sup></td>
|
||||
<td>
|
||||
|
||||
原文输出。即不对 HTML 字符进行转义,但需做好 XSS 防护。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{# }} <sup>旧</sup></td>
|
||||
<td>
|
||||
|
||||
**旧版本风格**(`< 2.11`)「Scriptlet 标签」。一般用于流程控制,如:
|
||||
|
||||
```js
|
||||
{{#if (d.title) { }}
|
||||
标题:{{= d.title }}
|
||||
{{#} else { }}
|
||||
默认标题
|
||||
{{#} }}
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ }} <sup>新</sup><sup>2.11+</sup></td>
|
||||
<td>
|
||||
|
||||
**新版本风格**「Scriptlet 标签」。一般用于流程控制,如:
|
||||
|
||||
```js
|
||||
{{ if (d.title) { }}
|
||||
标题:{{= d.title }}
|
||||
{{ } else { }}
|
||||
默认标题
|
||||
{{ } }}
|
||||
```
|
||||
|
||||
需设置 `tagStyle: 'modern'` 后生效,否则模板会报错。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{# }} <sup>新</sup><sup>2.11+</sup></td>
|
||||
<td>
|
||||
|
||||
**新版本风格**「注释标签」。即仅在模板中显示,不在视图中输出。
|
||||
|
||||
需设置 `tagStyle: 'modern'` 后生效,否则会被视为旧版本的 Scriptlet 标签。
|
||||
|
||||
</td>
|
||||
</tr>!}}
|
||||
<tr>
|
||||
<td>{{!{{! !}}!}}</td>
|
||||
<td>
|
||||
|
||||
忽略标签。即该区域中的标签不会被解析,一般用于输出原始标签。如:
|
||||
|
||||
```js
|
||||
{{! {{! 这里面的 {{= escape }} 等模板标签不会被解析 !}} !}}
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
|
@ -1,59 +1,59 @@
|
|||
<pre class="layui-code" lay-options="{preview: true, layout: ['preview'], copy: false, tools: ['full'], addTools: null}">
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['preview'], codeStyle: 'max-height: 520px;', copy: false, tools: ['full'], addTools: null}">
|
||||
<textarea>
|
||||
{{!
|
||||
<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{border: 1px solid #eee;}
|
||||
.laytpl-demo:first-child{border-right: none;}
|
||||
.laytpl-demo>textarea{position: relative; display: block; width:100%; height: 320px; 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;}
|
||||
|
||||
#ID-tpl-view-body {
|
||||
max-height: 320px; overflow: auto; clear: both;
|
||||
}
|
||||
#ID-tpl-view-body > div {
|
||||
display: none;
|
||||
}
|
||||
.laytpl-demo pre {
|
||||
margin: 0; padding: 16px; background-color: #1F1F1F; color: #F8F9FA; font-family: 'Courier New',Consolas, monospace;
|
||||
}
|
||||
</style>
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-xs6 laytpl-demo">
|
||||
<div>模板</div>
|
||||
<textarea id="ID-tpl-src">
|
||||
<h3>{{= d.title }}</h3>
|
||||
<ul>
|
||||
{{# layui.each(d.list, function(index, item){ }}
|
||||
<li>
|
||||
<span>{{= item.modname }}</span>
|
||||
<span>{{= item.alias }}:</span>
|
||||
<span>{{= item.site || '' }}</span>
|
||||
</li>
|
||||
{{# }); }}
|
||||
|
||||
{{# if(d.list.length === 0){ }}
|
||||
无数据
|
||||
{{# } }}
|
||||
</ul>
|
||||
</textarea>
|
||||
<div>
|
||||
<div style="cursor: pointer;" id="ID-tpl-src-title">
|
||||
<strong>模板(旧版本)</strong>
|
||||
<i class="layui-icon layui-icon-down layui-font-12"></i>
|
||||
</div>
|
||||
</div>
|
||||
<textarea id="ID-tpl-src"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="layui-col-xs6 laytpl-demo">
|
||||
<div>数据</div>
|
||||
<div><strong>数据</strong></div>
|
||||
<textarea id="ID-tpl-data">
|
||||
{
|
||||
"title": "Layui 常用模块",
|
||||
"title": "Layui 常用组件",
|
||||
"desc": "<a style=\"color:blue;\">一段带 HTML 内容的描述</a>",
|
||||
"list": [
|
||||
{
|
||||
"modname": "弹层",
|
||||
"alias": "layer",
|
||||
"site": "layer.domain.com"
|
||||
"title": "弹层",
|
||||
"name": "layer"
|
||||
},
|
||||
{
|
||||
"modname": "表单",
|
||||
"alias": "form"
|
||||
"title": "表单",
|
||||
"name": "form"
|
||||
},
|
||||
{
|
||||
"modname": "表格",
|
||||
"alias": "table"
|
||||
"title": "表格",
|
||||
"name": "table"
|
||||
},
|
||||
{
|
||||
"modname": "日期",
|
||||
"alias": "laydate"
|
||||
"title": "日期选择器",
|
||||
"name": "laydate"
|
||||
},
|
||||
{
|
||||
"modname": "上传",
|
||||
"alias": "upload"
|
||||
"title": "标签页",
|
||||
"name": "tabs"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -61,75 +61,198 @@
|
|||
</div>
|
||||
<div class="layui-col-xs12 laytpl-demo" style="border-top: none;">
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-xs6">视图</div>
|
||||
<div class="layui-col-xs6" style="text-align: right">
|
||||
<span id="ID-tpl-viewtime"></span>
|
||||
<div class="layui-col-xs6 layui-tabs" id="ID-tpl-view-header">
|
||||
<ul class="layui-tabs-header">
|
||||
<li><strong>渲染结果</strong></li>
|
||||
<li><strong>源码</strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-padding-sm" id="ID-tpl-view" style="max-height: 300px; padding: 16px; overflow: auto;">…</div>
|
||||
<div class="layui-col-xs6" style="text-align: right">
|
||||
<span class="layui-badge" id="ID-tpl-view-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 class="layui-clear"></div>
|
||||
|
||||
<!-- import layui -->
|
||||
<!-- 新版本模板 -->
|
||||
<script type="text/html" id="ID-tpl-template-modern">
|
||||
<p>转义输出:{{= d.desc }}</p>
|
||||
<p>原文输出:{{- d.desc }}</p>
|
||||
|
||||
{{#注释标签 - 仅在模板中显示,不在视图中输出 }}
|
||||
|
||||
<p>{{! 忽略标签,可显示原始标签:
|
||||
{{ let a = 0; }} {{= escape }} {{- source }} {{#comments }} {{! ignore !}}
|
||||
!}}</p>
|
||||
|
||||
{{#标签空主体测试 }}
|
||||
{{}} {{ }} {{ }} {{= }} {{=}} {{= }}
|
||||
|
||||
<p>
|
||||
条件语句:
|
||||
{{= d.list.length ? d.title : '' }}
|
||||
{{ if(d.title){}} - #AAAA{{='A'}}{{ } }}
|
||||
</p>
|
||||
<p>循环语句:</p>
|
||||
<ul>
|
||||
{{ d.list.forEach(function(item) { }}
|
||||
<li>
|
||||
<span>{{= item.title }}</span>
|
||||
<span>{{= item.name }}</span>
|
||||
</li>
|
||||
{{ }); }}
|
||||
</ul>
|
||||
{{ if (d.list.length === 0) { }}
|
||||
无数据
|
||||
{{} }}
|
||||
</script>
|
||||
|
||||
<!-- 旧版本模板 -->
|
||||
<script type="text/html" id="ID-tpl-template-legacy">
|
||||
<p>转义输出:{{= d.desc }}</p>
|
||||
<p>原文输出:{{- d.desc }}</p>
|
||||
<p>
|
||||
条件语句:
|
||||
{{= d.list.length ? d.title : '' }}
|
||||
{{#if(d.title){}} - #AAAA{{='A'}}{{#}}}
|
||||
</p>
|
||||
<p>循环语句:</p>
|
||||
<ul>
|
||||
{{#d.list.forEach(function(item) { }}
|
||||
<li>
|
||||
<span>{{= item.title }}</span>
|
||||
<span>{{= item.name }}</span>
|
||||
</li>
|
||||
{{#}); }}
|
||||
</ul>
|
||||
{{#if (d.list.length === 0) { }}
|
||||
无数据
|
||||
{{#} }}
|
||||
</script>
|
||||
|
||||
<!-- import layui -->
|
||||
<script>
|
||||
layui.use(function(){
|
||||
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({
|
||||
// tagStyle: 'modern' // 初始化标签风格
|
||||
});
|
||||
|
||||
// 获取模板和数据
|
||||
var get = function(type){
|
||||
var getData = function(type) {
|
||||
return {
|
||||
template: $('#ID-tpl-src').val(), // 获取模板
|
||||
data: function(){ // 获取数据
|
||||
try {
|
||||
return JSON.parse($('#ID-tpl-data').val());
|
||||
} catch(e){
|
||||
} catch(e) {
|
||||
$('#ID-tpl-view').html(e);
|
||||
}
|
||||
}()
|
||||
};
|
||||
};
|
||||
|
||||
var data = get();
|
||||
|
||||
// 耗时计算
|
||||
var startTime = new Date().getTime(), timer = function(startTime, title){
|
||||
var endTime = new Date().getTime();
|
||||
$('#ID-tpl-viewtime').html((title || '模板解析耗时:')+ (endTime - startTime) + 'ms');
|
||||
|
||||
// 视图渲染
|
||||
var renderView = function(html, startTime) {
|
||||
timer(startTime);
|
||||
$('#ID-tpl-view').html(html);
|
||||
$('#ID-tpl-view-code').html(util.escape(html));
|
||||
};
|
||||
|
||||
// 渲染模板
|
||||
var thisTpl = laytpl(data.template);
|
||||
// 生成模板
|
||||
var createTemplate = function(opts) {
|
||||
opts = $.extend({
|
||||
tagStyle: 'legacy'
|
||||
}, opts);
|
||||
|
||||
// 执行渲染
|
||||
thisTpl.render(data.data, function(view){
|
||||
timer(startTime);
|
||||
$('#ID-tpl-view').html(view);
|
||||
// 初始化模板
|
||||
var elem = $('#ID-tpl-template-'+ opts.tagStyle);
|
||||
$('#ID-tpl-src').val(elem.html().replace(/^\s+/g, ''));
|
||||
|
||||
return opts;
|
||||
};
|
||||
var tplConfig = createTemplate();
|
||||
var data = getData();
|
||||
|
||||
// 耗时计算
|
||||
var timer = function(startTime, title) {
|
||||
var endTime = new Date();
|
||||
$('#ID-tpl-view-time').html((title || '本次渲染总耗时:')+ (endTime - startTime) + 'ms');
|
||||
};
|
||||
var startTime = new Date();
|
||||
|
||||
// 创建一个模板实例
|
||||
var templateInst = laytpl(data.template, {
|
||||
condense: false, // 不处理连续空白符,即保留模板原始结构
|
||||
tagStyle: tplConfig.tagStyle
|
||||
});
|
||||
|
||||
|
||||
// 初始渲染
|
||||
templateInst.render(data.data, function(html) {
|
||||
renderView(html, startTime);
|
||||
});
|
||||
|
||||
// 编辑
|
||||
$('.laytpl-demo textarea').on('input propertychange', function(){
|
||||
var data = get();
|
||||
if(!data.data) return;
|
||||
|
||||
// 计算模板渲染耗时
|
||||
var startTime = new Date().getTime();
|
||||
|
||||
// 若模板有变化,则重新解析模板;若模板没变,数据有变化,则从模板缓存中直接渲染(效率大增)
|
||||
if(this.id === 'ID-tpl-src'){
|
||||
thisTpl.parse(data.template, data.data); // 解析模板
|
||||
$('.laytpl-demo textarea').on('input', function() {
|
||||
var data = getData();
|
||||
var startTime = new Date();
|
||||
|
||||
// 若模板有变化,则重新编译模板
|
||||
if (this.id === 'ID-tpl-src') {
|
||||
templateInst.compile(data.template);
|
||||
}
|
||||
|
||||
// 执行渲染
|
||||
thisTpl.render(data.data, function(view){
|
||||
timer(startTime);
|
||||
$('#ID-tpl-view').html(view);
|
||||
|
||||
// 若模板没变,数据有变化,则从模板缓存中直接渲染数据(效率大增)
|
||||
templateInst.render(data.data, function(html) {
|
||||
renderView(html, startTime);
|
||||
});
|
||||
});
|
||||
|
||||
// 视图结果 tabs
|
||||
tabs.render({
|
||||
elem: '#ID-tpl-view-header',
|
||||
body: ['#ID-tpl-view-body', '>div']
|
||||
});
|
||||
|
||||
// 切换模板
|
||||
dropdown.render({
|
||||
elem: '#ID-tpl-src-title',
|
||||
data: [{
|
||||
title: '新版本模板',
|
||||
tagStyle: 'modern'
|
||||
}, {
|
||||
title: '旧版本模板',
|
||||
tagStyle: 'legacy'
|
||||
}],
|
||||
click: function(obj){
|
||||
createTemplate({
|
||||
tagStyle: obj.tagStyle
|
||||
});
|
||||
this.elem.children('strong').html(obj.title);
|
||||
|
||||
// 同步设置标签风格
|
||||
templateInst.config.tagStyle = obj.tagStyle;
|
||||
|
||||
var data = getData();
|
||||
var startTime = new Date();
|
||||
|
||||
// 重新渲染
|
||||
templateInst.compile(data.template).render(data.data, function(html) {
|
||||
renderView(html, startTime);
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
!}}
|
||||
</textarea>
|
||||
</script>!}}</textarea>
|
||||
</pre>
|
||||
|
|
|
@ -7,72 +7,85 @@
|
|||
<tr>
|
||||
<th>标签</th>
|
||||
<th>描述</th>
|
||||
</tr>
|
||||
<th>类型</th>
|
||||
<th>默认值</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{{!
|
||||
<tr>
|
||||
<td>{{= }}</td>
|
||||
<td>open</td>
|
||||
<td>
|
||||
|
||||
转义输出。若字段存在 HTML,将进行转义。
|
||||
|
||||
```
|
||||
<h2>{{= d.title }}</h2>
|
||||
```
|
||||
用于设置起始界定符
|
||||
|
||||
</td>
|
||||
<td>string</td>
|
||||
<td>
|
||||
|
||||
`{{`
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{- }} <sup>2.8+</sup></td>
|
||||
<td>close</td>
|
||||
<td>
|
||||
|
||||
原始输出。若字段存在 HTML,将正常渲染。
|
||||
|
||||
```
|
||||
<div>{{- d.content }}</div>
|
||||
```
|
||||
用于设置结束界定符
|
||||
|
||||
该语句一般在需要正常渲染 HTML 时用到,但若字段存在 script 等标签,为防止 xss 问题,可采用 `{{= }}` 进行转义输出。
|
||||
</td>
|
||||
<td>string</td>
|
||||
<td>
|
||||
|
||||
> ### 注意
|
||||
> 由于 `2.6.11` 版本对 laytpl 语句进行了重要调整,原 `{{ }}` 语法即等同 `{{- }}`,升级版本时,请进行相应调整。可参考:https://gitee.com/layui/layui/issues/I5AXSP
|
||||
`}}`
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{# }}</td>
|
||||
<td>cache <sup>2.11+</sup></td>
|
||||
<td>
|
||||
|
||||
JavaScript 语句。一般用于逻辑处理。
|
||||
|
||||
```
|
||||
<div>
|
||||
{{#
|
||||
var fn = function(){
|
||||
return '2017-08-18';
|
||||
};
|
||||
}}
|
||||
{{# if(true){ }}
|
||||
开始日期:{{= fn() }}
|
||||
{{# } else { }}
|
||||
已截止
|
||||
{{# } }}
|
||||
</div>
|
||||
```
|
||||
是否开启模板缓存,以便下次渲染时不重新编译模板
|
||||
|
||||
</td>
|
||||
</tr>!}}
|
||||
<tr>
|
||||
<td>{{!{{! !}}!}}</td>
|
||||
<td>boolean</td>
|
||||
<td>
|
||||
|
||||
对一段指定的模板区域进行过滤,即不解析该区域的模板。
|
||||
|
||||
```
|
||||
{{! {{! 这里面的模板不会被解析 !}} !}}
|
||||
```
|
||||
`true`
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tr>
|
||||
<td>condense <sup>2.11+</sup></td>
|
||||
<td>
|
||||
|
||||
是否压缩模板空白符,如:将多个连续的空白符压缩为单个空格
|
||||
|
||||
</td>
|
||||
<td>boolean</td>
|
||||
<td>
|
||||
|
||||
`true`
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>tagStyle<br><sup>2.11+</sup></td>
|
||||
<td>
|
||||
|
||||
设置标签风格。可选值:
|
||||
|
||||
- `legacy`: 采用 `< 2.11` 旧版本的标签风格
|
||||
- `modern`: 采用 `2.11+` 新版本的标签风格
|
||||
|
||||
为了保持向下兼容,默认仍然采用旧版本的标签风格,但在后续版本可能会将默认值设置为 `modern`,因此,**实际使用时,建议显式设置该选项值,以免升级时产生不兼容的问题**。
|
||||
|
||||
</td>
|
||||
<td>string</td>
|
||||
<td>
|
||||
|
||||
`legacy`
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>!}}
|
||||
</table>
|
||||
|
|
|
@ -5,11 +5,11 @@ toc: true
|
|||
|
||||
# 模板引擎
|
||||
|
||||
> `laytpl` 是 Layui 的一款轻量 JavaScript 模板引擎,在字符解析上有着比较出色的表现。
|
||||
> `laytpl` 是 Layui 内置的 JavaScript 模板引擎,采用原生控制流,在模板解析上有着比较出色的表现。
|
||||
|
||||
<h2 id="test" lay-toc="{hot: true}" style="margin-bottom: 0;">在线测试</h2>
|
||||
|
||||
在以下*模板*或*数据*中进行编辑,下方*视图*将呈现对应结果。
|
||||
对文本框中的*模板*或*数据*进行编辑,下方将呈现对应的*渲染结果*。注:自 <sup>2.11+</sup> 版本开始,你可以设置 `tagStyle: 'modern'` 让模板采用新的标签风格。为了保持向下兼容,默认仍然采用旧版本的标签风格。
|
||||
|
||||
<div>
|
||||
{{- d.include("/laytpl/detail/demo.md") }}
|
||||
|
@ -20,135 +20,256 @@ toc: true
|
|||
| API | 描述 |
|
||||
| --- | --- |
|
||||
| var laytpl = layui.laytpl | 获得 `laytpl` 模块。 |
|
||||
| [laytpl(str, options).render(data, callback)](#render) | laytpl 组件渲染,核心方法。 |
|
||||
| [laytpl.config(options)](#config) | 配置 laytpl 全局属性 |
|
||||
| [var templateInst = laytpl(template, options)](#laytpl) | 创建模板实例。 |
|
||||
| [laytpl.config(options)](#config) | 设置基础选项默认值 |
|
||||
| [laytpl.extendVars(variables)](#variables) <sup>2.11+</sup> | 扩展模板内部变量 |
|
||||
|
||||
<h2 id="render" lay-toc="{level: 2, hot: true, title: '解析和渲染'}">模板解析和渲染</h2>
|
||||
<h3 id="laytpl" lay-toc="{level: 2, hot: true}">创建模板实例</h3>
|
||||
|
||||
`laytpl(str, options).render(data, callback);`
|
||||
`var templateInst = laytpl(template, options)`
|
||||
|
||||
- 参数 `str` : 模板原始字符
|
||||
- 参数 `options` <sup>2.8+</sup> : 当前模板实例的属性配置项。可选项详见:[#属性配置](#config)
|
||||
- 参数 `data` : 模板数据
|
||||
- 参数 `callback` : 模板渲染完毕的回调函数,并返回渲染后的字符
|
||||
- 参数 `template` : 原始模板字符
|
||||
- 参数 `options` <sup>2.8+</sup> : 当前模板实例的选项。详见下述:[#基础选项](#options)
|
||||
|
||||
该方法返回一个模板编译器实例,用于对模板进行数据渲染等操作,实例对象的成员有:
|
||||
|
||||
| 实例成员 | 描述 |
|
||||
| --- | --- |
|
||||
| templateInst.render(data, callback) | 给模板实例进行数据渲染,返回渲染后的 HTML 字符 |
|
||||
| templateInst.compile(template) <sup>2.11+</sup> | 编译新的模板,会强制清除旧模板缓存 |
|
||||
| templateInst.config <sup>2.11+</sup> | 获取当前模板实例的配置选项 |
|
||||
|
||||
通过将模板编译与渲染两个环节分开,我们可以在模板仅编译一次的情况下,对其渲染不同的数据,如:
|
||||
|
||||
{{!
|
||||
```js
|
||||
var laytpl = layui.laytpl;
|
||||
|
||||
```
|
||||
layui.use('laytpl', function(){
|
||||
var laytpl = layui.laytpl;
|
||||
// 创建模板实例
|
||||
var templateInst = laytpl('{{= d.name }}是一名{{= d.role }}');
|
||||
|
||||
// 直接解析字符
|
||||
laytpl('{{= d.name }}是一名前端工程师').render({
|
||||
name: '张三'
|
||||
}, function(str){
|
||||
console.log(str); // 张三是一名前端工程师
|
||||
});
|
||||
// 数据渲染 1
|
||||
templateInst.render({
|
||||
name: '张三',
|
||||
role: '全栈开发者'
|
||||
}, function(html) {
|
||||
console.log(html); // 张三是一名全栈开发者
|
||||
});
|
||||
|
||||
// 同步写法
|
||||
var str = laytpl('{{= d.name }}是一名前端工程师').render({
|
||||
name: '张三'
|
||||
});
|
||||
console.log(str); // 张三是一名前端工程师
|
||||
// 数据渲染 2
|
||||
var html = templateInst.render({
|
||||
name: '王五',
|
||||
role: '架构师'
|
||||
});
|
||||
```
|
||||
!}}
|
||||
|
||||
若模板字符较大,可存放在页面某个标签中,如:
|
||||
若每次需要对不同的模板进行编译和数据渲染,你也可以使用链式写法,如:
|
||||
|
||||
{{!
|
||||
```js
|
||||
laytpl('{{= d.name }}是一名{{= d.role }}').render({
|
||||
name: '张三',
|
||||
role: '全栈开发者'
|
||||
}, function(html) {
|
||||
console.log(html); // 张三是一名全栈开发者
|
||||
});
|
||||
```
|
||||
<script id="TPL" type="text/html">
|
||||
!}}
|
||||
|
||||
若模板字符较大,你可以将模板存放在页面某个标签中,如:
|
||||
|
||||
{{!
|
||||
```js
|
||||
<script id="ID-demo-tpl" type="text/html">
|
||||
<h3>{{= d.name }}</h3>
|
||||
<p>性别:{{= d.sex ? '男' : '女' }}</p>
|
||||
<p>角色:{{= d.role }}</p>
|
||||
</script>
|
||||
|
||||
<div id="view"></div>
|
||||
<div id="ID-demo-view"></div>
|
||||
|
||||
<!-- import layui -->
|
||||
<script>
|
||||
layui.use(function(){
|
||||
var laytpl = layui.laytpl;
|
||||
|
||||
// 渲染
|
||||
var data = {
|
||||
name: '张三',
|
||||
sex: 1
|
||||
var template = document.getElementById('ID-demo-tpl').innerHTML; // 获取模板字符
|
||||
var target = document.getElementById('ID-demo-view'); // 输出结果的目标元素
|
||||
var data = { // 数据
|
||||
"name": "张三",
|
||||
"role": "全栈开发者"
|
||||
};
|
||||
var getTpl = document.getElementById('TPL').innerHTML; // 获取模板字符
|
||||
var elemView = document.getElementById('view'); // 视图对象
|
||||
// 渲染并输出结果
|
||||
laytpl(getTpl).render(data, function(str){
|
||||
elemView.innerHTML = str;
|
||||
laytpl(template).render(data, function(html) {
|
||||
target.innerHTML = html;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
!}}
|
||||
|
||||
在实际使用时,若模板通用,而数据不同,为减少模板解析的开销,可将语句分开书写,如。
|
||||
实际使用时,若模板通用,而数据不同,为了避免对模板进行不必要的重复编译,推荐将创建模板实例与数据渲染分开书写。
|
||||
|
||||
```
|
||||
var compile = laytpl(str); // 模板解析
|
||||
compile.render(data, callback); // 模板渲染
|
||||
```
|
||||
<h3 id="options" lay-toc="{level: 2, hot: true}">基础选项</h3>
|
||||
|
||||
<h2 id="grammar" lay-toc="{level: 2, hot: true}">标签语法</h2>
|
||||
创建模板实例时,你还可以对其设置一些选项,如:
|
||||
|
||||
{{!
|
||||
```js
|
||||
// 创建模板实例
|
||||
var templateInst = laytpl(`
|
||||
{{ let role = d.role || '全栈开发者'; }}
|
||||
{{= d.name }}是一名{{= role }}
|
||||
`, {
|
||||
tagStyle: 'modern' // 采用新版本的标签风格
|
||||
});
|
||||
var html = templateInst.render({ name: '张三' });
|
||||
```
|
||||
!}}
|
||||
|
||||
支持设置的完整选项如下:
|
||||
|
||||
<div>
|
||||
{{- d.include("/laytpl/detail/options.md") }}
|
||||
</div>
|
||||
|
||||
> ### 注意
|
||||
> 开发者在使用模板语法时,需确保模板中的 JS 语句不来自于页面用户输入,即必须在页面开发者自身的可控范围内,否则请避免使用该模板引擎。
|
||||
<h3 id="delimiter" lay-toc="{level: 2, hot: true}">标签规则</h3>
|
||||
|
||||
<h2 id="config" lay-toc="{level: 2}">属性配置</h2>
|
||||
<div>
|
||||
{{- d.include("/laytpl/detail/delimiter.md") }}
|
||||
</div>
|
||||
|
||||
#### ⚡ 请注意:
|
||||
> *开发者在使用模板标签时,需确保模板中待输出的内容在开发者自身的可控范围内,尤其对于用户输入的字符要做好 XSS 防护,否则请避免使用该模板引擎,以免产生 XSS 安全隐患*。
|
||||
|
||||
<h3 id="include" lay-toc="{level: 2, hot: true}">导入子模板 <sup>2.11+</sup></h3>
|
||||
|
||||
{{!
|
||||
laytpl 支持在模板中通过添加 `{{- include(id, data) }}` 语句引入子模板。`include` 语句参数解释:
|
||||
|
||||
- `id` : 子模板 ID
|
||||
- `data` : 向子模版传入的数据
|
||||
|
||||
为了引入的子模板不被转义,因此这里应该使用 `{{- }}`,即对子模板进行原文输出。示例:
|
||||
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['code', 'preview'], codeStyle: 'max-height: 520px;', tools: ['full']}">
|
||||
<textarea>
|
||||
<script id="ID-demo-tpl-header" type="text/html">
|
||||
<div>头部公共模板</div>
|
||||
</script>
|
||||
<script id="ID-demo-tpl-list" type="text/html">
|
||||
<ul>
|
||||
{{ d.items.forEach(function(item, index) { }}
|
||||
<li>
|
||||
<span>{{= item.title }}</span>
|
||||
{{ if(item.children) { }}
|
||||
{{- include('ID-demo-tpl-list', { items: item.children }) }}
|
||||
{{ } }}
|
||||
</li>
|
||||
{{ }); }}
|
||||
</ul>
|
||||
</script>
|
||||
<script id="ID-demo-tpl-main" type="text/html">
|
||||
{{- include('ID-demo-tpl-header') }}
|
||||
<h3>循环输出:</h3>
|
||||
{{- include('ID-demo-tpl-list', { items: d.items }) }}
|
||||
</script>
|
||||
<div id="ID-demo-view"></div>
|
||||
|
||||
<!-- import layui -->
|
||||
<script>
|
||||
layui.use(function() {
|
||||
var laytpl = layui.laytpl;
|
||||
|
||||
var template = document.getElementById('ID-demo-tpl-main').innerHTML; // 获取模板字符
|
||||
var target = document.getElementById('ID-demo-view'); // 输出结果的目标元素
|
||||
var data = {
|
||||
items: [{"title": "list 1", "children": [{"title": "list 1-1", "children": [{"title": "list 1-1-1"}]}, {"title": "list 1-2"}]},{"title": "list 2", "children": [{"title": "list 2-1"}]},{"title": "list 3"}]
|
||||
};
|
||||
|
||||
// 创建模板实例
|
||||
var templateInst = laytpl(template, {
|
||||
tagStyle: 'modern' // 采用新版本的标签风格
|
||||
});
|
||||
|
||||
// 渲染并输出结果
|
||||
templateInst.render(data, function(html) {
|
||||
target.innerHTML = html;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</textarea>
|
||||
</pre>
|
||||
|
||||
!}}
|
||||
|
||||
若在 Node.js 环境,可通过 `laytpl.extendVars()` 方法重置 `include` 语句实现模板文件的导入。
|
||||
|
||||
<h3 id="config" lay-toc="{level: 2}">设置选项默认值</h3>
|
||||
|
||||
`laytpl.config(options);`
|
||||
|
||||
- 参数 `options` : 属性配置项。可选项详见下表
|
||||
- 参数 `options`: 基础选项
|
||||
|
||||
| 属性 | 描述 |
|
||||
| --- | --- |
|
||||
| open | 标签符前缀 |
|
||||
| close | 标签符后缀 |
|
||||
你可以设置任意选项的默认值,如:
|
||||
|
||||
### 全局配置
|
||||
|
||||
若模板默认的标签符与其他模板存在冲突,可通过该方法重新设置标签符,如:
|
||||
|
||||
```
|
||||
{{!
|
||||
```js
|
||||
laytpl.config({
|
||||
open: '<%',
|
||||
close: '%>'
|
||||
open: '<%', // 自定义起始界定符
|
||||
close: '%>', // 自定义起始界定符
|
||||
tagStyle: 'modern' // 采用新版本的标签风格
|
||||
});
|
||||
|
||||
// 模板语法将默认采用上述定义的标签符书写
|
||||
laytpl(`
|
||||
<%# var job = ["前端工程师"]; %>
|
||||
<%= d.name %>是一名<%= job[d.type] %>。
|
||||
`).render({
|
||||
// 创建模板实例
|
||||
var templateInst = laytpl(`
|
||||
<% var roles = ["前端工程师","全栈工程师","架构师"]; %>
|
||||
<%= d.name %>是一名<%= roles[d.role] %>
|
||||
`);
|
||||
// 渲染
|
||||
templateInst.render({
|
||||
name: '张三',
|
||||
type: 0
|
||||
role: 1
|
||||
}, function(string){
|
||||
console.log(string); // 张三是一名前端工程师。
|
||||
console.log(string); // 张三是一名全栈工程师
|
||||
});
|
||||
```
|
||||
!}}
|
||||
|
||||
### 局部配置 <sup>2.8+</sup>
|
||||
<h3 id="variables" lay-toc="{level: 2}">扩展模板内变量</h3>
|
||||
|
||||
若不想受到上述全局配置的影响,可在 `laytpl(str, options)` 方法的第二个参数中设置当前模板的局部属性,如:
|
||||
`laytpl.extendVars(variables)`
|
||||
|
||||
```
|
||||
laytpl('<%= d.name %>是一名前端工程师', {
|
||||
open: '<%',
|
||||
close: '%>'
|
||||
}).render({name: '张三'}, function(string){
|
||||
console.log(string); // 张三是一名前端工程师。
|
||||
- 参数 `variables` : 扩展的变量列表,变量值通常是一个函数
|
||||
|
||||
事实上 laytpl 内置了一些模板内部方法,如 `_escape, include`。你可以对它们进行重构,或扩展更多内部变量,如:
|
||||
|
||||
{{!
|
||||
```js
|
||||
// 扩展模板内部变量
|
||||
laytpl.extendVars({
|
||||
// 重构 include 方法,实现引入模板文件
|
||||
include: function(filename, data) {
|
||||
// …
|
||||
},
|
||||
// 添加 toDataString 方法
|
||||
toDataString: function(date) {
|
||||
date = date || new Date();
|
||||
return new Date(date).toLocaleDateString();
|
||||
}
|
||||
});
|
||||
|
||||
// 在模板中使用扩展的变量
|
||||
var templateInst = laytpl('日期:{{= toDataString(d.time) }}');
|
||||
templateInst.render({ time: 1742745600000 }, function(html) {
|
||||
console.log(html);
|
||||
});
|
||||
```
|
||||
!}}
|
||||
|
||||
## 💖 心语
|
||||
|
||||
## 贴士
|
||||
我们在 `2.11` 版本对 laytpl 完成了重要重构,使其能够具备应对更多复杂模板结构的解析能力。同时,为了与业界常用的 JavaScript 模板引擎 ejs 对齐,我们新增了与 ejs 相同的标签规则,这意味着同一套模板可以在 laytpl 和 ejs 中任意切换。
|
||||
|
||||
> Layui table 等组件的动态模板功能,均采用 laytpl 驱动。 laytpl 亦可承载单页面应用开发中的视图模板。
|
||||
作为 Layui 为数不多的一个纯功能型的模块,laytpl 承载了一些重要组件的功能支撑,如 table, dropdown 等,使得它们也能够自定义动态模板,增强了组件的可定制化。当然,laytpl 也可以作为前端单页面应用及 Express 等 Web 框架的视图引擎。
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
title: 面板 panel,card,collapse
|
||||
toc: true
|
||||
---
|
||||
|
||||
|
||||
# 面板
|
||||
|
||||
> 面板是一个包含普通面板(panel)、卡片面板(card)、折叠面板(collapse)的集合
|
||||
|
@ -44,7 +44,7 @@ toc: true
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</textarea>
|
||||
</pre>
|
||||
|
||||
|
@ -127,7 +127,7 @@ toc: true
|
|||
|
||||
<!-- import layui -->
|
||||
</textarea>
|
||||
</pre>
|
||||
</pre>
|
||||
|
||||
<h3 id="collapse-tree" lay-toc="{level: 2}">折叠面板嵌套</h3>
|
||||
|
||||
|
@ -141,33 +141,33 @@ toc: true
|
|||
<div class="layui-colla-item">
|
||||
<div class="layui-colla-title">文学家</div>
|
||||
<div class="layui-colla-content layui-show">
|
||||
|
||||
|
||||
<div class="layui-collapse" lay-accordion>
|
||||
<div class="layui-colla-item">
|
||||
<div class="layui-colla-title">唐代</div>
|
||||
<div class="layui-colla-content layui-show">
|
||||
|
||||
|
||||
<div class="layui-collapse" lay-accordion>
|
||||
<div class="layui-colla-item">
|
||||
<div class="layui-colla-title">杜甫</div>
|
||||
<div class="layui-colla-content layui-show">
|
||||
一代诗圣
|
||||
唐代著名诗人,与李白齐名
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-colla-item">
|
||||
<div class="layui-colla-title">李白</div>
|
||||
<div class="layui-colla-content">
|
||||
<p>一代诗仙</p>
|
||||
<p>唐代著名诗人,与杜甫齐名</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-colla-item">
|
||||
<div class="layui-colla-title">王勃</div>
|
||||
<div class="layui-colla-content">
|
||||
<p>千古绝唱《滕王阁序》</p>
|
||||
<p>著有千古名篇《滕王阁序》</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-colla-item">
|
||||
|
@ -227,7 +227,7 @@ toc: true
|
|||
`element.on('collapse(filter)', callback)`
|
||||
|
||||
- 参数 `collapse(filter)` 是一个特定结构。
|
||||
- `collapse` 为折叠面板点击事件固定值;
|
||||
- `collapse` 为折叠面板点击事件固定值;
|
||||
- `filter` 为导航容器属性 `lay-filter` 对应的值。
|
||||
- 参数 `callback` 为事件执行时的回调函数,并返回一个 `object` 类型的参数。
|
||||
|
||||
|
@ -276,7 +276,7 @@ layui.use(function(){
|
|||
console.log(data.show); // 得到当前面板的展开状态,true or false
|
||||
console.log(data.title); // 得到当前点击面板的标题区域对象
|
||||
console.log(data.content); // 得到当前点击面板的内容区域对象
|
||||
|
||||
|
||||
// 显示状态,仅用于演示
|
||||
layer.msg('展开状态:'+ data.show);
|
||||
});
|
||||
|
|
|
@ -26,7 +26,7 @@ toc: true
|
|||
|
||||
`rate.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
<br>注 <sup>2.8+</sup> : 除 `elem` 属性外,其他基础属性也可以直接写在元素的 `lay-options="{}"` 属性中。
|
||||
|
||||
```
|
||||
|
@ -54,4 +54,4 @@ layui.use(function(){
|
|||
|
||||
<div>
|
||||
{{- d.include("/rate/detail/options.md") }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
滑块初始值。
|
||||
|
||||
- 默认可直接设置数值,如: `value: 50`
|
||||
- 若滑块开启 `range: true` 区间选择,则值为数组,异表示开始和结尾的区间,如: `value: [30, 60]`
|
||||
- 若滑块开启 `range: true` 区间选择,则值为数组,亦表示开始和结尾的区间,如: `value: [30, 60]`
|
||||
|
||||
</td>
|
||||
<td>number<br>array</td>
|
||||
|
|
|
@ -24,13 +24,13 @@ toc: true
|
|||
| var slider = layui.slider | 获得 `slider` 模块。 |
|
||||
| [var inst = slider.render(options)](#render) | slider 组件渲染,核心方法。 |
|
||||
| [inst.setValue(value)](#setValue) | 设置滑块值 |
|
||||
| inst.config | 获得当前实例的属性配置项 |
|
||||
| inst.config | 获得当前实例的属性选项 |
|
||||
|
||||
<h2 id="render" lay-toc="{level: 2}">渲染</h2>
|
||||
|
||||
`slider.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
<br>注 <sup>2.8+</sup> : 除 `elem` 属性外,其他基础属性也可以直接写在元素的 `lay-options="{}"` 属性中。
|
||||
|
||||
```
|
||||
|
@ -90,4 +90,4 @@ ins1.setValue(60, 1) // 设置结尾值
|
|||
|
||||
<div>
|
||||
{{- d.include("/slider/detail/options.md") }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -185,7 +185,7 @@ layui.use(function(){
|
|||
`element.tabAdd(filter, options);`
|
||||
|
||||
- 参数 `filter` : tab 容器(`class="layui-tab"`)的 `lay-filter` 属性值
|
||||
- 参数 `options` : 添加 tab 时的属性可选项,见下表:
|
||||
- 参数 `options` : 添加 tab 时的属性选项,见下表:
|
||||
|
||||
| options | 描述 | 类型 | 默认 |
|
||||
| --- | --- | --- | --- |
|
||||
|
@ -225,7 +225,7 @@ layui.use(function(){
|
|||
|
||||
`element.tab(options);`
|
||||
|
||||
- 参数 `options` : 属性可选项,见下表:
|
||||
- 参数 `options` : 属性选项,见下表:
|
||||
|
||||
| options | 描述 | 类型 |
|
||||
| --- | --- | --- |
|
||||
|
|
|
@ -155,7 +155,7 @@ table.render({
|
|||
| LAY_DISABLED | 当前行是否禁止选择 | 可读可写 |
|
||||
| LAY_INDEX | 当前行下标。每页重新从零开始计算 | 只读 |
|
||||
| LAY_NUM | 当前行序号 | 只读 |
|
||||
| LAY_COL | 当前列的表头属性配置项 | 只读 |
|
||||
| LAY_COL | 当前列的表头属性选项 | 只读 |
|
||||
|
||||
示例一: 在返回的数据中设置特定字段:
|
||||
|
||||
|
|
|
@ -527,7 +527,7 @@ initSort: {
|
|||
```
|
||||
table.render({
|
||||
before: function(options){
|
||||
console.log(options); // 当前实例属性配置项
|
||||
console.log(options); // 当前实例属性选项
|
||||
options.where.abc = 123; // 修改或额外追加 where 属性
|
||||
},
|
||||
// … // 其它属性
|
||||
|
|
|
@ -277,7 +277,7 @@ layui.use(['table', 'dropdown'], function(){
|
|||
console.log(obj);
|
||||
if(event === 'email-tips'){
|
||||
layer.alert(layui.util.escape(JSON.stringify(obj.col)), {
|
||||
title: '当前列属性配置项'
|
||||
title: '当前列属性选项'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -29,7 +29,7 @@ layui.use(['table', 'dropdown', 'util'], function(){
|
|||
var dataCache = obj.dataCache; // 得到当前行缓存数据,包含特定字段 --- 2.8.8+
|
||||
var index = obj.index; // 得到当前行索引
|
||||
var tr = obj.tr; // 得到当前行 <tr> 元素的 jQuery 对象
|
||||
var options = obj.config; // 获取当前表格基础属性配置项
|
||||
var options = obj.config; // 获取当前表格基础属性选项
|
||||
var e = obj.e; // 当前的 jQuery 事件对象 --- 2.9.14+
|
||||
console.log('rowContextmenu', obj); // 查看返回对象的所有成员
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ toc: true
|
|||
|
||||
<h3 id="set" class="ws-anchor ws-bold">全局设置</h3>
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该方法主要用于初始化设置属性默认值。实际应用时,必须先设置该方法,再执行渲染、重载等操作。
|
||||
|
||||
|
@ -74,7 +74,7 @@ table 提供了以下三种渲染模式,在实际使用时,一般按情况
|
|||
|
||||
`table.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该方法返回当前实例对象,包含可操作当前表格的一些成员方法。
|
||||
|
||||
|
@ -119,7 +119,7 @@ layui.use(function(){
|
|||
</table>
|
||||
```
|
||||
|
||||
> 2.8 之前版本通过 `lay-data="{}"` 定义属性配置项;<br>
|
||||
> 2.8 之前版本通过 `lay-data="{}"` 定义属性选项;<br>
|
||||
> 2.8+ 版本推荐采用 `lay-options`,但同时兼容 `lay-data`。
|
||||
|
||||
|
||||
|
@ -128,7 +128,7 @@ layui.use(function(){
|
|||
`table.init(filter, options);`
|
||||
|
||||
- 参数 `filter` : `<table>` 元素对应的 `lay-filter` 属性值
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该方法用于将已输出在页面中的静态表格内容转换为动态 table 组件。[#参考相关示例](#demo-init)
|
||||
|
||||
|
@ -220,7 +220,7 @@ table 的属性众多,我们大致分为以下几种:
|
|||
`table.reload(id, options, deep);`
|
||||
|
||||
- 参数 `id` : table 渲染时的 `id` 属性值
|
||||
- 参数 `options` : 为基础属性配置项
|
||||
- 参数 `options` : 为基础属性选项
|
||||
- 参数 `deep` <sup>2.6+</sup> : 是否采用深度重载(即重载时始终携带初始时及上一次重载时的参数),默认 false。<div style="margin-top:5px;"><button type="button" class="layui-btn layui-btn-sm layui-btn-primary" lay-layer="{content: '#DOCS-table-reload-comp'}">2.6 之前版本的 <code>table.reload()</code> 方法兼容性说明</button></div>
|
||||
|
||||
<div style="display: none;" id="DOCS-table-reload-comp">
|
||||
|
@ -482,7 +482,7 @@ table.resize('test');
|
|||
`table.exportFile(id, data, opts);`
|
||||
- 参数 `id` : table 渲染时的 `id` **或** 要导出的数据表头(当 `id` 为 `array` 类型时)。
|
||||
- 参数 `data` : 要导出的自定义数据,参数可选。
|
||||
- 参数 `opts` <sup>2.7+</sup>: 导出数据时的属性可选项,支持: `type,title`。
|
||||
- 参数 `opts` <sup>2.7+</sup>: 导出数据时的属性选项,支持: `type,title`。
|
||||
|
||||
该方法用于外部导出对应 table 的数据和任意自定义数据。
|
||||
|
||||
|
@ -513,7 +513,7 @@ table.exportFile(['名字','性别','年龄'], [
|
|||
`table.getOptions(id);`
|
||||
- 参数 `id` : table 渲染时的 `id` 属性值
|
||||
|
||||
该方法用于外部获取对应 table 实例的属性配置项。
|
||||
该方法用于外部获取对应 table 实例的属性选项。
|
||||
|
||||
```
|
||||
// 渲染
|
||||
|
@ -622,7 +622,7 @@ layui.use(function(){
|
|||
|
||||
// 头部工具栏事件
|
||||
table.on('toolbar(test)', function(obj){
|
||||
var options = obj.config; // 获取当前表格属性配置项
|
||||
var options = obj.config; // 获取当前表格属性选项
|
||||
var checkStatus = table.checkStatus(options.id); // 获取选中行相关数据
|
||||
console.log(obj); // 查看对象所有成员
|
||||
|
||||
|
@ -697,8 +697,8 @@ table.render({
|
|||
|
||||
// 表头自定义元素工具事件
|
||||
table.on('colTool(test)', function(obj){
|
||||
var col = obj.col; // 获取当前列属性配置项
|
||||
var options = obj.config; // 获取当前表格基础属性配置项
|
||||
var col = obj.col; // 获取当前列属性选项
|
||||
var options = obj.config; // 获取当前表格基础属性选项
|
||||
var layEvent = obj.event; // 获得自定义元素对应的 lay-event 属性值
|
||||
console.log(obj); // 查看对象所有成员
|
||||
});
|
||||
|
@ -721,8 +721,8 @@ table.render({
|
|||
|
||||
// 列拖拽宽度后的事件
|
||||
table.on('colResized(test)', function(obj){
|
||||
var col = obj.col; // 获取当前列属性配置项
|
||||
var options = obj.config; // 获取当前表格基础属性配置项
|
||||
var col = obj.col; // 获取当前列属性选项
|
||||
var options = obj.config; // 获取当前表格基础属性选项
|
||||
console.log(obj); // 查看对象所有成员
|
||||
});
|
||||
```
|
||||
|
@ -744,8 +744,8 @@ table.render({
|
|||
|
||||
// 列筛选(显示或隐藏)后的事件
|
||||
table.on('colToggled(test)', function(obj){
|
||||
var col = obj.col; // 获取当前列属性配置项
|
||||
var options = obj.config; // 获取当前表格基础属性配置项
|
||||
var col = obj.col; // 获取当前列属性选项
|
||||
var options = obj.config; // 获取当前表格基础属性选项
|
||||
console.log(obj); // 查看对象所有成员
|
||||
});
|
||||
```
|
||||
|
@ -774,7 +774,7 @@ table.on('row(test)', function(obj) {
|
|||
var dataCache = obj.dataCache; // 得到当前行缓存数据,包含特定字段 --- 2.8.8+
|
||||
var index = obj.index; // 得到当前行索引
|
||||
var tr = obj.tr; // 得到当前行 <tr> 元素的 jQuery 对象
|
||||
var options = obj.config; // 获取当前表格基础属性配置项
|
||||
var options = obj.config; // 获取当前表格基础属性选项
|
||||
var e = obj.e; // 当前的 jQuery 事件对象 --- 2.9.14+
|
||||
|
||||
console.log('onrow', obj); // 查看返回对象的所有成员
|
||||
|
@ -887,7 +887,7 @@ layui.use(function(){
|
|||
var index = obj.index; // 得到当前行索引
|
||||
var layEvent = obj.event; // 获得元素对应的 lay-event 属性值
|
||||
var tr = obj.tr; // 得到当前行 <tr> 元素的 jQuery 对象
|
||||
var options = obj.config; // 获取当前表格基础属性配置项
|
||||
var options = obj.config; // 获取当前表格基础属性选项
|
||||
var col = obj.getCol(); // 得到当前列的表头配置属性 -- v2.8.3 新增
|
||||
console.log(obj); // 查看对象所有成员
|
||||
|
||||
|
|
|
@ -30,7 +30,9 @@
|
|||
|
||||
<h3 id="demo-trigger" lay-toc="{level: 2}">自定义事件</h3>
|
||||
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['preview', 'code'], codeStyle: 'max-height: 520px;', tools: ['full']}">
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['preview', 'code'], codeStyle: 'max-height: 520px;', tools: ['full'], done: function(obj){
|
||||
obj.render();
|
||||
}}">
|
||||
<textarea>
|
||||
{{- d.include("/tabs/examples/trigger.md") }}
|
||||
</textarea>
|
||||
|
@ -40,7 +42,9 @@
|
|||
|
||||
切换 tabs 标签项后,地址栏同步 `hash` 值,当页面刷新时,tabs 仍保持对应的切换状态。
|
||||
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['preview', 'code'], codeStyle: 'max-height: 520px;', tools: ['full']}">
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['preview', 'code'], codeStyle: 'max-height: 520px;', tools: ['full'], done: function(obj){
|
||||
obj.render();
|
||||
}}">
|
||||
<textarea>
|
||||
{{- d.include("/tabs/examples/hash.md") }}
|
||||
</textarea>
|
||||
|
@ -48,7 +52,9 @@
|
|||
|
||||
<h3 id="demo-nest" lay-toc="{level: 2}">标签嵌套</h3>
|
||||
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['preview', 'code'], codeStyle: 'max-height: 520px;', tools: ['full']}">
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['preview', 'code'], codeStyle: 'max-height: 520px;', tools: ['full'], done: function(obj){
|
||||
obj.render();
|
||||
}}">
|
||||
<textarea>
|
||||
{{- d.include("/tabs/examples/nest.md") }}
|
||||
</textarea>
|
||||
|
|
|
@ -35,7 +35,7 @@ toc: true
|
|||
|
||||
`tabs.render(options)`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
组件支持以下三种渲染方式:
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ toc: true
|
|||
|
||||
`transfer.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
<h3 id="options" lay-toc="{level: 2, hot: true}">属性</h3>
|
||||
|
||||
|
@ -46,7 +46,7 @@ toc: true
|
|||
`transfer.reload(id, options);`
|
||||
|
||||
- 参数 `id` : 对应渲染时定义的 `id` 属性值
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
```
|
||||
var transfer = layui.transfer;
|
||||
|
@ -89,7 +89,7 @@ var getData = transfer.getData('test');
|
|||
|
||||
`transfer.set(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该方法对所有的 `transfer` 实例有效,设置的属性优先级低于 `transfer.render(options)`
|
||||
|
||||
|
@ -99,4 +99,4 @@ transfer.set({
|
|||
height: 'auto', // 所有穿梭框默认高度为自动
|
||||
// …
|
||||
});
|
||||
```
|
||||
```
|
||||
|
|
|
@ -27,7 +27,7 @@ toc: true
|
|||
|
||||
`tree.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
<h3 id="options" lay-toc="{level: 2, hot: true}">属性</h3>
|
||||
|
||||
|
@ -109,7 +109,7 @@ tree.setChecked('test', [1, 3]); // 批量勾选 id 为 1,3 的节点
|
|||
`tree.reload(id, idArr);`
|
||||
|
||||
- 参数 `id` : 对应 tree 渲染时定义的 id 属性值
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
```
|
||||
var tree = layui.tree;
|
||||
|
@ -125,4 +125,4 @@ tree.render({
|
|||
tree.reload('test', { // options
|
||||
data: []
|
||||
});
|
||||
```
|
||||
```
|
||||
|
|
|
@ -124,7 +124,7 @@ treeTable.render({
|
|||
|
||||
| 属性 | 描述 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| enable | 是否开启异步加载模式。只有开启时 `async` 的其他属性配置才有效。 **注意:** 异步加载子节点不应跟 `simpleData` 同时开启,可以是 `url+simpleData` 的方式,获取完整的简单数据进行转换。若开启异步加载模式,即表示按需异步加载子节点。 | boolean | `false` |
|
||||
| enable | 是否开启异步加载模式。只有开启时 `async` 的其他属性选项才有效。 **注意:** 异步加载子节点不应跟 `simpleData` 同时开启,可以是 `url+simpleData` 的方式,获取完整的简单数据进行转换。若开启异步加载模式,即表示按需异步加载子节点。 | boolean | `false` |
|
||||
| url | 异步加载的接口,可以根据需要设置与顶层接口不同的接口,若相同可不设置该属性 | string | - |
|
||||
| [format](#options.tree.async.format) | 用于处理异步子节点数据的回调函数,该属性优先级高于 `async.url` 属性。用法详见下文。 | function | - |
|
||||
| type | 请求的接口类型,设置可缺省同上 | string | - |
|
||||
|
@ -146,7 +146,7 @@ treeTable.render({
|
|||
enable: true,
|
||||
async: {
|
||||
format: function(trData, options, callback){
|
||||
// trData 为行数据、options 为 treeTable 属性配置项
|
||||
// trData 为行数据、options 为 treeTable 属性选项
|
||||
// callbacck 为子节点的渲染函数
|
||||
// 可利用该函数对子节点数据进行异步请求或其他格式化处理
|
||||
var nodeList = [
|
||||
|
|
|
@ -47,7 +47,7 @@ toc: true
|
|||
|
||||
`treeTable.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该组件渲染的使用方式与 `table` 组件完全相同。
|
||||
|
||||
|
@ -69,7 +69,7 @@ toc: true
|
|||
| 仅数据重载 | treeTable.reloadData(id, options) |
|
||||
|
||||
- 参数 `id` : treeTable 渲染时的 id 属性值
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
使用方式与 `table` 组件完全相同,具体用法可参考:[table 重载](../table/#reload)
|
||||
|
||||
|
@ -144,7 +144,7 @@ console.log(obj);
|
|||
|
||||
- 参数 `id` : treeTable 渲染时的 `id` 属性值
|
||||
- 参数 `filter` : 过滤函数
|
||||
- 参数 `opts` : 该方法的属性可选项,详见下表:
|
||||
- 参数 `opts` : 该方法的属性选项,详见下表:
|
||||
|
||||
| 属性名 | 描述 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
|
|
|
@ -27,13 +27,13 @@ toc: true
|
|||
| [var inst = upload.render(options)](#render) | upload 组件渲染,核心方法。 |
|
||||
| [inst.upload()](#upload) | 对当前实例提交上传 |
|
||||
| [inst.reload(options)](#reload) | 对当前实例进行重载 |
|
||||
| inst.config | 获得当前实例的属性配置项 |
|
||||
| inst.config | 获得当前实例的属性选项 |
|
||||
|
||||
<h3 id="render" lay-toc="{level: 2, hot: true}">渲染</h3>
|
||||
|
||||
`upload.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
<br>注 : 除 `elem` 属性外,其他基础属性也可以直接写在元素的 `lay-options="{}"` 属性中。
|
||||
|
||||
```
|
||||
|
@ -94,7 +94,7 @@ var inst = upload.render({
|
|||
|
||||
`inst.reload(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该方法用于对当前的上传实例进行完整重载,所有属性均可参与到重载中。
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ toc: true
|
|||
|
||||
`util.countdown(options);`
|
||||
|
||||
- 参数 `options` <sup>2.8.9+</sup>: 属性配置项。可选项详见下表:
|
||||
- 参数 `options` <sup>2.8.9+</sup>: 属性选项。可选项详见下表:
|
||||
|
||||
| 属性 | 描述 |
|
||||
| --- | --- |
|
||||
|
@ -101,7 +101,7 @@ var result = util.timeAgo(1672531200000); // 2023-01-01 00:00:00
|
|||
|
||||
- 参数 `time` : 毫秒数或日期对象
|
||||
- 参数 `format` : 日期字符格式。默认格式:`yyyy-MM-dd HH:mm:ss` 。可自定义,如: `yyyy年MM月dd日`
|
||||
- 参数 `options` <sup>2.8.13+</sup> : 该方法的属性可选项,详见下表:
|
||||
- 参数 `options` <sup>2.8.13+</sup> : 该方法的属性选项,详见下表:
|
||||
|
||||
| 属性名 | 描述 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
|
@ -179,7 +179,7 @@ var str2 = util.unescape('<div>123</div>'); // 返回: <div>123</d
|
|||
|
||||
`util.openWin(options);`
|
||||
|
||||
- 参数 `options` : 属性配置项。可选项详见下表
|
||||
- 参数 `options` : 属性选项。可选项详见下表
|
||||
|
||||
| 属性 | 描述 |
|
||||
| --- | --- |
|
||||
|
|
|
@ -5,10 +5,26 @@ toc: true
|
|||
|
||||
# 更新日志
|
||||
|
||||
> 导读:📑 [Layui 2.8+ 《升级指南》](/notes/2.8/upgrade-guide.html) · 📑 [Layui 新版文档站上线初衷](/notes/2.8/news.html)
|
||||
> 导读:📑 [Layui 2.x 系列版本主要升级变化](/notes/share/2x-major-upgrade-changes.html) · 📑 [Layui 2.8+ 《升级指南》](/notes/2.8/upgrade-guide.html) · 📑 [Layui 新版文档站上线初衷](/notes/2.8/news.html)
|
||||
|
||||
<h2 id="2.10+" lay-toc="{title: '2.10+'}"></h2>
|
||||
|
||||
<h2 id="v2.10.1" class="ws-anchor">
|
||||
v2.10.1
|
||||
<span class="layui-badge-rim">2025-03-19</span>
|
||||
</h2>
|
||||
|
||||
- #### component
|
||||
- 修复 `reload` 时传入的选项未正确合并的问题 #2566 @sentsim
|
||||
- 优化 `lay-options` 属性上的配置在重载时的优先级 #2566 @sentsim
|
||||
- #### 其他
|
||||
- 优化 tabs `reload` 未按照 `closable` 正确渲染可关闭状态的问题 #2566 @sentsim
|
||||
- 优化 form 的 `checkbox` 标签风格选中且禁用时的显示 #2563 @bxjt123
|
||||
- 修复 body 初始 `line-height` 无效的问题 #2569 @sentsim
|
||||
|
||||
### 下载: [layui-v2.10.1.zip](https://gitee.com/layui/layui/attach_files/2100525/download)
|
||||
|
||||
---
|
||||
|
||||
<h2 id="v2.10.0" class="ws-anchor">
|
||||
v2.10.0
|
||||
|
@ -49,6 +65,10 @@ toc: true
|
|||
|
||||
---
|
||||
|
||||
|
||||
|
||||
<h2 id="2.9.x" lay-toc="{title: '2.9.x'}"></h2>
|
||||
|
||||
<h2 id="v2.9.25" class="ws-anchor">
|
||||
v2.9.25
|
||||
<span class="layui-badge-rim">2025-03-13</span>
|
||||
|
@ -62,8 +82,6 @@ toc: true
|
|||
|
||||
---
|
||||
|
||||
<h2 id="2.9.x" lay-toc="{title: '2.9.x'}"></h2>
|
||||
|
||||
<h2 id="v2.9.24" class="ws-anchor">
|
||||
v2.9.24
|
||||
<span class="layui-badge-rim">2025-03-07</span>
|
||||
|
|
|
@ -1,74 +1,45 @@
|
|||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>视图模板引擎 - layui</title>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<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">
|
||||
<style>
|
||||
.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;}
|
||||
.laytpl-demo>div:first-child{height: 32px; line-height: 32px; padding: 6px 11px; border-bottom: 1px solid #EBEBEB; background-color: #F8F9FA;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div>
|
||||
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-xs6 laytpl-demo">
|
||||
<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">
|
||||
#ID-tpl-view-body {
|
||||
height: calc(100vh - 430px); overflow: auto; clear: both;
|
||||
}
|
||||
#ID-tpl-view-body > div {
|
||||
display: none;
|
||||
}
|
||||
.laytpl-demo pre {
|
||||
padding: 16px; background-color: #20222A; color: #F8F9FA; font-family: 'Courier New',Consolas, monospace;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="layui-padding-3">
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-xs3">数据</div>
|
||||
<div class="layui-col-xs9" style="text-align: right">
|
||||
<button class="layui-btn layui-btn-sm layui-btn-primary" lay-on="createData">生成数据</button>
|
||||
<div class="layui-col-xs6 laytpl-demo">
|
||||
<div>
|
||||
<a href="javascript:;" id="ID-tpl-src-title">
|
||||
<cite><strong>模板</strong></cite>
|
||||
<i class="layui-icon layui-icon-down layui-font-12"></i>
|
||||
</a>
|
||||
</div>
|
||||
<textarea id="ID-tpl-src"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<textarea id="demoData1">
|
||||
<div class="layui-col-xs6 laytpl-demo">
|
||||
<div class="layui-row">
|
||||
<div><strong>数据</strong></div>
|
||||
</div>
|
||||
<textarea id="ID-tpl-data">
|
||||
{
|
||||
"title": "标题",
|
||||
"desc": "<a href=\"\" style=\"color:blue;\">一段描述</a>",
|
||||
|
@ -91,156 +62,316 @@
|
|||
{"title": "list 3"}
|
||||
]
|
||||
}</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 class="layui-col-xs4" style="text-align: right">
|
||||
<span id="demoViewTime"></span>
|
||||
<div class="layui-col-xs12 laytpl-demo" style="border-top: none;">
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-xs4 layui-tabs" id="ID-tpl-view-header">
|
||||
<ul class="layui-tabs-header">
|
||||
<li><strong>渲染结果</strong></li>
|
||||
<li><strong>源码</strong></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-view-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 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>
|
||||
</div>
|
||||
|
||||
<script type="type/html" template id="laytplTestTpl">
|
||||
{{# for(var i = 0; i < d.items.length; i++){ }}
|
||||
第{{= d.items[i].index }}个,Name: {{- d.items[i].name }} Number: {{= d.items[i].number }}
|
||||
{{# } }}
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="ID-tpl-template-modern">
|
||||
<h2>
|
||||
{{= d.title }} - {{= d.title ? '#' : '' }}
|
||||
{{ if(true){ }}AAAA{{='A'}}{{ } }}
|
||||
</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>
|
||||
|
||||
<script src="../src/layui.js"></script>
|
||||
<script>
|
||||
layui.use(['laytpl', 'util'], function(){
|
||||
var laytpl = layui.laytpl;
|
||||
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');
|
||||
};
|
||||
<div>
|
||||
{{= d.content || '' }}
|
||||
\反斜杠 | '单引号' "双引号" ""''"" | "左双右单' | '左单右双"
|
||||
</div>
|
||||
|
||||
// 全局设置
|
||||
/*laytpl.config({
|
||||
open: '<%',
|
||||
close: '%>'
|
||||
});*/
|
||||
<p>渲染时间:{{= layui.util.toDateString(new Date()) }}</p>
|
||||
</script>
|
||||
|
||||
// 渲染模板
|
||||
var thisTpl = laytpl(data.template);
|
||||
<script type="text/html" id="ID-tpl-template-common">
|
||||
公共模板 - {{= d.title }}
|
||||
</script>
|
||||
|
||||
// 执行渲染
|
||||
thisTpl.render(data.data, function(view){
|
||||
timer(startTime);
|
||||
$('#demoView1').html(view);
|
||||
});
|
||||
|
||||
// 编辑
|
||||
$('.laytpl-demo textarea').on('input', function(){
|
||||
var data = get();
|
||||
if(!data.data) return;
|
||||
|
||||
// 计算模板渲染耗时
|
||||
var startTime = new Date().getTime();
|
||||
|
||||
// 若模板有变化,则重新解析模板;若模板没变,数据有变化,则从模板缓存中直接渲染(效率大增)
|
||||
if(this.id === 'demoTPL1'){
|
||||
thisTpl.parse(data.template, data.data); // 解析模板
|
||||
}
|
||||
|
||||
// 执行渲染
|
||||
thisTpl.render(data.data, function(view){
|
||||
timer(startTime);
|
||||
$('#demoView1').html(view);
|
||||
});
|
||||
});
|
||||
|
||||
// 事件
|
||||
util.on({
|
||||
// 性能测试
|
||||
test1: 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
|
||||
<script type="text/html" id="ID-tpl-template-list">
|
||||
{{ if(d.items && d.items.length > 0){ }}
|
||||
<ul>
|
||||
{{ layui.each(d.items, function(index, item){ }}
|
||||
<li>
|
||||
<strong>{{= item.title }}</strong>
|
||||
{{- include('ID-tpl-template-list', {items: item.child}) }}
|
||||
</li>
|
||||
{{ }); }}
|
||||
</ul>
|
||||
{{ } }}
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="ID-tpl-template-test">
|
||||
{{ for (var i = 0; i < d.items.length; i++) { }}
|
||||
第 {{= d.items[i].index }} 个,Name: {{- d.items[i].name }} Number: {{= d.items[i].number }}
|
||||
{{ } }}
|
||||
</script>
|
||||
|
||||
<!-- 旧版本模板 -->
|
||||
<script type="text/html" id="ID-tpl-template-legacy">
|
||||
<h2>
|
||||
{{= d.title }} - {{= d.title ? '#' : '' }}
|
||||
{{# if(true){ }}AAAA{{='A'}}{{# } }}
|
||||
</h2>
|
||||
|
||||
<hr>
|
||||
|
||||
<p>转义输出:{{ d.desc }}</p>
|
||||
<p>转义输出:{{= d.desc }}</p>
|
||||
<p>原文输出:{{- d.desc }}</p>
|
||||
|
||||
{{}} {{ }} {{ }} {{= }} {{=}} {{= }}
|
||||
|
||||
<div>
|
||||
<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 || '' }}
|
||||
\反斜杠 | '单引号' "双引号" ""''"" | "左双右单' | '左单右双"
|
||||
</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({
|
||||
tagStyle: '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({
|
||||
tagStyle: 'modern'
|
||||
}, opts);
|
||||
|
||||
// 初始化模板
|
||||
var elem = $('#ID-tpl-template-'+ opts.tagStyle);
|
||||
$('#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-view-time').html((title || '模板解析耗时:')+ (endTime - startTime) + 'ms');
|
||||
};
|
||||
var startTime = new Date();
|
||||
|
||||
// 创建一个模板实例
|
||||
var templateInst = laytpl(data.template, {
|
||||
condense: false, // 不处理连续空白符,即保留模板原始结构
|
||||
tagStyle: tplConfig.tagStyle
|
||||
});
|
||||
|
||||
// 初始渲染
|
||||
templateInst.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') {
|
||||
templateInst.compile(data.template);
|
||||
}
|
||||
|
||||
// 若模板没变,数据有变化,则从模板缓存中直接渲染数据(效率大增)
|
||||
templateInst.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); // 张三是一名前端工程师。
|
||||
});
|
||||
|
||||
// 视图结果 tabs
|
||||
tabs.render({
|
||||
elem: '#ID-tpl-view-header',
|
||||
body: ['#ID-tpl-view-body', '>div']
|
||||
});
|
||||
|
||||
// 切换模板
|
||||
dropdown.render({
|
||||
elem: '#ID-tpl-src-title',
|
||||
data: [{
|
||||
title: '新版本模板',
|
||||
tagStyle: 'modern'
|
||||
}, {
|
||||
title: '旧版本模板',
|
||||
tagStyle: 'legacy'
|
||||
}],
|
||||
click: function(obj){
|
||||
createTemplate({
|
||||
tagStyle: obj.tagStyle
|
||||
});
|
||||
this.elem.children('cite').html(obj.title);
|
||||
|
||||
// 同步设置标签风格
|
||||
templateInst.config.tagStyle = obj.tagStyle;
|
||||
|
||||
var data = getData();
|
||||
var startTime = new Date();
|
||||
|
||||
// 重新渲染
|
||||
templateInst.compile(data.template).render(data.data, function(html) {
|
||||
renderView(html, startTime);
|
||||
});
|
||||
}
|
||||
return items;
|
||||
}([])
|
||||
};
|
||||
|
||||
// 模板
|
||||
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>
|
||||
})
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "layui",
|
||||
"version": "2.10.0",
|
||||
"version": "2.10.1",
|
||||
"description": "Classic modular Front-End UI library",
|
||||
"keywords": [
|
||||
"layui",
|
||||
|
|
|
@ -18,7 +18,7 @@ input,button,textarea,select,optgroup,option{font-family: inherit; font-size: in
|
|||
pre{white-space: pre-wrap; white-space: -moz-pre-wrap; white-space: -pre-wrap; white-space: -o-pre-wrap; word-wrap: break-word;}
|
||||
|
||||
/** 初始化全局标签 **/
|
||||
body{line-height: 1.6; color: #333; color: rgba(0,0,0,.85); font: 14px Helvetica Neue,Helvetica,PingFang SC,Tahoma,Arial,sans-serif;}
|
||||
body{line-height: 1.6; color: rgba(0,0,0,.85); font-size: 14px; font-family: Helvetica Neue,Helvetica,PingFang SC,Tahoma,Arial,sans-serif;}
|
||||
hr{height: 0; line-height: 0; margin: 10px 0; padding: 0; border: none; border-bottom: 1px solid #eee; clear: both; overflow: hidden; background: none;}
|
||||
a{color: #333; text-decoration: none;}
|
||||
a cite{font-style: normal;}
|
||||
|
@ -930,6 +930,8 @@ hr.layui-border-black{border-width: 0 0 1px;}
|
|||
.layui-checkbox-disabled > div{color: #c2c2c2!important;}
|
||||
.layui-checkbox-disabled > i{border-color: #eee !important;}
|
||||
.layui-checkbox-disabled:hover > i{color: #fff !important;}
|
||||
.layui-form-checkbox[lay-skin="tag"].layui-form-checked.layui-checkbox-disabled > i{color:#c2c2c2;}
|
||||
.layui-form-checkbox[lay-skin="tag"].layui-form-checked.layui-checkbox-disabled:hover > i{color: #c2c2c2!important;}
|
||||
|
||||
/* 单选框 */
|
||||
.layui-form-radio{display: inline-block; vertical-align: middle; line-height: 28px; margin: 6px 10px 0 0; padding-right: 10px; cursor: pointer; font-size: 0;}
|
||||
|
@ -1317,6 +1319,7 @@ body .layui-table-tips .layui-layer-content{background: none; padding: 0; box-sh
|
|||
|
||||
.layui-tabs-header li .layui-tabs-close{position: relative; display: inline-block; width: 16px; height: 16px; line-height: 18px; margin-left: 8px; top: 0px; text-align: center; font-size: 12px; color: #959595; border-radius: 50%; font-weight: 700; transition: all .16s; -webkit-transition: all .16s;}
|
||||
.layui-tabs-header li .layui-tabs-close:hover{ background-color: #ff5722; color: #fff;}
|
||||
.layui-tabs-header li[lay-closable="false"] .layui-tabs-close{display: none;}
|
||||
|
||||
.layui-tabs-body{padding: 16px 0;}
|
||||
.layui-tabs-item{display: none;}
|
||||
|
|
|
@ -20,7 +20,6 @@ layui.define(['jquery', 'lay'], function(exports) {
|
|||
|
||||
// 组件名
|
||||
var MOD_NAME = settings.name;
|
||||
var MOD_INDEX = 'layui_'+ MOD_NAME +'_index'; // 组件索引名
|
||||
var MOD_ID = 'lay-' + MOD_NAME + '-id'; // 用于记录组件实例 id 的属性名
|
||||
|
||||
// 组件基础对外接口
|
||||
|
@ -31,7 +30,7 @@ layui.define(['jquery', 'lay'], function(exports) {
|
|||
// 通用常量集,一般存放固定字符,如类名等
|
||||
CONST: $.extend(true, {
|
||||
MOD_NAME: MOD_NAME,
|
||||
MOD_INDEX: MOD_INDEX,
|
||||
MOD_ID: MOD_ID,
|
||||
|
||||
CLASS_THIS: 'layui-this',
|
||||
CLASS_SHOW: 'layui-show',
|
||||
|
@ -103,7 +102,7 @@ layui.define(['jquery', 'lay'], function(exports) {
|
|||
// 重载实例
|
||||
Class.prototype.reload = function(options, type) {
|
||||
var that = this;
|
||||
$.extend(settings.isDeepReload, that.config, options);
|
||||
that.config = $.extend(settings.isDeepReload, {}, that.config, options);
|
||||
that.init(true, type);
|
||||
};
|
||||
|
||||
|
@ -124,7 +123,13 @@ layui.define(['jquery', 'lay'], function(exports) {
|
|||
}
|
||||
|
||||
// 合并 lay-options 属性上的配置信息
|
||||
$.extend(true, options, lay.options(elem[0]));
|
||||
var layOptions = lay.options(elem[0]);
|
||||
if (rerender) {
|
||||
// 若重载渲染,则重载传入的 options 配置优先
|
||||
options = that.config = $.extend(layOptions, options);
|
||||
} else {
|
||||
$.extend(options, layOptions); // 若首次渲染,则 lay-options 配置优先
|
||||
}
|
||||
|
||||
// 若重复执行 render,则视为 reload 处理
|
||||
if (!rerender && elem.attr(MOD_ID)) {
|
||||
|
@ -173,21 +178,39 @@ layui.define(['jquery', 'lay'], function(exports) {
|
|||
Class.prototype.render = settings.render; // 渲染
|
||||
Class.prototype.events = settings.events; // 事件
|
||||
|
||||
// 元素操作缓存
|
||||
Class.prototype.cache = function(key, value) {
|
||||
/**
|
||||
* 元素缓存操作
|
||||
* @param {string} key - 缓存键
|
||||
* @param {*} value - 缓存值
|
||||
* @param {boolean} remove - 是否删除缓存
|
||||
* @returns {*} - 若 value 未传,则返回缓存值
|
||||
*/
|
||||
Class.prototype.cache = function(key, value, remove) {
|
||||
var that = this;
|
||||
var options = that.config;
|
||||
var elem = options.elem;
|
||||
|
||||
var MOD_CACHE_NAME = MOD_ID + '-cache';
|
||||
if (!elem) return;
|
||||
|
||||
var CACHE_NAME = 'lay_'+ MOD_NAME + '_cache';
|
||||
var cache = elem.data(CACHE_NAME) || {};
|
||||
var cache = elem.data(MOD_CACHE_NAME) || {};
|
||||
|
||||
if (value === undefined) return cache[key];
|
||||
// value 未传则获取缓存值
|
||||
if (value === undefined) {
|
||||
return cache[key];
|
||||
}
|
||||
|
||||
cache[key] = value;
|
||||
elem.data(CACHE_NAME, cache);
|
||||
if (remove) {
|
||||
delete cache[key]; // 删除缓存
|
||||
} else {
|
||||
cache[key] = value; // 设置缓存
|
||||
}
|
||||
|
||||
elem.data(MOD_CACHE_NAME, cache);
|
||||
};
|
||||
|
||||
// 清除缓存
|
||||
Class.prototype.removeCache = function(key) {
|
||||
this.cache(key, null, true);
|
||||
};
|
||||
|
||||
// 缓存所有实例对象
|
||||
|
|
|
@ -1,162 +1,475 @@
|
|||
/**
|
||||
* laytpl 轻量模板引擎
|
||||
* laytpl
|
||||
* 轻量级通用模板引擎
|
||||
*/
|
||||
|
||||
layui.define(function(exports){
|
||||
"use strict";
|
||||
(function(global) {
|
||||
'use strict';
|
||||
|
||||
// 默认属性
|
||||
var config = {
|
||||
open: '{{', // 标签符前缀
|
||||
close: '}}' // 标签符后缀
|
||||
};
|
||||
var MOD_NAME = 'laytpl';
|
||||
|
||||
// 模板工具
|
||||
var tool = {
|
||||
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 thisModule = function() {
|
||||
var that = this;
|
||||
that.config = that.config || {};
|
||||
that.template = template;
|
||||
var options = that.config;
|
||||
return {
|
||||
config: options,
|
||||
|
||||
// 简单属性合并
|
||||
var extend = function(obj){
|
||||
for(var i in obj){
|
||||
that.config[i] = obj[i];
|
||||
/**
|
||||
* 渲染模板
|
||||
* @param {Object} data - 模板数据
|
||||
* @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 options = this.config;
|
||||
var types = [
|
||||
'#([\\s\\S])+?', // js 语句
|
||||
'([^{#}])*?' // 普通字段
|
||||
][type || 0];
|
||||
|
||||
return inner.exp((_||'') + options.open + types + options.close + (__||''));
|
||||
// 模板内部变量
|
||||
var vars = {
|
||||
// 字符转义
|
||||
escape: function(html) {
|
||||
var exp = /[<"'>]|&(?=#[a-zA-Z0-9]+)/g;
|
||||
if (html === undefined || html === null) return '';
|
||||
html = ''+ html;
|
||||
if (!exp.test(html)) return html;
|
||||
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, // 是否压缩模板空白符,如:将多个连续的空白符压缩为单个空格
|
||||
tagStyle: '' // 标签风格。默认采用 < 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 options = that.config;
|
||||
var source = template;
|
||||
var jss = inner.exp('^'+ options.open +'#', '');
|
||||
var jsse = inner.exp(options.close +'$', '');
|
||||
var openDelimiter = options.open;
|
||||
var closeDelimiter = options.close;
|
||||
var condense = options.condense;
|
||||
var regex = tools.regex;
|
||||
const placeholder = '\u2028'; // Unicode 行分隔符
|
||||
|
||||
// 模板必须为 string 类型
|
||||
if(typeof template !== 'string') return template;
|
||||
// console.log('compile');
|
||||
|
||||
// 正则解析
|
||||
template = template.replace(/\s+|\r|\t|\n/g, ' ')
|
||||
.replace(inner.exp(options.open +'#'), options.open +'# ')
|
||||
.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){
|
||||
// 模板必须为 string 类型,且不能为空
|
||||
if (typeof template !== 'string' || !template) {
|
||||
return function() {
|
||||
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 tagRegex = 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(tagRegex(['!', '', '!'], 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 +'));'
|
||||
) : '';
|
||||
};
|
||||
|
||||
// 解析 Scriptlet
|
||||
var statement = function(str, body) {
|
||||
if (!body) return '';
|
||||
body = clear(body);
|
||||
return strConcatenation(body);
|
||||
};
|
||||
|
||||
// 标签风格
|
||||
if (options.tagStyle === 'modern') { // 2.11+ 版本风格
|
||||
// 注释标签 - 仅在模板中显示,不进行解析,也不在视图中输出
|
||||
tpl = tpl.replace(tagRegex(['#'], delimSides), '');
|
||||
// 输出标签
|
||||
tpl = tpl.replace(tagRegex(['(=|-)']), output);
|
||||
// Scriptlet 标签
|
||||
tpl = tpl.replace(tagRegex([], delimSides), statement);
|
||||
} else { // < 2.11 版本风格
|
||||
// Scriptlet 标签
|
||||
tpl = tpl.replace(tagRegex(['#'], delimSides), statement);
|
||||
// 输出标签
|
||||
tpl = tpl.replace(tagRegex(['(=|-)*']), 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);
|
||||
|
||||
try {
|
||||
/**
|
||||
* 请注意: 开发者在使用模板语法时,需确保模板中的 JS 语句不来自于页面用户输入。
|
||||
* 即模板中的 JS 语句必须在页面开发者自身的可控范围内,否则请避免使用该模板解析。
|
||||
*/
|
||||
that.cache = template = new Function('d, laytpl', template);
|
||||
return template(data, tool);
|
||||
return new Function('laytpl', 'return '+ codeBuilder)(that.vars);
|
||||
};
|
||||
|
||||
try {
|
||||
return createCompiler(template); // 返回编译器
|
||||
} catch(e) {
|
||||
delete that.cache;
|
||||
return inner.error(e, source);
|
||||
delete that.compilerCache;
|
||||
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 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);
|
||||
return result;
|
||||
};
|
||||
|
||||
// 创建实例
|
||||
var laytpl = function(template, options){
|
||||
return new Class(template, options);
|
||||
};
|
||||
|
||||
// 配置全局属性
|
||||
laytpl.config = function(options){
|
||||
options = options || {};
|
||||
for(var i in options){
|
||||
config[i] = options[i];
|
||||
// 逐行查找
|
||||
var i = 0;
|
||||
var str = '';
|
||||
var len = srcs.length;
|
||||
for (; i < len; i++) {
|
||||
str += srcs[i];
|
||||
try {
|
||||
data
|
||||
? that.createCompiler(str)(data)
|
||||
: new Function('"use strict";_laytpl__="'+ that.parse(str) +'";');
|
||||
validLine = i;
|
||||
} catch(e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 呈现模板出错大致区域
|
||||
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} variables - 扩展内部变量,变量值通常为函数
|
||||
*/
|
||||
laytpl.extendVars = function(variables) {
|
||||
Object.assign(vars, variables);
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置默认配置
|
||||
* @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);
|
|
@ -459,7 +459,7 @@ layui.define('component', function(exports) {
|
|||
var that = this
|
||||
var options = that.config;
|
||||
|
||||
if(!options.closable) return;
|
||||
if (!options.closable) return;
|
||||
|
||||
opts = opts || {};
|
||||
|
||||
|
@ -484,17 +484,13 @@ layui.define('component', function(exports) {
|
|||
var that = this;
|
||||
var options = that.config;
|
||||
var container = that.getContainer();
|
||||
var hasDel = that.cache('close');
|
||||
|
||||
// 是否开启关闭
|
||||
if (options.closable) {
|
||||
if (!hasDel) {
|
||||
container.header.items.each(function(){
|
||||
that.appendClose($(this));
|
||||
});
|
||||
that.cache('close', true);
|
||||
}
|
||||
} else if(hasDel) {
|
||||
container.header.items.each(function() {
|
||||
that.appendClose($(this));
|
||||
});
|
||||
} else {
|
||||
container.header.items.each(function() {
|
||||
$(this).find('.'+ component.CONST.CLOSE).remove();
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue