Giter Site home page Giter Site logo

ecomfe / etpl Goto Github PK

View Code? Open in Web Editor NEW
495.0 495.0 96.0 1.06 MB

ETPL是一个强复用、灵活、高性能的JavaScript模板引擎,适用于浏览器端或Node环境中视图的生成。

Home Page: http://ecomfe.github.io/etpl/

License: BSD 3-Clause "New" or "Revised" License

JavaScript 52.52% CSS 0.66% HTML 46.79% Shell 0.03%

etpl's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

etpl's Issues

target手动闭合不管用?

<!--  这里是注释 -->
<!-- target: 1-->
xxx1
<!-- /target -->

<! -- 这里是注释 -->
<!-- target: 2 -->
xxx2

target 1把注释内容也给包含进去了,但是我不想让注释内容插入到html中。。。所以尝试用了手工闭合target,结果貌似不管用。。。

当使用filter并传递参数时输出不合预期

var etpl = require('etpl')
etpl.addFilter('trim', function (s, space) { return s.substring(space) })
var render = etpl.compile('${id | trim(${space})}')
var data = { id: 'abc', space: 1 }
render(data)
// -> "${id | trim(1)}"

已经修复了 #8 的错误保证能顺利执行

预期输出应该是abc在截取掉1位(space控制)后的字符串,即"bc",但真实输出是"${id | trim(1)}",逻辑不大能分析出来……似乎trim本身也没有执行

ETPL3 模版继承(母版)功能改进的方案投票

在ETPL3.0中计划targetmaster归一化,只保留target,删除master。也就是说,未来的模板继承(母版功能),会这么写:

<!-- target: masterTarget -->
<!-- target: myTarget[master=masterTarget] -->

在ETPL2中,target内通过content,覆盖mastercontentplaceholder的部分。在smarty和nunjucks中,模板继承的内容覆盖是通过block来实现的。block可以表达contentplaceholder,也可以被继承的block覆盖。

但是,ETPL提供了命令标签的自闭合功能,当contentplaceholder没有默认内容时,是可以不写闭合的。而content也是可以不写闭合的(content允许不写闭合还有一个弊端,content之间无法嵌套)。

所以,在ETPL3.0中,需要对使用contentplaceholdercontent,或使用block进行选择。

方案描述

方案1: 放弃contentplaceholdercontent,使用block

使用该方案,在任何情况下,block都需要手工闭合

<!-- target: masterTarget -->
<header><!-- block: header --><!-- /block --></header>
<div><!-- block: content -->default content<!-- /block --></div>

<!-- target: myTarget[master=masterTarget] -->
<!-- block: header -->my header<!-- /block -->
<!-- block: content -->my content<!-- /block -->

方案2: 沿用ETPL2中的contentplaceholdercontent

和ETPL2使用方法一致

<!-- target: masterTarget -->
<header><!-- contentplaceholder: header --></header>
<div><!-- contentplaceholder: content -->default content<!-- /contentplaceholder --></div>

<!-- target: myTarget[master=masterTarget] -->
<!-- content: header -->my header
<!-- content: content -->my content

方案决策

请投票:

  • A: 使用方案1, 放弃contentplaceholdercontent,使用block
  • B: 使用方案2,沿用ETPL2中的contentplaceholdercontent

无论使用哪种方案,ETPL3的升级都会推出模版自动升级工具。投票时无需考虑迁移成本。

能use或import一个master吗

比如

<!-- master: sidebar -->
<div class="sidebar">
    <!-- contentplaceholder: content -->
</div>

<!-- target: main -->
<header>Hello</header>
<!-- use: sidebar -->
    <!-- content: content -->
    This is Sidebar
<!-- /use -->

现在得这么写

<!-- target: sidebarHead -->
<div class="sidebar">

<!-- target: sidebarTail -->
</div>

<!-- target: main -->
<header>Hello</header>
<!-- import: sidebarHead -->
This is Sidebar
<!-- import: sidebarTail -->

弱弱的问下是否考虑支持 ${'some string'|filterName} 类似用法

初次提问,心情忐忑,不知大大是否空闲。

