Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

从HTML Components的衰落看Web Components的危机 #3

Open
xufei opened this issue Sep 30, 2014 · 36 comments
Open

从HTML Components的衰落看Web Components的危机 #3

xufei opened this issue Sep 30, 2014 · 36 comments
Labels

Comments

@xufei
Copy link
Owner

xufei commented Sep 30, 2014

从HTML Components的衰落看Web Components的危机

搞前端时间比较长的同学都会知道一个东西,那就是HTC(HTML Components),这个东西名字很现在流行的Web Components很像,但却是不同的两个东西,它们的思路有很多相似点,但是前者已是昨日黄花,后者方兴未艾,是什么造成了它们的这种差距呢?

HTML Components的一些特性

因为主流浏览器里面只有IE支持过HTC,所以很多人潜意识都认为它不标准,但其实它也是有标准文档的,而且到现在还有链接,注意它的时间!

http://www.w3.org/TR/NOTE-HTMLComponents

我们来看看它主要能做什么呢?

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

行为

行为(Behavior)是在IE5中引入的一个概念,主要是为了做文档结构和行为的分离,把行为通过类似样式的方式隔离出去,详细介绍在这里可以看:

http://msdn.microsoft.com/en-us/library/ms531079(v=vs.85).aspx

行为里可以引入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/22426434/answer/21433867

所以,大家都想办法去提供自己的扩充元素的方式,现在我们是知道典型的有angularjs,polymer,但很早的时候也不是没有啊:

http://msdn.microsoft.com/en-us/library/ms531076(v=vs.85).aspx

看,这就是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离目标的距离还远很多。

@xufei xufei added the blog label Sep 30, 2014
@markyun
Copy link

markyun commented Sep 30, 2014

学习,国庆快乐。

@xufei
Copy link
Owner Author

xufei commented Sep 30, 2014

如果是我来实现Web Components,很可能会以iframe为蓝本,删除作为独立页面所拥有的cookie,localStorage等东西,并且把跟主文档的关联适当删掉一些,然后,js部分真不知怎么搞了,那个公共的部分不知放哪好……

@LingyuCoder
Copy link

个人感觉Angular这样的框架主要是着重点在于MVVM的开发方式,而WC则着重于对html本身标签提供自定义的扩展方案,WC应当融入到MVVM的V层面,而VM和M层面的管理以及VM和V的双向绑定依旧交给Angular去做

@tiye
Copy link

tiye commented Sep 30, 2014

我关于 React 跟 Web Compoennts 的对比主要基于 Pete Hunt 的说明 https://t.co/mY04yPZABR
Web Components 的做法问题在于依然用 DOM 操作为维护状态, React 的方式更好,
而数据绑定只是让操作的流程更方便, 并不是让调试变得轻松了.

虽然我们通常思考组件化都认为界面拆成一个个 Module, Module 之间通过事件相互通信
但是在 React 当中, 做法其实是写一个 state 的值, 通信在界面重绘过程中直接完成了
这种方式将 Message 隐藏在了流程内部, 因此程序员编写代码不再需要写消息状态维护的代码
Web Components 里沿用 DOM 的属性和事件, 与之相比差异非常大

以及 React 所属的函数式响应式编程一派的 Elm 对于界面的处理:
http://elm-lang.org/Examples.elm#intermediate
Elm 中有模块重用的概念, 可是编程的思路更是不一样
我没能掌握具体不好说, 但这些让我对 Web Components 那一套很有怀疑态度

说 Elm 有点跑题, 不过拿过来对比 HTML 这边, 看着确实这边的抽象有些问题..
http://debug.elm-lang.org/

Our debuggers are limited by our programming languages. In languages like C++, Java, and JavaScript, we step through stack traces because that is the most concise way to express the meaning of an imperative program. We step forward, one command at a time, mutating variables, writing to files, sending requests. These debuggers typically only go forward because each step may destroy past state. In short, low-level languages lead to low-level debuggers.

