webcomponents 笔记 之 配置管理

话说上周末看到一个吐槽腾讯“内部开源”的微博,后来我想了想,自己那么骚包的在项目还没做完之前,就在 CSSConf 上说我们将来要开源一个名叫 Zorro 的库。结果好几个月过去了还是没有准备好,也就不敢再笑话别人了……

我觉得把东西开源出来之前,有几件事要准备好,不然除了自己刷存在感之外,真的没意义。比如:

  1. 是否有了 (或阐述清楚了) 明确的目标和方向,不然找不到合适的合作者和贡献者
  2. 是否有了 (或阐述清楚了) 明确的设计哲学和开发原则,不然大家无法形成合力,项目很容易陷入混乱
  3. 是否有了最小的可工作版本,不然雪球滚不起来
  4. 是否有了充分的文档、demo和测试用例,让大家更直观的了解项目,利用项目,也对项目的质量更有信心

印象中我见到的优秀的开源项目,基本都在被大家广泛认识之前,都已经把这些事情打理好了——这也是我一直推崇的。
好吧很惭愧,这几点我还都没有做到……

不过在这之前,我愿意在此分享一些自己开发中的心得,跟大家一起探讨相关的话题。

-- 以上是一些比较啰嗦的铺陈 --

组件分解的方式及其衍变

在开发大型应用的时候,难免要用到一些组件化的分解方式。比如:把一个相册浏览界面分解成:“相册列表”和“大图预览”两个区域,“相册列表”又由一个个“相册缩略图”组成,每个“相册缩略图”包含了一个“小图片”以及“预览按钮”、“删除按钮”、“排序按钮”等操作按钮……

而如何管理和划分组件逐渐变成了前端工程里的一门学问。


components_1.png

最简单的分解方式是树形分解,自上而下。比如刚才的那个相册浏览界面的例子。

components_2.png

同时,我们会发现,树形的最末端往往存在着有共性的组件,比如按钮、文本框之类的组件,它们无处不在。这时,就有了所谓的“基础组件”和“业务组件”之分。“基础组件”是共享的,树形结构中的任何一个结点(“业务组件”)都可以直接使用这些“基础组件”。

components_3.png

如果程序的结构再复杂,那么就在“业务”和“基础”之间分更多的层,每一层有自己明确的职能范围,同时,较高层的组件可以自由调用较低层的组件。

components_4.png

组件的配置信息

配置信息大多是在组件在兼顾通用性抽象和特殊性业务时出现的。好的配置设计可以避免大量重复的组件设计和实现。

较简单的配置信息通常都是组件本身的一些属性 (properties) 或特性 (attributes),在 webcomponents (polymer) 的场景下,就是:

<polymer-element name="x-person" attributes="name, age, gender, avatar, ...">  
  ...  
</polymer-element>

进一步的,有时候我们需要把上层组件的配置信息带到更下层的组件:

<polymer-element name="x-person-avatar" attributes="avatar">  
  ...  
</polymer-element>

<polymer-element name="x-person" attributes="name, age, gender, avatar, ...">
  <template>
    ...
    <x-person-avatar avatar=""></x-person-avatar>
    ...
  </template>
  ...
</polymer-element>

如果一个程序的组件层次太深,则可能出现下面两个问题:

  1. 会有一些很小的底层组件的配置信息,需要从最外层配置一层一层的“透传”到最底层,每一层组件上都要定义一个配置字段
  2. 最外层组件的配置字段往往会被各种七七八八的小组件配置占领

于是顺着这个思路,我们发现,有一种不太起眼的办法在很早的时候就被忽略掉了——这就是全局配置信息。

什么时候全局配置更合适

通常情况下,和整体应用所处环境相关,同时和上层组件无关的配置适合做全局配置

举一个实践中的例子:在开发图片上传组件的时候,我们发现,图片上传组件往往需要一个上传图片的服务器地址,这个地址在固定的用户、固定的应用之中,通常是一致的,只是在不同的应用中,图片可能需要长传到不同的服务器地址。

这种情况下,通过组件的配置字段一层一层向下传递找到图片上传组件显然是很繁琐的。于是我们想了个简单的办法:

  1. 在页面最外层 (<body>里面) 写入一些 <input type="hidden"> 的标签,注明服务器相关配置
  2. 在组件中写一段脚本,查询所有的这样的标签,并且把键值对记录下来——这其中就包括应用自身的图片上传的服务器地址

2015-03-31 追加说明:感谢民工哥 @民工精髓微博上的指点。也是因为很多服务器的配置是后端同学决定的,所以我们创造了这种对传统后端配置友好的 <input type="hidden"> 写法。这样的写法对于后端的友好之处我就不一一列举了。如果是纯前端程序,配置来自前端,确实直接定义全局变量就好,不必这么麻烦。

于是整个程序变成了:

<polymer-element ...>
  <script>
    Polymer({
      ready: function () {
        var inputList = document.querySelectorAll('input[type="hidden"]');
        ...
      }
    });
  </script>
</polymer-element>
...
<body>
  <input type="hidden" name="uploadUrl" value="/pathToUpload.do">
  ...
</body>

这样不管图片上传组件用在哪里,其它组件都不会因此而产生负担,同时这些配置的管理也变得很清晰——这甚至和前端工程师平时和后端工程师协作的流程是完全吻合的:前端负责写好 components,后端负责把 <input type="hidden"> 配置好。

如何做好配置管理

我们顺着上面的思路继续想:如果程序中很多组件都有类似的配置需求,那么:

  1. <input type="hidden"> 不能被滥用
  2. 组件内部的读取配置信息的方式可以抽象出来

于是,就有了 Zorro 现在的一个组件:<z-config>。它的大致功能如下:

  1. 首先它可以快速的提取 <body> 最外层的 <input type="hidden"> 配置信息——这显然是和后端工程师约定过的
  2. 其次,它可以被任何组件作为“基础组件”引用
  3. 它提供方便的接口,供任何上层组件访问配置信息

自从有了这个组件,很多配置相关的问题在 webcomponents 中都显得很轻松了。毫不夸张的说前端工程师和后端工程师的关系也因此不像之前那么紧张了。

管理更多的信息

后来,我们在配置管理的基础上,加入了更多的实用信息。比如:

  • 可以读取 location.href 中的字段信息
  • 可以像 localStorage 一样提供一块全局共享的键值对空间,方便组件之间共享状态信息
  • 可以在 <z-config> 在被创建时,根据不同的场景设置一些初始化数据等

以上这些,构成了今天的 <z-config> 组件

实现机制并不复杂,这里就暂不贴代码出来了,但终归是会开源的,我保证。

总结

说回配置管理本身,它实际上是一种信息在程序和组件之间的流通方式。我们基于对 webcomponents 自身特点和形态的理解,加上业务实践中的一些体会,设计了这样的一个标签。希望给大家一些启发,同时也欢迎大家的讨论和观点。