某年某月的某一天,为了真爱, 于是默默的写下了纠结一生的代码:

// filter
etpl.addFilter('kiss', function (source) {
    return 'duang~duang~ ' + source + ' too.';
});
// view
<p>${'i love u'|kiss}</p>
// output
<p>duang~duang~  too.</p>

心都碎了, 求 大大 指点 ~

支持直接输出${}

用etpl时,想要直接输出${xxx},而不是用data.xxx来代替似乎做不了,希望可以有类似这样的语法

$${xxx}

输出成${xxx}

for语法对于自定义集合的支持

简单来说,现在的etpl并不支持backboneCollection,也不支持emcCollection,因为它们封装了数组,并且没有暴露出类数组的索引下标访问功能

我们当然可以在一切这样的场景下先把Collection对象变成纯数组,但这会使得应用系统变得非常复杂,我们使用Collection是希望使用它的变化通知的相关事件,转为纯数组会失去这一功能,或者会变成每次收到集合变化时再转一次纯数组交给etpl

我总结了一下几个知名库的封装数组类的接口:

  • backbone.Collection:有.length属性,有eachforEach方法,使用at(index)访问元素
  • ember.Array:有.length属性,有forEach方法,使用objectAt(index)访问元素
  • ko.ObservableArray:有.length属性,能直接下标访问
  • jQuery:有.length,能直接下标访问

我们的emc.Collection.length属性,没有任何遍历的方法,使用get(index)访问元素

因此我希望可以支持这一类的封装数组,从上面来看,大家都会有.length属性,但访问方法各不相同,其中以eachforEach最为通用,emc可以添加该方法作支持

个人的建议是当具备.length属性时,增加检测forEacheach方法,存在的话也认为是一个数组并使用方法进行循环

最终这一需求会影响多少的体积,是否合适,还是交给 @errorrik 来判断吧

调查:在使用etpl编写模板时,哪些命令标签的自闭和功能被用到

etpl部分命令标签提供了标签自闭和功能,为了方便模板编写时能够减少输入。

为了评估该功能带来的收益,评估自闭和功能设计是否达到了想像中的便利,为了接下来etpl的设计,发起该调查。

该调查为多选

  • A 使用target自闭和,target不编写结束标签
  • B 使用master自闭和,master不编写结束标签
  • C 使用content自闭和,content不编写结束标签
  • D 使用contentplaceholder自闭和,contentplaceholder不编写结束标签

addFilter问题

addFilter传递过来的参数是个string,如果原始参数是个Object,那么传过来的就会是[object Object],这样根本没法做任何处理吧!?比如我想加个stringifyfilter:

etpl.addFilter('stringify', function(source) {
    console.log(source);  // "[object Object]"
});

稳跪。。。

建议为for增加break功能

应用中发现for循环写死不太方便,有时后端数据可能没有做限制,建议可以增加break功能,使使用更灵活
语法可以是这样:
{{ break: conditional-expression }}
在for循环中插入命令语句
我自测时实现了一个break command,测试用例如 {{ break: 3 }} {{ break: ${list.length} - 2 }} 等均能通过

移动端测试结果

i9100 (Android 4.1.2)

MIUI Browser

compile-time.html

93ms
83ms
157ms
110ms
84ms

render-time.html?5000*10|start

etpl            811ms
juicer          834ms
mustache        2236ms
hogan           843ms
handlebars      827ms
ejs             2762ms
dot             3515ms
artTemplate     1162ms
baiduTemplate   1984ms

Chrome

compile-time.html

140ms
96ms
78ms
81ms
75ms

render-time.html?5000*10|start

etpl            692ms
juicer          638ms
mustache        2480ms
hogan           726ms
handlebars      603ms
ejs             2446ms
dot             1327ms
artTemplate     776ms
baiduTemplate   1759ms

Changhong Baidu Cloud (Android 2.3.6)

Baidu Browser

compile-time.html

368ms
353ms
361ms
495ms
357ms

render-time.html?5000*10|start

etpl            3461ms
juicer          3752ms
mustache        7704ms
hogan           4754ms
handlebars      4728ms
ejs             11429ms
dot             9086ms
artTemplate     2ms
baiduTemplate   8997ms