@yyx990803
Copy link

  1. Web Components 中 Custom Elements 规范里命名必须带一个短横 -,这基本上是起到强制命名空间的意义。链接

  2. Shadow DOM 内的样式默认就是局部作用域的。链接

  3. 通讯

    • 从内向外:就是沿用现有的 DOM 事件 API。
    • 从外向内:Polymer 的做法是通过暴露公开的属性 (attributes),然后用 MutationObserver 来观察属性的变化,并且保持对应的 property 和 attribute 之间的同步。用法上是 <input type="xxx" value="xxx"> 这样的感觉。至于动态的数据,Web Components 规范本身其实不涉及数据的绑定和传递,具体实现还是由框架来执行。
  4. 数据源问题,我觉得好像没什么特别难的,在一个闭包里定义共享的部分和构造函数即可。举个简单的例子:

    (function () {
      var sharedStore = new Store() // 共享的数据推送/抓取服务,可以包含缓存机制
      var customElementProto = Object.create(HTMLElement.prototype)
      customElementProto.createdCallback = function () {
        this.data = sharedStore.fetch() // 调用共享的数据
      }
      document.registerElement('my-element', {
        prototype: customElementProto
      })
    })()
  5. 需要明确一点,那就是 Web Components 作为一套规范并不试图取代框架的职责,而只是提供一套和现有 DOM API 风格一致的封装机制。每个 component 内部可以采用不同的实现,但是不管你内部用什么框架,对外暴露的都是一样的 API。所以你可以在框架 A 写的应用里很轻松地使用内部由框架 B 实现的组件。这样组件复用的一大难题:框架不兼容的问题就被抹平了。个人认为,这才是 Web Components 的核心意义所在。对于框架作者来说,并不需要针对 Web Component 做出很大的调整,只需要提供一个注册用自身实现的 Web Component 的便利函数就足够了。

  6. Polymer 和 Web Components 不是一回事,Web Components 是一套规范,Polymer 是一个框架。Polymer 本身也分三部分:

    1. 抹平浏览器差异,实现 Web Components 规范的 polyfill
    2. 核心框架。其实就是基于 DOM 对象本身的 MVVM,理念是 "DOM 就是 ViewModel"。
    3. 在核心框架基础上附加的组件系统。

    在核心框架的层面上,Polymer 和 Angular、React 虽然各自实现有很大的差异,但从功能的角度来看其实是差不多的。

@yyx990803
Copy link

删掉 shadow dom 代码的事情,是因为 google fork Blink 之后不再维护那部分了,Apple 的人又不想就着烂摊子继续写,所以就删了... 其实他们是想删了以后从头自己写,倒还真不是故意拆台。

@liyinkan
Copy link

liyinkan commented Oct 1, 2014

爪机码字还痛苦。弄到后来,除了布局,都朝着 .net 里客户端组件那样的封装发展着……

@jumbo
Copy link

jumbo commented Oct 1, 2014

@myst729 最新的测试版safari已经加了对shadow dom支持,开发工具已经可以展开input 内部了

@hax
Copy link

hax commented Oct 3, 2014

@xufei 公共部分的疑问其实已经有解决方案,就是 SharedWorker。
[更新] 又想了下,这个场景还是 @yyx990803 说的在createCallback中注入更适合。或者就是通过暴露自定义dom property来做(允许主文档指定不同的数据源)。还有一种可能的方式是用通过es6 module导入公共模块(前提是内部的loader是和主文档相同或衍生的)。

@hax
Copy link

hax commented Oct 3, 2014

namespace 那个我赞同。现在草案用短横线的方式是过犹不及的愚蠢方式。

@hax
Copy link

hax commented Oct 3, 2014

