前端技术的开放和封闭

940 阅读11分钟
原文链接: github.com

Unix 哲学”的根本原则:“简单原则”——尽量用简单的方法解决问题。

阿里系的 KISSY 框架的取名就源自 Unix 哲学中的这一条,Keep it Simple & Stupid。这也是我们在 PC 和无线时代不断提炼前端开发框架、研发模式、共享代码和开源社区建设的基本理念,即将规范做轻、研发环境做实、抽离代码、贡献代码的体验打磨到最佳。这种对一致性、规范性、简单性的美学追求,一直影响所有前端开发同学至今,也让前端的开源世界充满活力。

我们(阿里航旅前端团队)也在前端研发模式上有了一定沉淀,但随着业务和开源社区的平行发展,我越发感觉到自己和开源社区之间的壁垒越来越重。以至于影响到团队成员的(技术)意识形态,我们必须做出选择,追求拥抱开源,还是更加务实封闭。

1. 航旅的前端研发模式

架构图大图

  1. 底层是项目源码仓库,项目代码携带开发环境,项目目录结构固定,开发环境通过 tnpm (阿里内部私有 NPM 仓库)一键安装
  2. 源码(src)需要被构建到可发布代码(dist),构建过程包括模板解析、TMS (阿里内部的运营数据管理系统)数据异步化(生产环境只支持静态 HTML)、HTML 合并和压缩,JS 和 CSS 的合并和压缩
  3. 项目仓库中所需的组件是通过 bower 安装进来的,组件代码存放在内部的 gitlab 仓库上
  4. 最后的打包发布也是通过 Node 工具完成的,这些工具都是一键安装的
  5. 项目和组件的初始模板通过基于 Yeoman 的生成器来生成,基本上也是一键生成的
  6. JavaScript 模块规范遵循 KISSY 模块规范(KMD)

整个研发模式基于以上六个要点搭建,看上去也很完整,能够很好的支撑研发模式所有环节,而且对面向多端容器的适配也一并在 Node 工具层面完成,保持源码(src)的“轻便”。

2. Why Bower?

KISSY 组件库提供了组件研发的体系,为什么不直接依赖这个体系建设航旅的组件库?原因有二:

  1. 这些组件只能在线使用,不能离线(或者说离线成本很高)
  2. 线上组件出了问题,bugfix 升级成本很高(因为你要 at 组件作者去修复)

而在业务高速增长的阿里内,稳定的组件可能仅限于一些不痛不痒的轮播日历之类。大量组件配合业务的迭代需要不断升级,在缺少 UI 测试用例的前提下,集中式管理组件代码极大拖慢了整个节奏,干脆把组件 fork 到我项目里吧。bower 作为对格式无约定的包管理器,非常适合安装、升级外部模块。

3. 问题出在哪里?

刚刚我用“轻便”来描述我们的项目源码环境,实际上每个项目迭代几个版本之后,就再也不轻便了。源码(src)开始有冗余,特别是 bower 安装进来的组件代码也因频繁升级导致版本交错愈加剧烈,组件被 fork 出来之后基本上就单独衍生版本了,再也难合并到主干去。一方面有研发任务的压力,另一方面,绝大多数组件的构成原本就不干净,要么文档不全要么注释稀少、再要不连 Demo 也没有,merge Request 难度很高。

这就是我们实实在在发生的事情,一个团队中,到头来始终只有几个编程高手在抽组件、维护核心的公用代码,大部分人大都只会 fork。这就背离了我们的初衷,代码流动性越来越差。这也是 KISSY 开始走向没落的大背景,组件库一直缺少血液供给,社区再难像两年前搞组件大赛那样火爆。团队小圈子开始一个个的形成,轮子越来越多。

进一步讲,阿里业务的发展实在太快,无线化对研发模式带来极大的冲击,从天猫、支付宝、淘宝等前端团队变化来看,从作为支撑角色的前端,会逐渐强耦合业务,就又加剧了前端技术的多元化和去中心化的大趋势。所以我们经常开玩笑,百度的前端业务得多简单,一个 FIS 就可以支撑百度绝大多数前端业务了。