Meizu MX3 (Flyme OS 3.0.2, Android 4.2.1)

Meizu Browser

compile-time.html

234ms
194ms
172ms
137ms
150ms

render-time.html?5000*10|start

etpl            623ms
juicer          567ms
mustache        1557ms
hogan           563ms
handlebars      712ms
ejs             1875ms
dot             2182ms
artTemplate     714ms
baiduTemplate   1324ms

iPhone 5 (iOS7)

Safari

compile-time.html

59ms
57ms
54ms
53ms
55ms

render-time.html?5000*10|start

etpl            489ms
juicer          361ms
mustache        606ms
hogan           416ms
handlebars      301ms
ejs             762ms
dot             283ms
artTemplate     351ms
baiduTemplate   847ms

BaiduClient (WebView)

compile-time.html

84ms
84ms
83ms
84ms
80ms

render-time.html?5000*10|start

etpl            1173ms
juicer          940ms
mustache        2040ms
hogan           2036ms
handlebars      1064ms
ejs             1159ms
dot             2323ms
artTemplate     1374ms
baiduTemplate   3143ms

iTouch 4 (iOS6)

Safari

compile-time.html

197ms
165ms
178ms
168ms
170ms

render-time.html?5000*10|start

etpl            2395ms
juicer          1782ms
mustache        3475ms
hogan           1638ms
handlebars      1615ms
ejs             2815ms
dot             2089ms
artTemplate     3330ms
baiduTemplate   3450ms

BaiduClient (WebView)

compile-time.html

261ms
260ms
268ms
272ms
265ms

render-time.html?5000*10|start

etpl            5212ms
juicer          4153ms
mustache        7485ms
hogan           7052ms
handlebars      4383ms
ejs             5497ms
dot             6865ms
artTemplate     6820ms
baiduTemplate   10792ms

注:

  1. 在所有浏览器中,测试用例都已通过
  2. compile-time.html 的数据为测试5次的结果
  3. iOS系统中Safari与使用WebView存在差异,所以分别测试

target name需要允许包含"/"字符

未来在node server环境,可以通过某种机制,target file和file path可以有一些对应映射关系,更方便server环境中模板的管理

支持语法自定义:变量open和变量close

现在的变量是${开头和}结束,不可自定义。

现在的命令open和close是可以通过engine的option:commandOpen&commandClose来自定义的。

如果变量的open和close也能自定义,使用者就能很大程度上使用自己喜欢的模板语法。

支持option:variableOpen, variableClose

etpl的amd加载无法跨域

在所有中间页中,所有的静态资源都是放在vs-static域下的,与主域不同。etpl/tpl是通过ajax来加载模板的,无法跨域。

在后端无法配合的条件下,etpl模板是没办法跨域加载到的。

使用的例子太少了!!

官方例子只有这么简单一个

var render = etpl.compile('Hello ${name}!');
var text = render({ name: 'etpl' });

那个example页面,也没有如何调用的js代码。对于一个一开始没接触过模板的人来说,完全无法下手。

比如这个循环,我改怎么写js,完全不知道

  • ${person.name}[${person.email}]

语法整理

Issue方便讨论,就在这里了:

母板

<!-- master: masterName(master=masterName) -->
    <!-- contentplaceholder: x -->
    <!-- /contentplaceholder -->
    Additional content
    <!-- contentplaceholder: y -->
    <!-- /contentplaceholder -->
<!-- /master -->

母板即建立一个模板,里面放一些“变量”,继承母板的目标或者子母板通过填充这些“变量”来实现输出。

母板可以指定一个父母板,形成多层的母板关系。但父母板中的contentplaceholder不会再传递下去,即contentplaceholder只在一层有效。

自闭合。内部可嵌套任何条件和循环语句。

内容片

<!-- contentplaceholder: name -->
default content
<!-- /contentplaceholder -->

内容片用在母板中,为一个占位空间。

内容片 必须 闭合。内部可嵌套任何条件和循环语句。