我个人对wc持保留意见,倒不是在这些方面,而是自定义标签太容易被滥用。看看polymer的例子,大部分都是实现ui,完全违背语义化。更不要说一个custom element内部的实现(当然作为内部实现,dirty通常是无所谓的)。当初 BECSS 没有进一步标准化,至少也有因此遭到反对的部分原因。

@cnberg
Copy link

cnberg commented Oct 10, 2014

求转发至 div.io ~

@cutewalker
Copy link

应该是“明日黄花” 哈

@catjam
Copy link

catjam commented Nov 14, 2014

Polymer 群: 192188831

@myadomin
Copy link

我X 发现issue写blog太爽了

@gideonstele
Copy link

发现了一个问题:

在我眼里,html/css/js 是用作排版布局的,是类似于 .doc 的东西;
在你眼里,html/css/js 是用作实现人机交互,甚至是实现业务本身的,是类似于 .exe 的东西...

可怜的html啊,能同时满足我们俩嘛...

@xufei
Copy link
Owner Author

xufei commented Jan 8, 2015

@ShiJinYu 这也就是我之前说,两帮不同目的的人,抢同一个平台标准的话语权,不仅仅在Web Components这块,还包括ES的语法,做应用的想一步到位搞成C#那样,做页面的只愿接受ES3-ES5这种小幅更新。

所以我一直对标准推进速度挺悲观的,觉得这两派人要的不是同样的东西。上次 @yyx990803 也说了,Web的应用化是不可逆的潮流了,从Win32,Swing,WinForm,Flex这条路线上来的我当然乐于看到这条路线,但反对者肯定越来越多……

@gideonstele
Copy link

我有一个奇葩的想法:把浏览器 javascript 各个模块做成类似java的package的机制,webcomponent、ES6标准全部变成一个js package集成在浏览器js引擎里,但默认不加载,代码用到时再import。

其实就是担心js变“重”,“doc”类的页面一般都要求“快”,“exe”相对这方面要求低一些。说不定随着网络和硬件的进化,这些担心都是多余的……

还是忍不住吐槽啊,内些为了做web app而产生的奇葩DOM结构、CSS Tricks、JS Tricks实在是不忍直视啊,会被逼疯的……

@stanleyxu2005
Copy link

很多时候一些技术落寞了,是受时代的局限,不能说那些技术本身不好。说不定html component的精髓会在不久的将来以另外一个形式回来。

@sapjax
Copy link

sapjax commented Jun 5, 2015

很明显不能放在组件内部了,只能放在某个“全局”的地方,但刚才我们假设的是,组件内部的JavaScript代码不能访问外界的对象,所以……
但要是让它能访问,组件的隔离机制等于白搭。最好的方式,也许是两种都支持,默认是局部作用域,另外专门有一个作用域放给JS框架之类的东西用,但浏览器实现的难度可能就大了不少。

组件的js本来就是全局的,你这假想一个敌人,然后来批判....

@steelli
Copy link

steelli commented Jun 8, 2015

还是比较看好的

@qiangspecial
Copy link

个人认为 Web Component 和 react, angular此类框架关注的问题本身就不一样
Web Component 更关注于组件化,组件之间互不干扰
react,angular关心的是M与V的一致性

@lizheming
Copy link

所以总而言之就是博主觉得 Web Component 太组件化了,和主文档的事件交流和数据交流都很麻烦,是这个意思么?

@stanleyxu2005
Copy link

@lizheming 经典桌面应用时代,mfc或者vcl都是mvc分离的。组件的独立性非常强。也没有见到事件交流和数据交流有多复杂。以前web的很多控件能力不强,现在这方面的劣势已经没有了。只要用桌面应用的思路能玩转,其他的领域想必是可以推广出去的。

@lizheming
Copy link

@stanleyxu2005 别误解我只是看了博主的文章之后有点晕乎想要总结下文章的中心槽点,知道说什么之后才好正确判断这事... 我也没说我同意与不同意哈。

@tsoukw
Copy link

tsoukw commented Dec 24, 2015

