一个对前端模板技术的全面总结
发布在Regularjs: 中文指引2015年8月7日view:40913模板引擎
在文章任何区域双击击即可给文章添加【评注】!浮到评注点上可以查看详情。

此文的写作耗时很长,称之为雄文不为过,小心慢用

此文缘由

其实从发布regularjs之后,我发现在google搜索regularjs 不是给我这个画面

regular-search-1

<!-- more -->

就是给我这个画面

regular-search-2

突然发现取名字真是个大学问,当时就基本预计到了会有不明真相的朋友认为它只是一个照搬angularjs的家伙,对于这点,有兴趣的朋友可以看下【为什么要造Regularjs这个轮子】

而在这个文章,我不会直截了当去与angular做直接的对比,而是从最基本原理开始对现有的模板解决方案进行一个全面的分类,同时会给出它们的一些或优或劣的特性,这些特性基本都是本质性的,即它不为维护者的水平高低和勤勉与否所限制,所以是具有客观性的。


什么是模板解决方案?

你可以先简单的理解为模板引擎。

事实上前端的模板解决方案已经从 “选出一个好用的模板好难” 发展到了 “从这么多模板中选一个好难的”的阶段,Template-Engine-Chooser!似乎也开始无法跟上节奏了。再加上目前Dom-based的模板技术的崛起(angularjs, knockout等),渐渐让这个领域有乱花渐欲迷人眼的感觉。

这篇文章会对当今前端界的三种截然不同的模板方案做一个全面的对比,它们分别是

  1. String-based 模板技术 (基于字符串的parse和compile过程)
  2. Dom-based 模板技术 (基于Dom的link或compile过程)
  3. 杂交的Living templating 技术 (基于字符串的parse 和 基于dom的compile过程)

同种类型的模板技术的可能性都是相同的,即同样身为dom-based的vuejs如果愿意可以发展为angularjs的相同功能层级。

(注: 其实这么说作者后续思考后觉得并不是很妥当,因为决定这类框架的还有重要一环就是它们的数据管理层:,比如是基于脏检查还是基于setter和getter,就会有截然不同的定位)

另外需要注意的是任何一种类型的模板技术都是不可被替代的,它们甚至可以结合使用,并且很长一段时间内还会继续共存下去。

除此之外另外一种奇葩模板技术本文也会提到即react,了解后你会发现它的特性更接近于Living templating。

在进入介绍之前,我们需要先过一下不得不说的 InnerHTML,它是本文的关键因素。

innerHTML

我不认为还需要从innerHTML的细节讲起,我们对它太熟悉了,那就直接从优劣开始讲吧!

innerHTML 毫无疑问是好的

innerHTML正是成为 web 标准 前,它当之无愧的已经是大家公认的事实标准,这是因为:

1 . 它便于书写并且直观

想象下你必须添加如下的html到你的文档里

<h2 title="header">title</h2>
<p>content</p>

直接使用 innerHTML

node.innerHTML = "<h2 title="header">title</h2><p>content</p>"

在对比使用Dom API

var header = document.createElement('h2');
var content = document.createElement('p');
h2.setAttribute('title', 'header');
h2.textContent = 'title';
p.textContent = 'content';
node.appendChild(header);
node.appendChild(content);

innerHTML 毫无疑问赢得了这张比赛.

尽管部分框架例如mootools:Element 提供了高效的API来帮助你创建dom结构,innerHTML仍然会是大多数人的最佳选择

2 . 它很快,特别在old IE

随着浏览器的发展,这个测试可能越来越不符合实际,innerHTMLDom Level 1创建dom结构的差距正变得原来越小

3. 它完成进行了String -> Dom的转换

这个论点有点拗口,事实上后续要提到的两类模板技术都是因为这个特点而与其有了强依赖


然而我们又清楚的被告知:

The recommended way to modify the DOM is to use the DOM Level 1 API. ——Chapter 15 of "Javascript: The Definitive Guide_"

为什么?

