“Unix 哲学”的根本原则:“简单原则”——尽量用简单的方法解决问题。
阿里系的 KISSY 框架的取名就源自 Unix 哲学中的这一条,Keep it Simple & Stupid。这也是我们在 PC 和无线时代不断提炼前端开发框架、研发模式、共享代码和开源社区建设的基本理念,即将规范做轻、研发环境做实、抽离代码、贡献代码的体验打磨到最佳。这种对一致性、规范性、简单性的美学追求,一直影响所有前端开发同学至今,也让前端的开源世界充满活力。
我们(阿里航旅前端团队)也在前端研发模式上有了一定沉淀,但随着业务和开源社区的平行发展,我越发感觉到自己和开源社区之间的壁垒越来越重。以至于影响到团队成员的(技术)意识形态,我们必须做出选择,追求拥抱开源,还是更加务实封闭。
1. 航旅的前端研发模式
架构图大图
- 底层是项目源码仓库,项目代码携带开发环境,项目目录结构固定,开发环境通过 tnpm (阿里内部私有 NPM 仓库)一键安装
- 源码(src)需要被构建到可发布代码(dist),构建过程包括模板解析、TMS (阿里内部的运营数据管理系统)数据异步化(生产环境只支持静态 HTML)、HTML 合并和压缩,JS 和 CSS 的合并和压缩
- 项目仓库中所需的组件是通过 bower 安装进来的,组件代码存放在内部的 gitlab 仓库上
- 最后的打包发布也是通过 Node 工具完成的,这些工具都是一键安装的
- 项目和组件的初始模板通过基于 Yeoman 的生成器来生成,基本上也是一键生成的
- JavaScript 模块规范遵循 KISSY 模块规范(KMD)。
整个研发模式基于以上六个要点搭建,看上去也很完整,能够很好的支撑研发模式所有环节,而且对面向多端容器的适配也一并在 Node 工具层面完成,保持源码(src)的“轻便”。
2. Why Bower?
KISSY 组件库提供了组件研发的体系,为什么不直接依赖这个体系建设航旅的组件库?原因有二:
- 这些组件只能在线使用,不能离线(或者说离线成本很高)
- 线上组件出了问题,bugfix 升级成本很高(因为你要 at 组件作者去修复)
而在业务高速增长的阿里内,稳定的组件可能仅限于一些不痛不痒的轮播和日历之类。大量组件配合业务的迭代需要不断升级,在缺少 UI 测试用例的前提下,集中式管理组件代码极大拖慢了整个节奏,干脆把组件 fork 到我项目里吧。bower 作为对格式无约定的包管理器,非常适合安装、升级外部模块。
3. 问题出在哪里?
刚刚我用“轻便”来描述我们的项目源码环境,实际上每个项目迭代几个版本之后,就再也不轻便了。源码(src)开始有冗余,特别是 bower 安装进来的组件代码也因频繁升级导致版本交错愈加剧烈,组件被 fork 出来之后基本上就单独衍生版本了,再也难合并到主干去。一方面有研发任务的压力,另一方面,绝大多数组件的构成原本就不干净,要么文档不全要么注释稀少、再要不连 Demo 也没有,merge Request 难度很高。
这就是我们实实在在发生的事情,一个团队中,到头来始终只有几个编程高手在抽组件、维护核心的公用代码,大部分人大都只会 fork。这就背离了我们的初衷,代码流动性越来越差。这也是 KISSY 开始走向没落的大背景,组件库一直缺少血液供给,社区再难像两年前搞组件大赛那样火爆。团队小圈子开始一个个的形成,轮子越来越多。
进一步讲,阿里业务的发展实在太快,无线化对研发模式带来极大的冲击,从天猫、支付宝、淘宝等前端团队变化来看,从作为支撑角色的前端,会逐渐强耦合业务,就又加剧了前端技术的多元化和去中心化的大趋势。所以我们经常开玩笑,百度的前端业务得多简单,一个 FIS 就可以支撑百度绝大多数前端业务了。
所以,问题很明显:
- 业务之间壁垒加剧,阻碍优秀代码的传播
- PC 时代大教堂模式的前端生态建设,在无线端早已水土不服
- 阿里成了围城,大量业务强相关的私有技术崛起,和开源社区渐行渐远
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
今天跟@勾股 兄长聊,有点小感慨,也很认同@勾股 的两个技术观点:
- 企业内开源无法持久,只有参与到开源社区中才能真正吮吸到技术创新的新鲜空气
- 用成熟的开源技术来搭建私有技术,帮助业务私有技术体系走完最后一公里
保持开放的心态拥抱开源,用务实的精神快速“拿来落地”。在模块规范和代码生态建设上,最适合拿来的就是 WebPack 和 Browserify。这也是@寒泉 的无线淘宝团队打算要做的事情,有信仰总归是更可爱一些的吧。
6. 完全被小瞧了的前端技术
好吧,我个人表示 WebPack 和 Browserify 难堪大用。
以 Browserify 为例,来对比下理想和现实的差距,Browserify 的优势:
- 用 npm 的大量开源资源
- 收敛的代码仓库(共有仓库 npm)
- CommonJS 规范 + Npm Package 规范
Cool!~
Browserify 的不足:
- 工程项目里的前端页面必须对 Seed(KISSY Seed 或者 jQuery) 有依赖,这种依赖不能在构建的时候复制多份(特别是在 H5 离线包 中,为了控制离线包体积,必须要去重),npm 中的模块物理上都是复制存储的
- CSS 样式的载入时机太慢(为了做到离线页面秒出 UI。样式必须比 JS 提前载入,在 DOM 构建时就生效),CSS 问题在 WebPack 和 Browserify 中无解决方案
- 项目代码下辖的模块代码存储过于分散,代码分三个部分
- 项目本身的代码,就在 gitlab 项目仓库中
- npm 安装进来的代码,在项目的 node_modules 目录中
- 项目组件代码,通过 bower 安装进来,存放在另外的 gitlab 仓库中
- 前端组件真真不同于 Npm 包,需要在此上构建一个前端 widget 包规范的超集,需要考虑这几个问题
- 为了便于 widget 代码携带 Demo,需要一个本地环境,将源码(src)构建出目标代码(dist)以便运行起来,所以包内必须携带 grunt 或者 gulp 之类的构建脚本
- widget 所携带的样式引用,依然缺少优雅的方案提
- 业务私有的 widget 代码事实上都在 gitlab 里,需要通过 bower 来安装,概念上就一定程度混淆 npm 安装和 bowser 安装,于是难对组件进行干净的归类,比如我要修复一个 npm 组件的 bug,修复之后应当 fork 在 gitlab 里用 bower 管理起来还是重新提交给 npm?
- 如果想收敛组件仓库,则 gitlab 里开发的公用代码必须发布到 npm 上,多此一举,不然,怎么收敛仓库?
- 物理代码不收敛
- 物理代码分散:npm 安装的代码放在项目的 node_modules 中,这个目录一般都在
.gitignore
里,不会提交到gitlab上
- 配置信息分散:代码依赖需要在两个地方定义,
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 哲学]([Unix 哲学]%28http://www.faqs.org/docs/artu/ch01s06.html%29”),别担心,让他一直水土不服下去好了。