乐趣区

关于web-components:从HTML-Components的衰落看Web-Components的危机

搞前端工夫比拟长的同学都会晓得一个货色,那就是 HTC(HTML Components),这个货色名字很当初风行的 Web Components 很像,但却是不同的两个货色,它们的思路有很多类似点,然而前者已是昨日黄花,后者方兴未艾,是什么造成了它们的这种差距呢?

HTML Components 的一些个性

因为支流浏览器外面只有 IE 反对过 HTC,所以很多人潜意识都认为它不规范,但其实它也是有规范文档的,而且到当初还有链接,留神它的工夫!

http://www.w3.org/TR/NOTE-HTM…

咱们来看看它次要能做什么呢?

它能够以两种形式被引入到 HTML 页面中,一种是作为“行为”被附加到元素,应用 CSS 引入,一种是作为“组件”,扩大 HTML 的标签体系。

行为

行为(Behavior)是在 IE5 中引入的一个概念,次要是为了做文档构造和行为的拆散,把行为通过相似款式的形式隔离进来,具体介绍在这里能够看:

http://msdn.microsoft.com/en-…

行为里能够引入 HTC 文件,方才的 HTC 标准里就有,咱们把它摘录进去,能看得分明一些:

// engine.htc

<HTML xmlns:PUBLIC="urn:HTMLComponent">
<PUBLIC:EVENT NAME="onResultChange" ID="eventOnResultChange" />

<SCRIPT LANGUAGE="JScript">

function doCalc()
{
   :
   oEvent = createEventObject();
   oEvent.result = sResult;
   eventOnResultChange.fire (oEvent);

}
<HTML xmlns:LK="urn:com.microsoft.htc.samples.calc">
<HEAD>
<STYLE>
   LK\:CALC    {behavior:url(engine.htc); } 
</STYLE>
</HEAD>

<LK:CALC ID="myCalc" onResultChange="resultWindow.innerText=window.event.result">
<TABLE>
<TR><DIV ID="resultWindow" STYLE="border:'.025cm solid gray'" ALIGN=RIGHT>0.</DIV></TR>
<TR><TD><INPUT TYPE=BUTTON VALUE="7"></TD>
    <TD><INPUT TYPE=BUTTON VALUE="8"></TD>
    <TD><INPUT TYPE=BUTTON VALUE="9"></TD>
    <TD><INPUT TYPE=BUTTON VALUE="/"></TD>
    <TD><INPUT TYPE=BUTTON VALUE="C"></TD>
</TR>
<TR><TD><INPUT TYPE=BUTTON VALUE="4"></TD>
    <TD><INPUT TYPE=BUTTON VALUE="5"></TD>
    <TD><INPUT TYPE=BUTTON VALUE="6"></TD>
    <TD><INPUT TYPE=BUTTON VALUE="*"></TD>
    <TD><INPUT TYPE=BUTTON VALUE="%" DISABLED></TD>
</TR>
<TR><TD><INPUT TYPE=BUTTON VALUE="1"></TD>
    <TD><INPUT TYPE=BUTTON VALUE="2"></TD>
    <TD><INPUT TYPE=BUTTON VALUE="3"></TD>
    <TD><INPUT TYPE=BUTTON VALUE="-"></TD>
    <TD><INPUT TYPE=BUTTON VALUE="1/x" DISABLED></TD>
</TR>
<TR><TD><INPUT TYPE=BUTTON VALUE="0"></TD>
    <TD><INPUT TYPE=BUTTON VALUE="+/-"></TD>
    <TD><INPUT TYPE=BUTTON VALUE="."></TD>
    <TD><INPUT TYPE=BUTTON VALUE="+"></TD>
    <TD><INPUT TYPE=BUTTON VALUE="="></TD>
</TR>

</TABLE>
</LK:CALC>
</HTML>

这是一个计算器的例子,咱们先大抵看一下代码构造,是不是很清晰?再看看当初用 jQuery,咱们是怎么实现这种货色的:是用选择器抉择这些按钮,而后增加事件处理函数。留神你多了一步抉择的过程,而且,整个过程混淆了申明式和命令式两种代码格调。如果依照它这样,你所有的 JS 根本都丢在了隔离的不相干的文件中,整个是一个配置的过程,拆散得很洁净。

除了这种计算器,还有标准文档中举例的扭转界面展现,或者增加动画之类,留神它们的切入点,都是相当于附加在特定选中元素上的行为,即便 DOM 不给 JS 裸露任何选择器,也毫无影响,因为它们间接就通过 CSS 的选择器挂到元素上了。