内容片的起始和结束之间放的东西为“默认内容”,即使用此母板的目标或母板未使用content片段定义这个内容片时,使用此默认内容。

目标

<!-- target: targetName(master=masterName) -->
    <!-- content: x -->
    Replaced content
    <!-- /content -->
<!-- /target -->

<!-- target: targetName -->
target content could be any HTML
<!-- /target -->

目标就是最简单的一个模板块,使用getRendererrender,提供targetName即可使用。

master参数可选。

自闭合。内部可嵌套任何条件和循环语句。

内容

<!-- content: x -->
Content
<!-- /content -->

用于覆盖母板中定义的一个内容片,内容的名称必须与母板定义的对应。如果一个内容名称在母板中未定义,则报错。

自闭合。内部可嵌套任何条件和循环语句。

混合

<!-- mixin: mixinName($arg1, $arg2...) -->
    Content
<!-- /mixin -->

混合就是个辅助函数,不能单独被getRendererrender使用,只能在其它的母板、目标中引入。

引入混合使用如下语法:

<!-- use: mixinName -->

使用useimport区分,前者负责混合的引入,后者负责目标的引入。

目标可以当混合用,但目标是不能传递参数的,而混合可以传递参数。

管道

同Linux的概念;

${x|date}

则将x这个数据,调用date这个管道处理器(在模板引擎中我们叫filter)。

管道处理器可以有参数:

${x|date('yyyy-MM-dd', true)}

则调用date管道处理器时会把参数跟上,如这么实现:

function dateFilter(value, format, useLocale) {
    if (!useLocale) {
        value = value.getUTCDate();
    }
    return formatDate(value, format)
}

关于管道处理器怎么设计我们另开Issue讨论。

给管道传参还可以用另一个动态数据:

${x|date(${dateFormat}, true)}

这个实现不知道代价有多大,但很实用。

既然叫管道,自然可以连着用:

${x|raw|customEncode|highlightKeyword(${keyword})|html}

意思就是:先原样输出(禁用默认的HTML编码),再通过customEncode进行自定义编码,再高亮关键词(使用keyword为关键词),再进行HTML编码。

当然语法上要适应空格,不然粘一起太丑:

${x | raw | customEncode | highlightKeyword(${keyword}) | html}

块处理器

相比管道对单个数据生效,块处理器弥补2个问题:

  • 管道只对蛮量生效,不对常量作用
  • 管道不能有一块东西给它用

块处理器在定义后能这么写:

<!-- filter: markdown(${useExtra}, true) -->
## markdown document

This is the content, also I can use `${variables}`
<!-- /filter -->

当然这么一写就能嵌套组合了,不再详述。

块处理器一样能传参数。任何管理处理器都能当块处理器用,纯粹是调用语法上的变化而已。

其它

  • 循环:<!-- for: ${list} as ${item}, ${index} -->
  • 遍历:<!-- for: ${object} as ${value}, ${key} -->
  • 分支: <!-- if: condition --><!-- elif: condition --><!-- else -->

总结

与原tpl相比,主要变化在:

  1. master可以有master,这在多级布局中很重要
  2. contentplaceholder有默认内容,可以不被覆盖,我实际使用中也倍感需求
  3. 增加mixin,主要为了使用的时候可以传参,非常重要
  4. 管道模型,这能让模板更具编程性

问题

  1. 要不要能定义变量?<!-- var ${i} = 1 -->,我认为有必要,在循环中格外有用,但作用域怎么控制是个问题(要不要块作用域)
  2. 特殊的管道处理器给个简写,现在能想到的就是“不HTML编码”能写成${:xxx}吧
  3. 需要有个办法在模板中加注释,此类注释render后会消失,建议<!-- // xxx -->
  4. 有一个很高级的功能叫“属性省略”,即比如<input value="${value}" />,则如果${value}为空时,输出的是<input />,这个太高级了我觉得不用做,但有这功能的话模板用着会非常非常舒服。ASP.NET的Razor是有这功能的,jade也有

支持commandSyntax参数

通过{RegExp} commandSyntax参数,可以制定command语法。

