Everything are HTML components – 小谈 HTML 组件化与可视化的编程

从 AngularJS 1.0.7 开始,我们就拿它做项目。刚开始使用了 angular-seed 创建页面,结合 ng-controller 做了好多事。同时我们也用了 directive 做了些公用组件。

我们组在 retrospection 时,慢慢地有了对比。 ng-controller 做出来的东西都是大块大块的,彼此连动,组织很不清晰。而 directive 却是自成体系,透过组件嵌套和通信控制,我们可以做出任意的页面——而且还保证各个组件自己是独立、可测试和不受其他 Scope / EventListener 影响的。甚至,我们还总结出了一个原则:

Everything should be directives.


 

具体呢,举个栗子。

<bw-appnode appnode-restful>
    <bw-table>
        <bw-start-appnode></bw-start-appnode>
        <bw-list></bw-list>
    </bw-table>
    <bw-search>

    </bw-search>
</bw-appnode>

这个简单的 html 结构,是我们吃了很多亏之后总结出来了 (高仿样本)

你能看出什么门道?1. 组件化 2. 可视化 HTML 3. 可测试单元

  1. 组件化

无需多言,bw-appnode 是一个组件,bw-table 与 bw-search 也是。这些组件彼此嵌套,也彼此独立,使用协议(Scope或者Service)通信。这样的好处非常明显。

比如我们现在还没有做好 bw-table ,服务器 Restful API 也还没有完成,但我们却可以独立开始开发 bw-search 。bw-search 自带独立的 UI 与复杂逻辑,其将对 bw-appnode 的假数据进行过滤,而 bw-table 则是渲染层而已,任何 bw-appnode 上面的变化,都会自动的 reactive 到 bw-table 里 (就是 Angular Scope 了)。

  1. 可视化/具象化

缘由一:我们习惯于把很多的功能使用 JavaScript 事件绑定的方式实现。慢慢地,点击了一个按钮会发生什么,我们只能猜,或者辛辛苦苦的去跟踪源代码。而到底一个 Dom 节点上的  JavaScript 事件监听器有几个,我们也很难察觉——每个工程师都可能在 JS 文件的任何地方给某个节点添加上不可预期、天花乱醉的监听事件。

缘由二:还记得你初学 HTML5 时,相关牛文告诉你它为什么要增加那么多新标签么?比如 section/article/video等。因为这样,HTML 页面与功能就可以更加“可视化”和“组件化”了。

第一,“可视化”。“可视化”就是一看代码就知道这个是干什么的(Chrome Developer Tools Inspect、Google Crawler 精准搜索)。

这里讲的 HTML5 标签“可视化”,严格意义上讲,应该叫“语义化”,不过我觉得“可视化”这个词更 Cool。前端么,几个人会在乎语义不语义,HTML结构读了通顺不通顺,分析起来合理不合理?好看和干脆(Crispy, Handy, Intuitive)很重要——主要气质;两者,语义化就是“看了就明白”的意思,所以干嘛不用“可视化”这词替代呢?;

第二,“组件化”。就是一个标签就渲染出自己的一套独立行为与 view (Web Component / Shadow-Dom的题中之义)。

我们很多人都曾近乎引颈渴望:如果从 HTML 上面就可以看出这一段 HTML 片断/页面块 是干什么的,是何等的奇妙和方便啊—— 不好意思,这忽然想到了另外一个术语,“描述性编程”,在此也可见一斑,门当户对。

在我们的例子中,顶级节点有一个新的 directive,叫

appnode-restful

顾名思义,这个就是用来拿服务端数据的。在 Server 还没有做完时,在它里面直接拿的是静态的数据 (使用Promise resolve),拿到数据之后,直接将之存为 bw-appnode 上的 一个 Scope 属性。

而更有意思的是,由于这个节点是独立的另外一个 directive,我们可以在上面考虑更多复杂的逻辑,比如 ajax 数据缓存处理等。我们坦然地对这个组件说,“反正你就干这事,要怎么干,你慢慢想;想做多精,就做多精;要做多细,就做多细;一辈子你一个人只做一件事,就不相信你做不好”。另外,这个节点还可以用在其他地方——想想,要 appnode 数据的,可不止于这个 bw-appnode 页面。不过呢,我这里不是说, Component 的主要目的是为了做重用——这个明显不是本文要谈的。

  1. 可测试单元和 jsDoc

这里讲的不是单元测试,而是测试单元。显而易见,上面的任何一个 Component 都可以独立拿出来做测试。我们只需要引入相关的 directive 的 js 文件与 view html 文件,就可以在另外一个独立的页面测试空间,给这个 directive 做全面细化的 UI 测试 (Protractor驱动)。测试用例、数据环境,想加就加,想改就改。完全不用担心耦合问题。

而既然是组件化, jsDoc 也好写多了。一个 directive,一个 module 定义,便于观看,颜值高,清晰度强。


当我们把这个经验(Everything should be directives.)总结出来之后不久几个月,我们就发现 Angular2 / React 的团队其实早已经意识到这个原则的重要性,他们默默地调整了一切。

在 React / Angular2 看来,一切都是组件。在写一个新的 SPA 时,你首先要做的,就是把你要做的页面划分成不同的嵌套的组件。

借用别人的图举个栗子。

component_example

一目了然,不言而喻。不过多说一句,划出组件之后,你还要决定是使用 Scope 通信,还是使用 Publish/Subscribe 通信;另外是串页面(容器组件)的 Router 抽象层,是这种组件化编程不得不提到的——一句话:用现成的 Router 都太弱,你应该用自己发明的 Router。这里不再多谈,喜欢了解,就找小方。

下面有几个好资源与童鞋们共享。

Facebook React: Step 1: break the UI into a component hierarchy

AngularJS: Component Based Thinking in AngularJS

AngularJS: Refactoring Angular Apps to Component Style

 

最后再来一个给力的,别人做的东西。

component_refac

让 Component 革命中关村和硅谷的前端吧。

后记:

我当前使用的是 Microsoft KnockoutJS,然而,从 Angular/React 得来的经验,已经彻底的革命了我们的 KnockoutJS 云应用。

Component,成功人士的不二之选。

小方,中关村软件园,Mar. 18, 2016。转载请标明出处。

IMG_1336