这种在当初看来,意义不算显著,当初广为应用的先抉择元素再增加事件,也是不错的展示和行为拆散形式。

但另外一种应用形式就不同了。

组件

广义的 HTML5 给咱们带来了什么?是很多新增的元素标签,比方 section,nav,acticle,那这些货色跟原先间接用 div 实现的,益处在哪里呢?在于语义化。

所谓语义化,就是一个元素能清晰表白本人是干什么的,不会让人有歧义,像 div 那种,能够类比成是一个 Object,它不具体示意什么货色,但能够当成各种货色来用。而 nav 一写,就晓得,它是导航,它就像有 class 定义的一个实体类,能表白具体含意。

那么,原有的 HTML 元素显然是不够的,因为理论开发过程中要表白的货色显然远远超出这些元素,比方日历,这种货色就没有一个元素用来形容它,更不用说在一些企业应用中可能会呈现的树之类简单控件了。

不提供原生元素,对开发造成的困扰是代码写起来麻烦,具体能够看之前我在知乎的一个回复,第三点:

http://www.zhihu.com/question…

所以,大家都想方法去提供本人的裁减元素的形式,当初咱们是晓得典型的有 angularjs,polymer,但很早的时候也不是没有啊:

http://msdn.microsoft.com/en-…

看,这就是 HTC 的增加自定义元素的形式,每个元素能够定义本人对外提供的属性、办法,还有事件,本人外部能够像写一个新页面一样,专一于实现性能。而且你发现没有,它思考得很久远,提供了命名空间,避免你在一个页面引入两个不同组织提供的同名自定义元素。

这个货色就能够称为组件了,它跟外界是齐全隔离的,外界只有把它拿来就能够用,就像用原生元素一样,用选择器抉择,设置属性,调用办法,增加事件处理等等,而且,留神到没有,它的属性是带 get 和 set 的,这是如许梦寐以求的货色!

正是因为它这么好用,所以在那个时代,咱们用它干了很多货色,封装了各种根底控件,比方树,数据表格,日期抉择,等等,甚至过后也有人厌弃浏览器原生 select 和 radio 不难看,用这么个货色,外面封装了图片来模仿性能,替换原生的来用。

过后也有人,比方我在 04 年就想过,能不能把这些扩大化,扩大到除了根底控件之外的中央,把业务的组件也这么搞一下,所有皆组件,多好?

但有些事件我直到起初很久当前才想明确,基于业务的端到端组件尽管写起来很不便,却是有致命缺点的。

到这里为止,对 HTML Components 的回顾告一段落,也不探讨它为什么就没了之类,这外面争议太大,我只想谈谈从这外面,能看到 Web Components 这么个大家寄予厚望的新规范须要面对一些什么问题。

Web Components 的挑战

以下逐条列出,挨个阐明,有的曾经有了,有的差一些,有的没有,不论这么多,总之谈谈我心目中的这个货色该当是怎么的。

自定义元素标签反对命名空间
起因我后面曾经说了,可能会有不同组织实现同类性能的组件,存在于同一个页面内,引起命名歧义,所以我想了很久,还是感觉有前缀比拟好:

<yours:ComponentA></yours:ComponentA>
<his:ComponentA></his:ComponentA>

甚至,这里的前缀还能够是个简称别名,比方 yours=com.aaa.productA,这可能只有简单到肯定水平才会呈现,大家不要认为这太夸大,但总有一天 Web 体系能构建超大型软件,到那时候你就晓得是不是可能了。

款式的部分作用域

这个前一段时间有的浏览器实现过,在组件外部,style 上加一个 scoped 属性,这是正确的方向。为什么要这么干呢,所谓组件,引入老本越小越好,在无约定的状况下都能引入,不造成问题,那是最佳的后果。

如果你一个组件的款式不是部分的,很可能就跟主界面的抵触了,就算你不跟主界面的抵触,怎么保障不跟主界面中蕴含的其余组件的款式抵触?靠命名约定是不事实的,看久远一些,等你的零碎够大,这就是大问题了。

跟主文档的通信

一个自定义组件,该当可能跟主文档进行通信,这个过程包含两个方向,别离能够有多种不同的形式。

从外向外