关于楼主提出的端到端组件第二个问题,即页面上的不同组件同步数据问题。
1.当前页面设计时,是如何看待这两个组件是否需要同步。
大部分时候是不会要求同步的。企业应用是多人协作,多人在不同地点操作同一数据。

可能系统管理员在维护职业列表,而开单的人用一个下拉列表框读取职业列表,这个时候就是两个完全不同的上下文,没有谁会要求这时候要同步数据,就算做得到,代价太大,发生的机率太小,也没必要。

如果删除了一个职业,用户选了提交会怎样,那是业务逻辑层管控的问题,它必然会检查某个职业是否还存在资料表中。

如果用户要选取管理员刚刚新增的那个职业(那一定是它刚才有联系管理员,要求他加的),这个时候最简单的方式(程式没有负担),用户关掉程式在打开。
或者程式准备一个刷新按钮,用户单击刷新一下(不过这个对于当前这个下拉框场景可能不实用)。
我们是将这个下拉框组件的每次下拉刷新属性设为true,这样用户再次单击一下下拉框,程式就会重新读取数据库,用户就可以选取到那个新职业了。

如果有人说用户修改了职业名称了,比如说警察修改成了护士,那用户明明看到的是警察,怎么提交后就变成了护士。这种情况确实奇葩,问题本质上是那位管理员的作法,一般会将警察修改成警察(Police),但很少会将警察变成护士这种业务上有问题的东西,因为这个table作为外键关联表,很多现在存在的数据的意义都会改变,所以还要具体场景具体分析(例如将id和名称同时带入,在服务端一起验证)

先写这些,起床了~~

@xufei
Copy link
Owner Author

xufei commented Dec 24, 2015

@tsoukw 传统的企业系统确实是不需要主动同步,但我们注意到,近几年的很多系统都偏向单页化,相对注重体验了。一个单页化的应用,最重要的是变化是几乎没有“刷新”操作,所以可以看到很多这方面的探索,比如有的产品不再直接使用HTTP做通信,而是把全部业务操作都封装成某种协议,经过WebSocket进行通信。

目前来看,对于较大型的产品,业务比较复杂,做这方面的事情难度会比较高,但是如果追求极致的用户体验,这个事情是值得去探索的。

比如刚才那个职业名称列表,为什么就不能是服务端进行一个“推送”?

@tsoukw
Copy link

tsoukw commented Dec 24, 2015

2.有的时候确实要同步
例如我们导航菜单组件里的菜单项有个star的图标,用户单击后就会将这个菜单项加成快捷方式。
另一个显示快捷方式的组件在导航菜单下的页面主要区域。
当然在导航组件中单击某个菜单项加入快捷方式后,快捷方式组件能够立刻响应,看到刚刚加的快捷方式已新增进来了。

这个时候涉及到的就是同步问题,首先找到导航菜单组件和快捷方式组件的共同父组件,由那个父组件(就是拼装两个组件)来实现同步。导航菜单组件在加入快捷方式成功后,发送事件给父组件,父组件调用快捷方式组件的刷新方法重新载入数据。
这是最简单的方式,避免了复杂的数据同步问题。

那还有一种是重复取数据导致的浪费问题,导航菜单组件实际上也是由多个子菜单组件组成,每个子菜单组件都要一份相同的【我的快捷方式数据】,以便在某个菜单项上显示star图标,表示这个菜单已加入快捷方式。像这样的子菜单组件,每一个都是动态载入,也不知道谁会先载入,谁会后载入,但是每个子菜单组件在自己内部都会去请求【我的快捷方式数据】,这样不仅浪费网络,也浪费内存,所以需要想办法来共用。

这种问题涉及到了组件的本质问题,到底要怎样封装一个组件?
端到端组件的本质又是什么?