innerHTML 有时候又是不听话的

1. 安全问题

innerHTML 具有安全隐患.,例如:

document.body.innerHTML = "<img src=x   onerror='alert(xss)'/>"

我知道像你这样优秀的程序员不会写出这样的代码,但当html片段不完全由你来控制时(比如从远程服务器中),这会成为一个可能引爆的炸弹。

2. 它很慢

等等,你刚才说了它很快! 是的,但是如果你仅仅为了替换一个属性而用innerHTML替换了所有的Dom节点,这显然不是一个明智的决定,因为你深知这是低效的。所以说:

Context is everything

所有离开背景谈的性能、功能、性功能都是伪科学

3. 它很笨

它会完全移除所有现有的Dom,并重新渲染一遍,包括事件和状态都以不复存在,这点利用innerHTML来进行render的框架(例如Backbone)的开发者应该深有体会,为了减少损失,不能不把View拆的越来越细,从而抱着看似“解耦完美”的架构体系进入了维护的深渊。

注: 其实react的最大贡献就是它差不多是提供了一个更smart的innerHTML解决方案。

4. 有可能会创建出意料之外的节点.

由于html的parser非常的“友好”, 以至于它接受并不规范的写法,从而创建出意料之外的结构,而开发者得不到错误提示。


好了,到现在为止,我们大概了解了innerHTML这个朝夕相处的小伙伴,接下来我们正式聊一聊模板技术,首先我们从最常见的“String-based templating”开始

String-based templating

基于字符串的模板引擎最大的功劳就是把你从大量的夹带逻辑的字符串拼接中解放出来了,由于它的完全基于字符串的特性,它拥有一些无可替代的优势。

It is essentially a way to address the need to populate an HTML view with data in a better way than having to write a big, ugly string concatenation expression. --- cited from http://www.dehats.com/drupal/?q=node/107

示例

  1. mustache及其衍生: 弱逻辑
  2. Dust.js: 强逻辑 (推荐)
  3. doT.js: 超级快

基本原理

string-based

如上图所示,我们发现字符串模板强依赖于innerHTML(渲染), 因为它的输出物就是字符串。由于这篇文章的重点不在这里,我们不会再对它们如何使用作深究。

优点

  1. 快速的初始化时间: 很多angular的簇拥者在奚落String-based templating似乎遗漏了这一点。
  2. 同构性: 完全的dom-independent,即可作为用服务器端和浏览器端(客官先不要急着搬phantomjs哈).
  3. 更强大的语法支持:因为它们都是不是自建DSL就是基于JavaScript语法,Parser的灵活性与受限于HTML的Dom-based模板技术不可同日而语

缺点

  1. 安全隐患: 见innerHTML
  2. 性能问题:见 innerHTML.
  3. 不够聪明: 见innerHTML(呵呵),除此之外render之后数据即与view完全分离。

尽管在这几年的发展之下,由于异常激烈的竞争,基于字符串的前端模板技术变得越来越快,但是它们显然大部分都遗漏了一些问题

  1. 大侠们你们没有考虑进把输出字符串加载到Dom的时间,这才是真正瓶颈之一
  2. 不在相同功能前提下的对比有意义么?

Dom-based Template Engine

近几年,借着Angularjs的东风,Dom-based的模板技术开始大行其道,与此同时也出现了一些优秀的替代者,就我们国人而言,近的就有@尤小右Vuejs司徒大大avalonjs。看仓库就可以发现风格也是完全不同:1) 一个简洁优雅 2)一个奔放不羁

示例

  1. Angularjs: 都28000star了还需多说么
  2. Knockout: 在此领域内,对Web前端而言是鼻祖级的

大致流程

dom-based

Dom-based的模板技术事实上并没有完整的parse的过程(先抛开表达式不说),如果你需要从一段字符串创建出一个view,你必然通过innerHTML来获得初始Dom结构. 然后引擎会利用Dom API(attributes, getAttribute, firstChild… etc)层级的从这个原始Dom的属性中提取指令、事件等信息,继而完成数据与View的绑定,使其”活动化”。