{RegExp} commandSyntax的match点:

  1. 是否闭合
  2. command name
  3. command value

比如我们现在的if是这样的

<!-- if: ${v} == 1 -->

如果commandSyntax/^\s*(\/)?([a-z]+)\s?([\s\S]*)$/,就能这样写if:

<!-- if ${v} == 1 -->

绘制“tree”

如果想绘制个“menu”(或者“tree”),比如:

<ul>
    <li><a href="#">Aberdeen</a></li>
    <li><a href="#">Ada</a></li>
    <li>
        <a href="#">Salzburg</a>
        <ul>
            <li><a href="#">Perch</a></li>
            <li><a href="#">D</a></li>
            <li>
                <a href="#">Delphi</a>
                <ul>
                    <li><a href="#">Ada</a></li>
                    <li><a href="#">Saarland</a></li>
                </ul>
            </li>
        </ul>
    </li>
</ul>

有什么好办法绘出?

例如,recursively 使用 target来绘制tree:

<script id="tpl" type="text/html">
    {{ target: some }}
        {{ use: menu(children=${menu}) }}
    {{ /target }}  

    {{ target: menu }}
        <ul>
            {{ for: ${children} as ${item} }}
            <li>
                <a href="#">${item.text}</a>
                {{ if: ${item.children} && ${item.children.length} }}
                    {{ use: menu(children=${item.children}) }}
                {{ /if }}
            </li>
            {{ /for }}
        </ul>
    {{ /target }}
</script>
<script id="self" type="text/javascript">
    etpl.config({
        commandOpen: '{{',
        commandClose: '}}'
    });
    etpl.compile($('#tpl').html());
    alert(
        etpl.render(
            'some', 
            {
                menu: [
                    { text: 'Aberdeen' },
                    { text: 'Ada' },
                    { 
                        text: 'Salzburg',
                        children: [
                            { text: 'Perch' },
                            { text: 'D' },
                            { 
                                text: 'Delphi',
                                children: [
                                    { text: 'Ada' },
                                    { text: 'Saarland' }
                                ]
                            }
                        ]
                    }
                ]
            }
        )
    );
</script>

当然,现在是不行的,compile时会 Maximum call stack size exceeded
支持这种事情容易么?
或者,有什么别的方法?

etpl2to3工具

由于etpl3的语法相对etpl2有一些变化,需要写一个工具,方便使用者转换

tool/etpl2to3.js

【也许有点洁癖】尝试了一段时间,请问如何处理格式的问题?

比如希望:

<!-- target: pc -->
<head>
  <!-- import: meta -->
</head>
<!-- /target -->

<!-- target: meta -->
<meta charset="utf-8" />
<meta name="renderer" content="webkit" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<!-- /target -->

希望生成:

<head>
  <meta charset="utf-8" />
  <meta name="renderer" content="webkit" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
</head>

【保留空格】,更复杂的情况是,有部分 target 需要被插入到任意深度的结构中

不知道这样描述的够不够清楚。

etpl中对于model数据的解析问题

hi,大神,帮忙看一个问题

场景:创建一个target,使用var定义maxLength变量,并赋值为model中rule.maxLength字段。
ddd

此时maxLength解析出来的结果是一个字符串

gv("rule.maxLength",["rule","maxLength"])

换成另一种写法
qq20140509171316

就是可以的。

是我姿势不对么?

哦,补充一下,这个textbox我之后会通过use的形式引用。

data有get方法时,有bug

  1. for循环内部对变量无法正常读取:因为直接把${xxx.xxx}传给了data的get方法,但是get方法并不知道上下文,所以无法正常读取
  2. get方法优先,var的变量无法读取到

在use/import命令中支持通过变量指定target名称

如题,需求大概是这样:

<!--use: ${someTargetName}(someVariable=${conf.name})-->
<!--import: ${yetAnotherTargetName}-->

应用场景是这样的:

  1. 有一个表单,包含多种类型的字段,有一个schema来描述这个表单的每个字段。
  2. 每种类型字段有一个对应的etpltarget模板,模板的内容根据字段类型与schema配置而不同。
  3. 根据表单的描述schema中对应字段配置,使用use命令动态渲染出dom结构。