所以,问题很明显:

  1. 业务之间壁垒加剧,阻碍优秀代码的传播
  2. PC 时代大教堂模式的前端生态建设,在无线端早已水土不服
  3. 阿里成了围城,大量业务强相关的私有技术崛起,和开源社区渐行渐远

4. 那么,航旅解了这个问题了吗?

当然没有。好在我们在最初开始争论的时候就选择了站队,目前形成了自有的技术体系:

  • Bower:组件代码管理器
  • Yeoman:项目脚手架
  • Grunt:构建管理
  • Clam:开发环境工具集合(内网访问)
  • KMD:JavaScript 模块规范
  • Sass:Css 语言扩展
  • Juicer:HTML 模板引擎
  • KISSY mini:JavaScript 基础库
  • 于此同时,天猫(@三七)和我们(的 PC 产品)在坚守 KISSY 1.4 体系(MUI),淘宝原核心业务(@圆心)开始 KISSY 5 的探索,同时面向无线产品开始研发一个将 Zepto 私有化的项目 Kimi(内网访问)。支付宝(@玉伯)坚持走 Seajs 生态路线。而无线淘宝(@寒泉)则选择“不站队”。

    这么多体系,脑袋要炸了。

    本质上讲,业务的高速发展加剧了这种“失控”,很多方案有着现实意义。比如 KISSY Mini 很好的承接了航旅的 PC 业务转无线。而 KISSY 1.4 在目前全站 PC 业务中支撑良好。Kimi 则在最快时间内提供了低学习成本的一个无线方案。因此,从业务支撑方面讲,这些技术方案都是“成功”的。尽管这些分支各自活的很好,但在前端开源世界蓬勃发展的今天,私有技术和开源社区之间渐行渐远,不免让人感觉有些失落。

    KISSY 看上去不再 KISS,Sigh~

    KISS 的 Unix 哲学理念在充满创新和变化的业务面前,应当如何自处并渗透自有的优雅美感呢?

    5. WebPack & Browserify

    今天跟@勾股 兄长聊,有点小感慨,也很认同@勾股 的两个技术观点:

    1. 企业内开源无法持久,只有参与到开源社区中才能真正吮吸到技术创新的新鲜空气
    2. 用成熟的开源技术来搭建私有技术,帮助业务私有技术体系走完最后一公里

    保持开放的心态拥抱开源,用务实的精神快速“拿来落地”。在模块规范和代码生态建设上,最适合拿来的就是 WebPackBrowserify。这也是@寒泉 的无线淘宝团队打算要做的事情,有信仰总归是更可爱一些的吧。

    6. 完全被小瞧了的前端技术

    好吧,我个人表示 WebPack 和 Browserify 难堪大用。

    以 Browserify 为例,来对比下理想和现实的差距,Browserify 的优势:

    1. 用 npm 的大量开源资源
    2. 收敛的代码仓库(共有仓库 npm)
    3. CommonJS 规范 + Npm Package 规范

    Cool!~

    Browserify 的不足:

    1. 工程项目里的前端页面必须对 Seed(KISSY Seed 或者 jQuery) 有依赖,这种依赖不能在构建的时候复制多份(特别是在 H5 离线包 中,为了控制离线包体积,必须要去重),npm 中的模块物理上都是复制存储的
    2. CSS 样式的载入时机太慢(为了做到离线页面秒出 UI。样式必须比 JS 提前载入,在 DOM 构建时就生效),CSS 问题在 WebPack 和 Browserify 中无解决方案
    3. 项目代码下辖的模块代码存储过于分散,代码分三个部分
      1. 项目本身的代码,就在 gitlab 项目仓库中
      2. npm 安装进来的代码,在项目的 node_modules 目录中
      3. 项目组件代码,通过 bower 安装进来,存放在另外的 gitlab 仓库中
    4. 前端组件真真不同于 Npm 包,需要在此上构建一个前端 widget 包规范的超集,需要考虑这几个问题
      1. 为了便于 widget 代码携带 Demo,需要一个本地环境,将源码(src)构建出目标代码(dist)以便运行起来,所以包内必须携带 grunt 或者 gulp 之类的构建脚本
      2. widget 所携带的样式引用,依然缺少优雅的方案提
      3. 业务私有的 widget 代码事实上都在 gitlab 里,需要通过 bower 来安装,概念上就一定程度混淆 npm 安装和 bowser 安装,于是难对组件进行干净的归类,比如我要修复一个 npm 组件的 bug,修复之后应当 fork 在 gitlab 里用 bower 管理起来还是重新提交给 npm?
      4. 如果想收敛组件仓库,则 gitlab 里开发的公用代码必须发布到 npm 上,多此一举,不然,怎么收敛仓库?
    5. 物理代码不收敛
      1. 物理代码分散:npm 安装的代码放在项目的 node_modules 中,这个目录一般都在.gitignore 里,不会提交到gitlab上
      2. 配置信息分散:代码依赖需要在两个地方定义,package.json 和 源码中(应用包时往往需要 require 一下),模块依赖关系定义没法干净的在一个地方指定,实在不爽。

    如此看来,Npm 天生不适合存放前端模块,一个前端代码包含的技术碎片过于分散,以至于几乎不可能用一种优雅的方式解决。举个例子,前端是强依赖 Loader 规范的,而由于场景不同,Loader 可以像 YUI Loader一样复杂,也可以像requirejs一样简单。而作为组件代码则必须明确“知道”依赖其中哪一种 Loader。Loader 作为前端模块载入的基础装置,是需要预载入 HTML 中的。所以,这个“知道”就是一种依赖。一旦有依赖,就很难将解耦拆的干净。所谓“约定”,不过是强行隐藏一些存在的配置项,很早之前就有过类似的讨论

    So,Unix 哲学在前端技术领域里的影响总是有限的,受浏览器运行机理的限制,前端项目研发复杂度是固定的,你只能封装其中的一部分,总有一些复杂度你无法封装,所以前端项目研发环境一定是为业务场景高度定制的,未来前端技术的私有化和壁垒天然会加剧。就如同开源和闭源一直争论不休一样。

    但这丝毫不妨碍我们拥抱开源,包括 bootstrap 在内的大量私有项目被开源出来,我们也可以享受前端技术高度“去中心化”和“碎片化”带来的红利。把社区中现有成熟的工具利用好,并严格律己的遵循开源协议,前端的开源也一定呈现多元共生的局面。这些所有成果,都极为有利于我们完成手头工作的“最后一公里”。

    7. 前端技术未来

    让人欣慰的是,ES6 让人看到了希望。

    至少我们不用再为 JavaScript 本身的 OO 去打各种补丁了。作为企业专职的前端工匠,面对现有技术选型时,也应当遵照标准的 ES6 规范去制定长远规划。Kissy Mini 接下来的版本中会更加拥抱 ES6。

    另一方面,在无线领域,H5 和 Native 的耦合会越来越重,前端代码的研发模式会一定程度和客户端开发整合,这也是我们航旅团队接下来将要突破的重点。前端技术边界向 Native 很多特性延伸,制定 H5 容器标准,目标是让 Hybrid 挑战 Native 的原生体验,PPK 似乎还在下意识的将两者对立,大量的事实已经证明,混合式研发会在未来无线战场中挑大梁。特别是 ReactNative 开始大量运用后,我们所掌握的已知的前端工程化管理的方法都将被打散重构,前端技术从真的变得此不再孤立。

    可以断定,前端技术学习门槛和身价会越来越高,会切页面已经不足以打动面试官了,FE 们开始消耗大量的计算机课上的那些基础知识,计算机网络、数据结构、数据库、C 语言、编译原理、信息安全这些基础课变得越来越重要,对知识面广度的要求远超从前。

    而在开源社区和闭源的私有技术中,这种开放与封闭的博弈会一直持续,着眼开源,立足闭源,保持开放、坚持务实,是我们应当具备的做事原则。

    至于那个在前端技术里一直水土不服的 KISS 哲学,别担心,让他一直水土不服下去好了。