文章同步在:https://github.com/hoperyy/bl...
微店前端工程化起步于一个内部产品 vbuilder,对外我们有一个开源版本 bio-cli。
去年我们也写过一篇文章介绍该产品: bio: 一站式前端开发工具。
这么长时间过去了,我们在前端工程化方面有了哪些变化、遇到了哪些问题、用怎样的方案解决这些问题等等,值得为大家再分享。
V0.0
这里也就是介绍下背景,为什么我们会开发 vbuilder。
总体思路就是:将重复性工作集成化。
当时,团队面临几个问题:
- 重复:每个项目要新开一个脚手架(webpack / gulp 之类)
- 混杂:工程目录既包含脚手架文件,也包含业务文件
-
混杂:
packge.json
中的依赖既有脚手架的依赖,也有业务依赖,难以区分 - 难更新:脚手架一旦确定,几乎不再更新,如 webpack 1.0 的项目极有可能会一直维持在 webpack 1.0 状态
- 协作:团队协作中,项目的技术栈纷杂,不同人员维护同一个项目成本高昂,如:需重新理解对应工程配置等
总结为下图:
基于以上问题,我们开始了 vbuilder 的研发。
最终产品以命令行的形式发布。
此时的 vbuilder 为 V0.0 状态。
V1.0
vbuilder V1.0 提供了以下能力:
-
默认命令集:内置一套命令集,用于常见功能开发,包括
mock / update / help
等 - 静默更新:用户安装一次命令即无需关注更新,其更新自行静默完成
- 收敛脚手架:将工程内的脚手架配置隐藏,并统一管理,开发者可快速聚焦业务逻辑
-
开放接入脚手架:不限制技术栈类型(
vue / react / angular / weex
等),开放接入不同技术栈 - 插件化:除内置命令集外,插件化扩展命令集,供团队同学实现订制逻辑
vbuilder 的不断推进下,我们欣喜地看到,团队发生了一些变化:
- 便捷:新项目一个命令即创建,直接开始业务开发
- 纯粹:工程目录只保留了业务文件,脚手架等工程配置被隐藏
- 更新:脚手架被收敛为统一管理,统一更新,尽可能应用最新的技术栈
- 协作:绝大部分项目协作的成本范围收敛到 “业务逻辑”,剔除了 “工程配置逻辑”,协作成本大大降低
-
开放:在收敛脚手架配置的同时,开放性接入各类技术栈脚手架,如
weex / vms / 后台管理 / serverside project
等 - 协作:团队统一性的技术更新得以快速进行,不会再遇到因工程配置不同不断适配的问题
总结为下图:
V1.0 出现后,推进的很顺利,在推进过程中秉持如下原则:
- 提效:帮助业务开发者节省时间
- 共担:开发者参与生态建设(脚手架开发维护、插件开发),至少在绩效上会得以加分
- 好用:使用方式简单好用,才让人有用的欲望
V1.0 基本解决了以下角色的痛点:
- 后端同学:内部系统开发场景被 100% 覆盖
- 前端同学:绝大部分业务场景被覆盖
- 脚手架开发者:强大的脚手架被开发好后,得以快速推广
- 插件开发者:自定义命令,满足个性化开发
V1.0 的问题
-
封闭性
高度定制化的工程配置需求实现难度增大
脚手架配置的主题被隐藏,虽然仍然开放给开发者一些配置性文件,对于高度定制化的配置需求而言依然杯水车薪。
此时,就必须新开一个脚手架,重新接入 vbuilder 体系。
在 “开放性” 来说,打了折扣。
- 插件开发的冲突
由于 vbuilder 是基于命令行开发,插件开发者扩展自定义命令式,依然是自定义命令行,团队规模不断扩大的状态下,很容易出现不同插件使用同一个命令,被同时安装的状态下,重复执行该命令。
V2.0
V2.0 至少要解决 V1.0 存在的问题,同时需要有更明确的发展方向。
不过,V2.0 依然基于命令行。
V2.0 如何解决封闭性问题
V1.0 的思路是 “闭合”,虽然有一定的开放性,但仍然不够。
V2.0 新增 “开放” 的能力,脚手架配置可以被隐藏,也可以随时在需要的时候暴露在工程配置中,进行定制化开发。
当然,会遇到脚手架难以统一管理的问题,这一点仍然有办法可以解决。
因为被暴露的工程配置是 vbuilder 提供的,vbuilder 得以方便地统计哪些项目使用了自定义的脚手架,将通用型工具包下发给该工程。
V2.0 如何解决插件开发的冲突
- 问题 1:插件间的冲突
举个例子,有两个插件中,都有一个命令
run
。如果用户安装了这两个插件,在执行run
命令的时候,两个插件的逻辑均会触发。在某些情况下,这不是用户希望看到的场景,可能 TA 希望的只是运行插件 A 的命令
run
。 - 问题 2:插件命令集与内置命令的冲突
例如,内置命令集中有命令
init
,而某个插件也有init
。那么在用户执行
init
命令时,依然会执行两遍逻辑。 -
怎样解决?
我们组合使用了以下方案:
- vbuilder 检测是否有重复命令,如有,提示用户是都运行、还是选择运行某一个插件中的命令
-
为命令圈定生效条件
vbuilder 的命令行基于
commander
。我们基于commander
扩展了一些方法。假如我们希望,插件中的命令
show
只在工程目录中xx.show
文件存在的情况下生效,那么代码如下:commander .command('show [param]') .effect(cwd => fs.existsSync(path.join(cwd, 'xx.show'))) ---- 这是我们扩展的命令 .description('我的自定义命令') .action((param, options) => { console.log('my show'); });
-
为内置命令集声明其为“内置命令”,插件命令可以阻止内置命令执行
假如插件中有个命令
init
,而 vbuilder 内置命令中也有init
,我们希望插件中的init
命令生效,内置命令不生效,该怎么做呢?我们扩展了
commander
的 2 个方法:declareDefault
声明内置命令、preventDefault
阻止内置命令执行。定义内置命令时,代码如下:
commander .command('init [param]') .declareDefault() --- 声明内置命令 .description('内置的 init 命令') .action((param, options) => { console.log('init inside'); });
开发插件命令时,代码如下:
commander .command('init [param]') .preventDefault() --- 阻止内置命令执行 .description('内置的 init 命令') .action((param, options) => { console.log('init inside'); });
Commander
的源码只有 1000 行左右,逻辑还是很清晰的,扩展起来非常方便,这里不再列举实现。
V2.0 的新功能
在命令行这个场景下,我们把 vbuilder 定义为公司内部开发的一个“水电煤”性质的基础设施。
通过 vbuilder,我们新增了以下场景:
- 支持 chrome 插件 es6/7 化开发
- 支持组件库快速开发
- 支持 js 工具库快速开发
- 支持快速打开文档库等等
得益于插件化,通过充分调动开发者积极性,我们可以将其能力无限延展。
V3.0
我们目前还没有进入 3.0 的开发,但有一些方向是我们可以尝试的:
- cloudIDE(内部已有该类平台)
- vscode 定制化 IDE,该类场景在超大型团队比较适合,IDE 定制化开发有更多的应用场景,更快的开发效率
- 云化(其实不算新了,很多公司已经实现了云化)
这是目前我们在微店前端工程化领域的一些实践和思考,希望对大家有帮助。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。