<!--target:form-->
<!--for:${fields} as ${field}-->
<!--use:${field.type}(conf=${field})-->
<!--/for-->

<!--target:TextBox-->
<div class="ui-textbox"> ... </div>
var formRenderer = etpl.getRenderer('form');
var html = formRenderer({
  fields: [{
    type: 'TextBox',
    ...
  }]
});
document.getElementById('main').innerHTML = html;

当然这里可以先用js来按字段一个一个render,就是比较笨拙呢。所以请大神们看看是否合适在etpl中支持此功能。

将 etpl 发布到 npm

我打算在 node 环境下使用 etpl,看到目前 npm 上的版本只是占个位
既然都支持了,发个版呗 :octocat:

“匿名target” 和 “返回第一个target编译后的renderer函数” 的 疑惑

匿名target
以及
返回第一个target编译后的renderer函数 的什么是 第一
似乎不看代码不能理解。

“默认生成target” 这一功能,似乎是为了 “与其他模板引擎使用方式类似” 而引入

比如这个模板:

<script id="tpl" type="text/html">
    bbbbbegin

    {{ use: t0 }}

    {{ target: t0 }}
        asdf
    {{ /target }}

    eeeeeend
</script> 

那么我一上来就认为

var renderer = etpl.compile($('#tpl').html());

返回的renderer就应该涵盖了 整个的模板片段的渲染。
但是事实上,返回的是一个只含有用于渲染“bbbbbegin”的renderer。
而由于是“匿名”的target,导致渲染“eeeeend”的render没有其他办法能够得到。

所以,是否

  • 在文档中说明:匿名的target中不能使用别的target 或者 使用别的话target必须放在最后

or

  • 改改“匿名”target的定义 或者 改改“返回第一个target编译后的renderer函数”这个设定

不知我的理解是否有问题

target、import、block的关系

我个人希望是可以有这样的功能:

<!-- target: page -->
<!-- import: header -->
    <!-- block: username -->
    Gray Zhang
    <!-- /block -->
<!-- /import -->

<div id="main">
    <!-- import: sidebar -->
        <!-- block: currentLink -->
        <a href="#/">Main Page</a>
        <!-- /block -->
    <!-- /import -->

Body Content

</div>

<!-- import: footer -->
    <!-- block: copyRight -->
    All Rights Reserved Baidu .Inc
    <!-- /block -->
<!-- /import -->

重点:

  1. import/import间可以通过block来覆写
  2. 一个targetimport时,其内部的block仅作用于import/import之间,出了/import之后就没这个block

这种方式可以非常灵活地组装各个target,相比master的机制灵活性有一定提升,当然master的存在也可以用于布局稳定的场景来减少重复代码

开放一个获取变量值的方法

因为RIA是专注比较重型解决方案,所以有些东西并不一定与etpl的理念相符,如果此Issue并不符合etpl希望提供的内容,直接Close就好

在系统中,我们会有一些经常会用到的数据,这些数据希望可以在任何的代码中使用,即全局的。其中比较典型的就是枚举,比如我们会在JavaScript中有这样的代码:

var Status = require('foo/enum').Status;
if (entity.status === Status.ENABLED) {
    // ...
}

由于JavaScript有AMD的模块管理,自然代码很容易。但是放在模板中,就比较难了,我们希望模板中有这样的代码:

<!-- if: ${entity.status} === ${slot.enum.Status.ENABLED} -->
WTF
<!-- /if -->

为了达到这个目标,我们会有一些方案:

  1. Model中加入Status这个属性,但这样需要不少额外的编码,在各种Model中都要有类似的代码放进许许多多的静态对象
  2. 改写View#getTemplateData,通过重写get方法来解析变量名称,通过一定的规则来获取常量。这种方案看上去美好,但仅限于View中使用的模板,在其它地方使用etpl就没有这样的效果,事实是我们确实有很多地方使用着etpl

因此,我希望etpl可以有一个方法:

{*} TemplateEngine#getVariableValue({string} variableName})

