前端模板技术面面观(1)
此文已由作者郑海波授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验
此文的写作耗时很长,称之为雄文不为过,小心慢用
此文缘由
其实从发布regularjs之后,我发现在google搜索regularjs 不是给我这个画面
就是给我这个画面
突然发现取名字真是个大学问,当时就基本预计到了会有不明真相的朋友认为它只是一个照搬angularjs的家伙,对于这点,有兴趣的朋友可以看下【为什么要造Regularjs这个轮子】。
而在这个文章,我不会直截了当去与angular做直接的对比,而是从最基本原理开始对现有的模板解决方案进行一个全面的分类,同时会给出它们的一些或优或劣的特性,这些特性基本都是本质性的,即它不为维护者的水平高低和勤勉与否所限制,所以是具有客观性的。
什么是模板解决方案?
你可以先简单的理解为模板引擎。
事实上前端的模板解决方案已经从 “选出一个好用的模板好难” 发展到了 “从这么多模板中选一个好难的”的阶段,Template-Engine-Chooser!似乎也开始无法跟上节奏了。再加上目前Dom-based的模板技术的崛起(angularjs, knockout等),渐渐让这个领域有乱花渐欲迷人眼的感觉。
这篇文章会对当今前端界的三种截然不同的模板方案做一个全面的对比,它们分别是
String-based 模板技术 (基于字符串的parse和compile过程)
Dom-based 模板技术 (基于Dom的link或compile过程)
杂交的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
随着浏览器的发展,这个测试可能越来越不符合实际,innerHTML和 Dom 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
示例
基本原理
如上图所示,我们发现字符串模板强依赖于innerHTML(渲染), 因为它的输出物就是字符串。由于这篇文章的重点不在这里,我们不会再对它们如何使用作深究。
优点
快速的初始化时间: 很多angular的簇拥者在奚落String-based templating似乎遗漏了这一点。
同构性: 完全的dom-independent,即可作为用服务器端和浏览器端(客官先不要急着搬phantomjs哈).
更强大的语法支持:因为它们都是不是自建DSL就是基于JavaScript语法,Parser的灵活性与受限于HTML的Dom-based模板技术不可同日而语
缺点
安全隐患: 见innerHTML
性能问题:见 innerHTML.
不够聪明: 见innerHTML(呵呵),除此之外render之后数据即与view完全分离。
尽管在这几年的发展之下,由于异常激烈的竞争,基于字符串的前端模板技术变得越来越快,但是它们显然大部分都遗漏了一些问题
大侠们你们没有考虑进把输出字符串加载到Dom的时间,这才是真正瓶颈之一
不在相同功能前提下的对比有意义么?
Dom-based Template Engine
近几年,借着Angularjs的东风,Dom-based的模板技术开始大行其道,与此同时也出现了一些优秀的替代者,就我们国人而言,近的就有@尤小右的Vuejs 和 司徒大大的avalonjs。看仓库就可以发现风格也是完全不同:1) 一个简洁优雅 2)一个奔放不羁
示例
Angularjs: 都28000star了还需多说么
Knockout: 在此领域内,对Web前端而言是鼻祖级的
大致流程
Dom-based的模板技术事实上并没有完整的parse的过程(先抛开表达式不说),如果你需要从一段字符串创建出一个view,你必然通过innerHTML来获得初始Dom结构. 然后引擎会利用Dom API(attributes, getAttribute, firstChild... etc)层级的从这个原始Dom的属性中提取指令、事件等信息,继而完成数据与View的绑定,使其"活动化"。
所以Dom-based的模板技术更像是一个数据与dom之间的“链接”和“改写”过程。
注意,dom-based的模板技术不一定要使用innerHTML,比如所有模板都是写在入口页面中时, 但是此时parse过程仍然是浏览器所为。
优点
是活动的: 完成compile之后,data与View仍然保持联系,即你可以不依赖与手动操作Dom API来更新View
是运行时高效的: 可以实现局部更新
指令等强大的附属物帮助我们用声明式的方式开发APP
缺点
部分请见innerHTML
没有独立的Parser,必须通过innerHTML(或首屏)获取初始节点,即它的语法是强依赖与HTML,这也导致它有潜在的安全问题
信息承载于属性中,这个其实是不必要和冗余的。 部分框架在读取属性后会通过诸如removeAttribute的方式移除它们,其实这个不一定必要,而且其实并无解决它们Dom强依赖的特性,比如如果你查看[angular的todomvc]的节点,你会发现它的输出是这样的:
FOUC(Flash of unstyled content):即内容闪动,这个无需多说了,只怪它初次进入dom的内容并不是最终想要的内容。
免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 使用Phaser开发你的第一个H5游戏(一)