所以Dom-based的模板技术更像是一个数据与dom之间的“链接”和*“改写”*过程。

注意,dom-based的模板技术不一定要使用innerHTML,比如所有模板都是写在入口页面中时, 但是此时parse过程仍然是浏览器所为。

优点

  1. 是活动的: 完成compile之后,data与View仍然保持联系,即你可以不依赖与手动操作Dom API来更新View
  2. 运行时高效的: 可以实现局部更新
  3. 指令等强大的附属物帮助我们用声明式的方式开发APP

缺点

  1. 部分请见innerHTML
  2. 没有独立的Parser,必须通过innerHTML(或首屏)获取初始节点,即它的语法是强依赖与HTML,这也导致它有潜在的安全问题
  3. 信息承载于属性中,这个其实是不必要和冗余的。 部分框架在读取属性后会通过诸如removeAttribute的方式移除它们,其实这个不一定必要,而且其实并无解决它们Dom强依赖的特性,比如如果你查看[angular的todomvc]的节点,你会发现它的输出是这样的: angular-todo
  4. FOUC(Flash of unstyled content):即内容闪动,这个无需多说了,只怪它初次进入dom的内容并不是最终想要的内容。

Living Template Engine

String-based 和 Dom-based的模板技术都或多或少的依赖与innerHTML, 它们的区别是一个是主要是为了Rendering 一个是为了 Parsing 提取信息

所以为什么不结合它们两者来完全移除对innerHTML的依赖呢?

事实上,值得庆幸的是,已经有几个现实例子在这么做了。

例子

  1. htmlbar: 运行在handlebar之后的二次编译
  2. ractivejs: 独立
  3. Regularjs 独立, 本文作者结精之一

基本原理

Living-Template

就如图中所示,parse和compile的过程分别类似于String-based 模板技术 和 Dom-based模板技术。

下面来完整讲述下这两个过程

1 . Parsing

首先我们使用一个内建DSL来解析模板字符串并输出AST。

例如,在regularjs中,下面这段简单的模板字符串