这个方法默认的实现etpl内部肯定是有的,开放到prototype上以便系统去定制,通过实现自身的TemplateEngine子类来增加各种不同的功能

对于这一功能我希望支持的原因是,我相信etpl内部本身就有这样的逻辑,只是从内部转到prototype上来,应该不会影响etpl的体积

增加config接口

现在用处不大,但总会用上的,这样:

var engine = new template.Engine();
engine.config('encodeHTML', false);

表示默认不要进行HTML转义。

当然也有template.config('encodeHTML', false);的写法。

另外还要全局配置(区别于template.config,那个只给默认引擎生效),因此要有

template.global('encodeHTML', false);

关于filter的实现

#1 所说,管道模型中的filter我觉得是个核心。

filter的管道模式肯定没问题,一个接一个调用就行了,关键在于filter可以传参。

一般这个时候,有2种设计:

engine.addFilter(
    'date',
    function (value, format, useUTC, includeTime) {
        // ...
    }
);

这种设计暴力简单,输入的内容放在第1个参数,其它放在后面。另一种:

engine.addFilter(
    'date',
    function (format, useUTC, includeTime) {
        return function (value) {
            // 用到format等参数
        }
    }
);

典型的函数工厂式实现,mustache应该是这么实现的。代价是每次用要先调用函数返回真正的filter,然后再调用filter输出内容。

优点是对参数的变化适应性要强一些,比如我们要增强模板,使用filter时不仅仅给value,还会给当时的Scope对象,当时的各种上下文,那第1种方案基本没法动了,第2种则很容易,内存函数多几个参数而已,爱用不用。

我建议是第2种,感觉filter多一次函数调用不应该会产生太多的性能问题,如果真要性能了,filter的实现者有义务做好memoize工作

命令有空行时无效

如沟通,类似这样的:

<!--
    use: someTarget(
        a = 1,
        b = 2
    )
-->

解析不了,原谅我洁癖……

去除一些空行

比如这样的一段模板

<!-- target: ok -->
OK!

直接渲染的时候会变成

[有个空行]
OK!

OK!前面多出一个空行来。这在组装HTML的时候不会出现太多问题,但是如果用etpl来做代码生成之类的工作,空行就显得尤其重要

因此希望可以至少把各命令前后的换行给去掉,比如

<!-- if: x -->
this is x

则要直接输出this is x,前面不会有2个空行

循环中获取元素本身报错!

<!-- for: ${list} as ${unit} -->
<div class="text-center">
<script type="text/javascript">
// 这样没问题
window["list" + '${unit.TId}'] = ${unit.TId};
// 这样就报错,导致我没法获取到对象本身
window["list" + '${unit.TId}'] = ${unit};
</script>
<a href="javascript:void(0)" onclick="editOne('${unit.TId}')"> 编辑</a>
</div>
<!-- /for -->

etpl SEO的问题

请教各位大牛,如果用此模板,SEO的问题如何处理?

是不是需要专门一个地方保存引擎模板?

我的意思是这样的,在页面上有两块区域,一个是隐藏的,写模板,一个是填充数据,如下
<div id="template"> // 模板写在这里 </div>

<div id="result"> // 把结果插入到这里 </div>

是不是每个页面都至少需要这么两块地方?

假设合并成一块,那么第一次 compile 没问题,第二次 compile就不行了,因为已经没有模板了(被数据填充了)?

<div id="templateANDresult"> // 第一次模板写在这里,如果把结果也插入到这里,那么模板就没了。 </div>

不知道 eptl 是不是那个缓存是不是解决这种问题的?

etpl.get 抛异常

2.0.10版,遇到个问题~

<script id="tpl" type="text/html">
    {{ target: ta1 }}
        ta1ta1ta1ta1
    {{ /target }}

    {{ target: ta2 }}
        ta2ta2ta2ta2
        {{ use: ta1 }}
        ta2ta2ta2ta2
    {{ /target }}
</script>

·

    etpl.config({
        commandOpen: '{{',
        commandClose: '}}'
    });
    etpl.compile($('#tpl').html());
    etpl.get('ta2');

get时抛异常,因为UseCommand没有getContent方法

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.