除了事件,真没有什么好方法能够做这个方向的通信,但事件也能够有两种定义形式,一种是相似 onclick 那种,主文档该当可能在它下面间接增加对应的事件监听函数,就像对原生元素那样,每个事件都能独自应用。另一种是像 postMessage 那样,只提供一个通道,具体怎么解决,本人去定义音讯格局和解决形式。

这两种实现形式都可行,后者比拟偷懒,但也够用了,前者也没有显著劣势。

从外向内

这个也能够有两种形式,一种是组件对外裸露属性或者办法,让主文档调用,一种是内部也通过 postMessage 往里传。前者用起来会比拟不便,后者也能对付用用。

所以,如果特地偷懒,这个组件就变得像一个 iframe 那样,跟内部根本都通过 postMessage 交互。

JavaScript

写到这里我是很纠结的,因为终于来到争议最大的中央了。依照很多人的思路,我这里应该也写隔离成部分作用域的 JavaScript 才对,但真不行,咱们能够先假如组件外部的所有 JavaScript 都跑在部分作用域,它不能拜访主文档中的对象。

我这里解释一下之前那个坑,为什么端到端组件是有缺点的。

先解释什么叫端到端组件。比如说,我有这么一个组件,它封装了对后端某接口的调用,还有本身的一些展现解决,跟外界通过事件通信。它整个是不须要依赖他人的,初始加载数据都是本人外部做,他人要用它也很简略,间接拿来放在页面里就能够了。

照理说,这货色该当十分好才对,应用起来这么不便,到底哪里不对?我来举个场景。

在页面上同时存在这个组件的多个实例,每个组件都去加载了初始数据,假如它们是不带参数的,每个组件加载的数据都一样,这里是不是就有节约的申请了?有人可能感觉一点点节约不算问题,那么持续。

假如这个组件就是一个很一般的下拉列表,用于选取人员的职业,初始可能有医生,老师,警察等等,我把这个组件间接放在界面上,它一呈现,就本人去加载了所需的列表信息并且展现了。有另外一个配置界面,用于配置这些职业信息,这时候我在外面增加了一个护士,并且提交了。假如为了数据一致性,咱们把这个变更推回到页面,麻烦就呈现了。

界面只有一个职业下拉列表的时候可能还好办,有多个的时候,这个更新的策略就有问题了。

如果在组件的外部做这个推送的对接,就会呈现要推送多份统一的数据给组件的不同实例的问题。如果把这个放在里面,那咱们也有两种形式:

订阅公布模式,组件订阅某个数据源,数据源跟服务端对接,当数据变更的时候,发给每个订阅者
观察者模式,组件察看某个数据源,当数据变更的时候,去取回来
这两种很相似,不论哪种,都面临一个问题:

数据源放在哪?

很显著不能放在组件外部了,只能放在某个“全局”的中央,但方才咱们假如的是,组件外部的 JavaScript 代码不能拜访外界的对象,所以……

但要是让它能拜访,组件的隔离机制等于白搭。最好的形式,兴许是两种都反对,默认是部分作用域,另外专门有一个作用域放给 JS 框架之类的货色用,但浏览器实现的难度可能就大了不少。

可能有人会说,你怎么把问题搞这么简单,用这么 BT 的场景来给咱们美妙的将来出难题。我感觉问题总是要面对的,能在做进去之前就面对问题,后果应该会好一些。

我留神察看了很多敌人对 Web Components 的态度,大部分都是齐全叫好,但其中有一部分,次要是搞前端 MV* 的同学对它的态度很激进,次要起因应该是我说的这几点。因为这个群体次要都在做单页型的利用,这个外面会遇到的问题是跟传统前端不同的。

那么,比方 Angular,或者 React,它们跟 Web Components 的合作点在哪里呢?我集体感觉是把引擎保留下来,下层局部逐渐跟 Web Components 交融,所以它们不是谁吃掉谁的问题,而是怎么去交融。最终就是在前端有两层,一层是数据和业务逻辑层,一层是偏 UI 的,在那个层外面,能够存在像 Web Components 那样的垂直切分,这样会很合适。

最初说说本人对 Polymer 的意见,我的认识没有 @司徒正美 那么粗犷,但我是认同他的观点的,因为 Polymer 的基本理念就是在做端到端组件,它会面临很多的挑战。尽管它是一个组件化框架,组件化最合适于解决大规模合作问题,然而如果是以走向大型单页利用这条路来看,它比 Angular 和 React 离指标的间隔还远很多。

退出移动版