<button {{#if !isLogin}} on-click={{this.login()}} {{/if}}>
  {{isLogin? 'Login': 'Wellcome'}}
</button>'

会被解析为以下这段数据结构

[
  {
    "type": "element",
    "tag": "button",
    "attrs": [
      {
        "type": "if",
        "test": {
          "type": "expression",
          "body": "(!_d_['isLogin'])",
          "constant": false,
          "setbody": false
        },
        "consequent": [
          [
            {
              "type": "attribute",
              "name": "on-click",
              "value": {
                "type": "expression",
                "body": "_c_['login']()",
                "constant": false,
                "setbody": false
              }
            }
          ]
        ],
        "alternate": []
      }
    ],
    "children": [
      {
        "type": "expression",
        "body": "_d_['isLogin']?'Login':'Wellcome'",
        "constant": false,
        "setbody": false
      }
    ]
  }
]

这个过程有以下特点

  1. 灵活强大的语法,因为它与基于字符串的模板一般,DSL是自治的,完全不依赖与html,你可以想像下dom-based的模板的那些语法相关的指令,事实上它们甚至无法表达上述那段简单的模板的逻辑。
  2. Living模板技术需要同时处理dsl元素xml元素来实现最终视图层的活动性,即它们是dom-aware的,而在字符串模板中其实xml元素完全可以无需关心,它们被统一视为文本元素

2 Compiler

结合特定的数据模型(在regularjs中,是一个裸数据), 模板引擎层级游历AST并递归生成Dom节点(不会涉及到innerHTML). 与此同时,指令、事件和插值等binder也同时完成了绑定,使得最终产生的Dom是与Model相维系的,即是活动的.

事实上,Living template的compile过程相对与Dom-based的模板技术更加纯粹, 因为它完全的依照AST生成,而不是在原Dom上的改写。

以上面的模板代码的一个插值为例:{{isLogin? 'Login': 'Wellcome'}}。一旦regularjs的引擎遇到这段模板与代表的语法元素节点,会进入如下函数处理

// some sourcecode from regularjs
walkers.expression = function(ast){
  var node = document.createTextNode("");
  this.$watch(ast, function(newval){
    dom.text(node, "" + (newval == null? "": String(newval)));
  })
  return node;
}

正如我们所见, 归功于$watch函数,一旦表达式发生改变,文本节点也会随之改变,这一切其实与angularjs并无两样(事实上regularjs同样也是基于脏检查)

与Dom-based 模板技术利用Dom节点承载信息所不同的是,它的中间产物AST 承载了所有Compile过程中需要的信息(语句, 指令, 属性…等等). 这带来几个好处

  1. 轻量级, 在Dom中进行读写操作是低效的.
  2. 可重用的.
  3. 可序列化 , 你可以在本地或服务器端预处理这个过程。
  4. 安全, 因为安全不需要innerHTML帮我们生成初始Dom

如果你查看Living Template的输出,你会发现是这样的

regular-todo

只有需要的内容被输出了

_总结Living templating _

我们可以发现Living templating几乎同时拥有String-based和Dom-based模板技术的优点

利用一个如字符串模板的自定义DSL来描述结构来达到了语法上的灵活性,并在Parse后承载信息(AST)。而在Compile阶段,利用AST和Dom API来完成View的组装,在组装过程中,我们同样可以引入Dom-based模板技术的诸如Directive等优良的种子。

living template’s 近亲 —— React

React当然也可以称之为一种模板解决方案,它同样也巧妙规避了innerHTML,不过却使用的是截然不同的策略:react使用一种virtual dom 的技术,它也同样基于脏检查,不过与众不同的是,它的脏检查发生在view层面,即发生在virtual dom上,从而可以以较小的开销来实现局部更新。

Example

var MyComponent = React.createClass({
 render: function() {
   if (this.props.first) {
     return <div className="first"><span>A Span</span></div>;
   } else {
     return <div className="second"><p>A Paragraph</p></div>;
   }
 }
});

同样的逻辑使用regularjs描述

{{#if first}}
  <div className="first"><span>A Span</span></div>
{{#else}}
  <div className="second"><p>A Paragraph</p></div>;
{{/if}}

仁者见仁智者见智, 反正我倾向于使用模板来描述结构,而不是杂糅Virtual dom和js语句。你呢?

值得一提的是,由于React的特性,它两次render之间,内部节点的替换是无法预计的(参考这里),所以无法有效的保留信息,所以它也有大量的关于id的placeholder存在。你可以同样查看react-todomvc生成的节点

一个全面的对照表

Contrast /Solutions String-based templating Dom-based templating Living templating
例子 Mustache,Dustjs Angularjs, Vuejs Regularjs 、Ractivejs、htmlbars
语法灵活性 ♦♦♦ ♦♦
活动性 X ♦♦♦ ♦♦♦
性能 初始: ♦♦♦
更新: ♦
初始: ♦
更新: ♦♦♦
初始: ♦
更新: ♦♦♦
安全性 ♦♦ ♦♦♦♦♦
Dom 无关 ♦♦♦♦♦ X ♦♦
SVG support(*1) X ♦♦ ♦♦♦

1. 任何一类无法被另一类全面替代 2. 它们并不是无法同时存在的,比如你可以使用字符串模板来生成Dom-based的模板需要的模板字符串。

参考资料

  1. Template Engines by @Sendhil
  2. string-templating-considered-harmful
评论
发表评论
1个月前

赞雄文,慢慢看

4个月前
赞了此文章!
8个月前

很棒!

8个月前

很棒!

10个月前

OneAPM Browser Insight是一个基于真实用户的 Web 前端性能监控平台,可以定位网站性能瓶颈,网站加速效果可视化;支持浏览器、微信、App 浏览 HTML 和 HTML5 页面。可以在官网注册体验哦~

1年前
赞了此文章!
1年前
赞了此文章!
1年前
赞了此文章!
1年前
赞了此文章!
1年前
赞了此文章!
1年前
添加了一枚【评注】:双引号
1年前
赞了此文章!
2年前
赞了此文章!
2年前
添加了一枚【评注】:只是用于文件的解析吗???
2年前
赞了此文章!
2年前
赞了此文章!
2年前
赞了此文章!
2年前
赞了此文章!
2年前
赞了此文章!
2年前
赞了此文章!
2年前

@拴萝卜的棍子 实际上依然有刻意规避innerHTML的感觉,安全性1星的描述实在是太严肃的指控;而living方式在接口设计上的确还是原来的string based的感觉,但实现层面却太复杂了(解析生成语法树再实现的感觉),我感觉我不乐意为了这些感觉其实不是什么问题的原因而引入一个过于复杂的系统。

2年前

对比的table我更新了下。可能我文章提到了太多次innerHTML导致给了你我完全是为了规避innerHTML的错觉

@waterwu 其实各个模板的优劣文章里差不多提到了, 评论里与别人的讨论也补充了一些,也提到了一些各个模板的适用场景。

关于所谓的Living模板,我就直接从使用的角度一句话说吧:** 就像你使用常规字符串模板的方式去构建你的view, 不过生成的Dom是活动的 **

2年前

为了消除innerHTML而搞一套DSL我觉得无法理解,因为HTML本身就是一个很不错的文档描述语言,而这时你又整出一个新的……

其实现在依赖于innerHTML的方式我觉得问题不是很大,结合$.parseHTML 与 视图类包装之后基本没有安全性问题,一次性渲染后再使用binding的方案基本上就没后顾之忧了。

虽然也可能仍然会有一些场景下会无法胜任,所以,我觉得最核心的问题是,各种模板方案的具体应用场景其实是啥?这个问题没搞清楚的话,讲再多模板方案都是浪费时间吧

2年前

@拴萝卜的棍子 嗯,我第一点里说的函数式其实差不多就是这个意思,数据状态的 snapshot 和产出的 DOM 状态一一对应。虽然我觉得 React 的 api 设计得还不够友好,但从开发思维上来说我觉得这是一个巨大优势。相信开发 regular 的时候你也能体会到,为了正确处理模板里的循环和 if 逻辑,内部实现是相当复杂的,因为在没有 vdom 的情况下,我们需要直接在真正的 DOM 上施加状态变化的结果,每一种不同的模板逻辑都需要不同的实现,不同逻辑的组合下还会有各种 edge case,可能产生的 bug 就多。但在 React 里根本就不存在这个问题,因为 vdom 的 render function 本身就已经包含了这个状态的映射过程,拿到最后产出的 vdom 和之前的 diff 一下就行了。换句话说只要 diff 算法做好了,就根本不需要担心模板逻辑的问题;而 diff 算法相比起各种可能的逻辑组合来说其实要简单很多。

2年前

@尤小右 关于react,我一直认为,它最大的意义在于用一个其它模型 vd,巧妙的实现了一个聪明版的innerHTML,这样带来的好处非常多,主要是使用它的开发者角度的,最深切的体会就是纯粹的单向性。

它的优点应该你表达有误 1. 不是你所说的创建scope,而是它内部不需要数据观察体系。 2. 也应该不需要编译,它事实上有编译就是根据vd来构建它的真实Dom树,它省略了一个步骤就是通过字符串来parse得到它的virtual dom. 但是事实上living template在上线时也没有parse这一步

它最大优势如我所说就是, 更smart的innerHTML 实现的纯粹单向架构

2年前

@尤小右 看了vueify 确实是个可行的方案, 特别是在普遍开发环境都有编译一环时。

2年前

最后,就目前来看,这几个 template 技术我个人也不觉得哪一个一定比另一个好,和具体实现关系很大,和开发者的需求偏好也有关系。但长远来看,如果浏览器的实现不发生重大变化的话,我觉得内部实现需要向 react 靠拢。

2年前

另外 dom-based 有一个好处就是因为是 valid HTML,所以完全可以用 jade 或者 haml 来写 Angular 或者 Vue 的模板…

2年前

@拴萝卜的棍子

  1. Angular 的安全问题很大一个来源是因为如果不手动 bootstrap 入口,默认是扫描全页的,换句话说任何来自服务端的 html 都可以包含会在编译过程中被执行的 js。Vue 内部真正用到 innerHTML 的地方只有转换未编译模板这一个用途,而未编译模板是不可能包含用户内容的,所以我还是不觉得 innerHTML 和 dom-based templating 的安全性有什么必然联系。

  2. login 的例子,现有的 dom-based 实现确实没法做到语法上完全等价。可以写 v-on="click: if (isLogin) login()",缺点是在未登录的情况下会多一个 event listener。但如果你的判断条件更复杂一些呢?那样的话放在模板里其实本来就是不合适的。

  3. Vue 其实也是以组件化为核心,你可以看看我最近搞的一个 browserify transform: https://github.com/vuejs/vueify

  4. 关于 React,我相信过两年你们的看法会变的…

2年前

@johnhax jade的话,其实是会带来比较大的问题。首先是基于缩进的,不好控制. 其次就是代码高亮了。 这边其实后台模板主要是freemarker 同事之间的水平其实有点层次不齐的 所以想弄成较熟悉的方式。

2年前

@johnhax 嗯 想过taglib的方式, 当时主要是为了 只需要配置 开关 符号就可以进行自定义. 因为插值 是{{}} 所以 就定为了 {{#rule xx}}的方式

2年前

擦,又被干了。。。这没法评论了。。。

2年前

再说一个,最小控制单元是节点这点其实不是必然的。

{{#if isLogin}} Digg Ignore {{/if}}

没有道理不可以写成:

Digg Ignore

c:if借用了taglib的形式。

所以这个我认为不是最关键的因素。

2年前

擦,后面的被吞掉了(显然代码有问题呵呵),补上。 {{= #{ mess going on... isn't HTML bad enough not to have to throw in additional crapola?),且更能凸显html结构的形式。

2年前

今天看了点regular,提点小意见。我认为regular不应该自称使用string-based模板,否则容易引起误解。

还有,我觉得regular的语法还是偏保守了。既然决定要dom-aware且不依赖于innerHTML,不如采用类jade的语法,parse起来其实比html要简单,parsing性能也(估计)会更好。更主要的是,可以得到更简洁(比如{{#if …}}…{{/if}},好多冗余符号,打起来,看起来都很累……我听过的一个对此的有力吐槽是:all of that crazy {# {{ {{{ %{ {{% <%

2年前

@司徒正美 其实如果模板可以序列化,完全没必要放在textarea之类的容器里啊. 我们是使用模块管理工具,统一管理

2年前

@johnhax 做到dom-aware是活动化的关键一步了. regularjs还多做了一步,可以通过mode参数切换dom-aware和非dom-aware。

2年前

@尤小右

首先,这篇文章不是为了否定任何一个技术,这三个技术方案我个人也是都在使用。

就个人而言,由于Dom-based 的模板其实就是标准的html,它最大的优势就是允许首屏就存在,这个对SEO是有些帮助的,最主要能简化逻辑,后台的预填数据可以通过服务器模板先填充,所以一般表单类的需求我目前还是经常会使用Dom-based的方式(虽然结构可能会由于指令被改写)。而Living Template 和 String Template 依赖的模板如果经过服务器填充就无法静态化,加之后续必然会有一次build -> inject到dom的过程。

  1. Dom-based, 它其实并不是常规意义上“编译过程”,innerHTML完成工作,框架引擎是无法参与其中的,它相当于是一个黑盒。angular的issue很大部分来源于这点 我也说到了完全没有风险 VS 有风险但是做了充分准备是有很大区别的。
  2. 再者,由于受限于与html的语法限制,你很难与自建DSL一样灵活的应用你的模板逻辑,我上面提到了一个简单的例子,你说用Dom-based如何描述? 不过这个各抒己见,我觉得都有存在的理由。
  3. 对于living dom的模板技术,与你理解类似。前后端组件共用个人觉得是伪科学,如果有开发者可以在实际项目中做到了,倒是可以出来分享一下。但是如果模板本身有独立的parse过程,至少我可以为爬虫提供一个轻量级的信息页面,dom-based无法通过轻量级的方式做到。
  4. Living Template最终会发展成一个专注于组件的技术。 它的宗旨是: “按你使用字符串模板的方式去构建你的View, 我来完成使其活动化和安全化的工作”。所以主要是替代String-based的技术来完成组件化的任务。我们实际开发中,组件都是 html+js的方式写了,发布后再序列化为一个组件文件。
  5. React 我无法多说什么了, 就如@johnhax 所说,可能是他们开发者水平太高了. 它理念的意义大于实际用途。
2年前

@尤小右 第一,按道理,只要实现对了,任何模板其实都可以视为无副作用的函数,我看不出react在这点上有什么优势。第二,不少string-based模板其实也可以嵌入js,所以一样图灵完备,至于linter,其他模板也能写出来。反过来说,我虽然并不支持logicless template,但是我也认为模板是要限制能力的,如果说可以任意写js,其实很容易出问题,比如无法确保纯函数(恰恰跟你说的第一点矛盾)。这是我不喜欢react的重要原因。当然我非常理解,facebook为什么产生了react,因为他们的前端其实都是接近全栈的,编程能力足够过关,所以js的代码架构本身不是问题,只要解决内嵌html的问题即可。可是大多数公司就很难达到这一点,前端的一般通用编程能力往往参差不齐。第三,react快实际上是因为它其实不是个纯前端模板,所有其他非纯前端模板也可以一样快。为题在于如何解决数据绑定。react的方法是它把大部分的问题留给你们自己搞,只解决vdom更新的核心问题。不得不说这个方法很聪明,也很适合facebook那种前端都是编程能手的公司。但是这也是一个偷懒方法,不是我认可的答案。

2年前

@拴萝卜的棍子 顺便提一下,如果考虑非纯前端的模板引擎,除了string-based之外,像jade,或者我开发的jedi,都是dom-aware的,虽然因为(暂时)没有数据绑定支持,但机制其实非常接近于你说的living template。

2年前

@拴萝卜的棍子 写得不错。先提一个,dom based将来可以有个方向是用html5的template元素来避免innerHTML。当然你说的living template也可以享受这个好处。总之string-based是最糟糕的方式(当然实现上最简单,比如米粽写的那个18行模板引擎)。

2年前

其实呢,我觉得 dom-based 的最大弱点是对于 dom 的依赖导致不能在非浏览器环境里渲染,这一点我是承认的,用 jsdom 的话很慢。不过实际应用中这样的需求还是很少,大部分 SPA 既不需要极限的首屏渲染,也不需要 SEO,再者也不是所有的应用都是用 Node 做后端的。加上现在 No-backend 的应用也越来越多了,比如 Firebase, Parse, AVOS Cloud 之类的,这样的架构下,前后端模板复用的意义无从体现。

另外关于 React,我虽然也不喜欢 vdom 的语法,但从设计思想上来说它是有很多优势的:

  1. React 本质上不是模板,它的作者是彻底的反模板主义者。React 的 “模板” 本质上就是一个没有副作用的函数。进去的是状态,出来的是对应的 vdom 结构,非常函数式。这样的设计让框架本身实现上可能出现的 bug 少了很多,只要保证 diffing 算法正确就可以了。
  2. 即使是 string-based 模板,依然受限于其模板语言本身的 DSL。React 的渲染函数由于本质上是 JavaScript,所以具有真正图灵完备的表达能力,还可以用 linter 来静态检查语法错误。
  3. 目前这些方案里 React 首屏渲染速度是最快的,因为不需要编译和创建 scope。

但是 React 的最大问题就是 render 函数逻辑一多就变得很难看… 作为要写 CSS 的人表示不能忍。

2年前

Dom-based 方式第一次 innerHTML 的内容依然是未编译的模板,不会包含任何用户提供的内容,为啥会有安全性问题?

2年前

@司徒正美 其实文章里的内容都是本质性。对于非首屏的内容,至少会调用一次innerHTML来获取初始节点吧,如果是解析后层级创建节点,那就是归属于文中的Living 模板范畴了。至于安全, 有隐患但是做了充足处理和完全没有隐患其实是两个完全不同的概念。 我并没有否定 Dom-based 的技术,事实上 在实际开发中,对于表单之类的需求我还是会使用自己开发的轻量的dom-based的模板技术。

2年前

Dom-based的模板技术,必然基于innerHtml

我的扫描系统就不是,innerHTML太多BUG了,我只是用于批量生成节点。

2年前

写得很详细,期待功能、性能、学习成本和代码量的比较

2年前

@拴萝卜的棍子 确认了一些,是不一定…少看了个字,抱歉..囧

2年前

@飞天小黑神猪 我貌似没说过这么绝对. 具体可以参考原理图下一段。 如果所有组件都是首屏就添加到页面上,那说明业务量还不够真实,否则使用innerHTML是必然的。

2年前

Dom-based的模板技术,必然基于innerHtml这点我有疑问.

我觉得只要设计的得当,可以通过递归遍历 dom 的attributes和childNodes,在第一次scan时,替换掉.并记录下来,后面数据改变后,只要更改相应的node element就可以.

innerHtml的性能应该还比这样低

2年前

@司徒正美 关于 ms- 等placeholder 是否去掉其实并不是关键问题了。 其次,react使用了vd是因为它的check发生在view层, 而其它框架都有自己的观察者体系,当然无需vd这一层, 遍历完之后 自然就完成了绑定关系。

2年前

@Bolt_白衣苍狗 还有这一说 非常意外 看来还能多一条feature

2年前

当我知道Window phone可以用javascript写应用的时候,非常兴奋的使用了regular、jquery,都因innerHTML警告而告终

看了这篇文章,我抱着试试看的态度使用regularjs成功运行了todoMVC,让我欣喜若狂。

2年前

这个不能叫模板技术吧,我只是在DOM树上添加一些标记,如ms-特性节点,{{}}文本节点,在第一次扫描后,都会全部去掉。

我们这样做,只是为了直接跳过jQuery这样的选择器引擎,直接得到所有要处理的节点(元素节点,特性节点,文本节点),与他们关联在一起的元数据(如ms-visible=”aaa”, visible表示做显示隐藏处理,aaa为VM的一个属性名,ms-visible所在的节点等等组成一个绑定对象 )放进一个个数组里,当VM的属性发生变动时,就会找到相关的数组,然后遍历数组中的绑定对象,然后通过visible对应的视图刷新函数进行更新。 没有像angular那样全局性的dirty checking,也不需要react的virtual dom diff。

之于nnerHTML,avalon内置了一个更安全的innerHTML方法,里面调用了parseHTML,能正确处理VML,SVG,HTML(具体可以看我的OniUI组件,我们就喜欢用VML,SVG来制作各种图形)

对于字符串模板,其最大缺点是要放到另一个容器里,如script, textarea, 或一个独立的文件,需要用户眼观八路,分散注意力,友好度太差,因此才被给我抛弃了。

WRITTEN BY
拴萝卜的棍子
零散的博文 无法分类成专栏 我都会放在我的博客http://leeluolee.github.io 欢迎订阅
TA的新浪微博
PUBLISHED IN
Regularjs: 中文指引

Regularjs: 利用动态模板快速创建数据驱动的组件.

我的收藏