UI组件本质上只包含两个部分:view,data
比如那个菜单组件的view就是一行行的div(菜单项)根据MENU数据(用于显示菜单名称在div里)以及我的快捷方式数据(用于某个div是否加上s-fav-menu的class)
这就够了,这就是业务组件
那我们请求MENU数据的逻辑和请求我的快捷方式数据的逻辑就不需要在那个组件里了吗?
如果不加入这部分逻辑,显然这个组件离封装差太远了,如果加上,又会造成请求逻辑和数据浪费的问题,两难。

其实请求逻辑是业务组件的外围,能够自己在组件内部就去完成所有数据的请求逻辑最好,但本质上它们是两个互相独立的概念。业务组件只要数据就可以完成组件渲染,数据如何来,从何处来是进一步封装的问题。默认情况下当然封装在一起,这也利于我们分而治之独立开发调试。但我们因为认识到它是外围概念,所以知道它和组件本质实际上可以分离。

在需要控制它的业务数据请求逻辑时,就可以控制(例如提供一个disableGetFavMenuData属性,如果为true,则自己不去请求),父组件停用子组件的请求后,自己去抓取相关的业务数据,并把它直接赋值给各个需要的子组件。

最近在网上看到楼主很多文章,和楼主一样,做这些事情好多年了,有空可以多多交流~~

@tsoukw
Copy link

tsoukw commented Dec 24, 2015

@xufei 没错,如果确实要同步,就只能通过这种推送作业。

像一些面向个人的产品,如todo-list这种,在一个终端改了数据,在另一个终端如果立刻显示出来,那带给用户的体验肯定是满满幸福感的。像支付宝付钱,网页显示二维码,手机扫后,网页就知道了,马上显示付款成功。这种感觉真是太棒了,赞一个支付宝团队。

@xufei
Copy link
Owner Author

xufei commented Dec 24, 2015

@tsoukw 是的,数据和组件层是需要分开设计,然后在整体方案上加以整合的。最近几年业界很多话题,有不少是跟这层东西相关的,大型应用最复杂的东西并不在上层组件的切分,而是数据和业务逻辑层的设计,以及他们与上层组件的通讯方式。

@tsoukw
Copy link

tsoukw commented Dec 24, 2015

@xufei 大型UI,不应该是横向划分成MV*,而是应该将切成多个子组件,子组件又划分成子组件,分而治之。每个子组件可以独立运行,又可以灵活地组装,这样才有机会保证大型UI的稳定和成功。

当到最低一层的原子组件时,会发现,所有的MV_都失去了意义,直接控制view和同步数据是一件简单又高效地做法,因为view和data都足够少,MV_要解决的问题都已不再是问题。所以研究组件可能比研究MV*更应该成为UI未来的方向

@yurychika
Copy link

@myst729 公司内部去dojo非常厉害 其实现在反过来看 dijit这一套东西出来得那么早 即使现在看他得一些理念还是相当厉害 就像你说的 SPA 即使用dijit也完全没问题

@stanleyxu2005
Copy link

@tsoukw 组件本身也是符合mvc的....
大型项目按照模块划分,我的理解是把单层次的mvc,演变成了多层次的mvc来管理。
打碎细分后的业务逻辑能重用的部分,就可以定义成组件了。
我们说的组件分两类:(1)原生通用的 (2)业务逻辑发展出来的。

@showzyl
Copy link

showzyl commented Feb 17, 2016

但有些事情我直到后来很久以后才想明白,基于业务的端到端组件虽然写起来很方便,却是有致命缺陷的。@xufei

致命缺陷是啥啊?

@Kpyu
Copy link

Kpyu commented Jul 6, 2016

大大文章开头有个成语用错了,是明日黄花😂

@pengcc
Copy link

pengcc commented Mar 29, 2017

看到评论里有提到style,最近看了一些css in js, https://vimeo.com/116209150 , https://speakerdeck.com/vjeux/react-css-in-js, 请教各位高手,对这样的技术什么态度?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests