微医门户在移动端组件库方向上的探索
序言
来公司两年了,见证了公司组件库从无到有,从1.0到2.0的变迁过程,组件库已有1500+次提交,承载了门户技术部数个移动端项目,也算得上是一个经过考验的组件库了。
组件库内的组件固然重要,但是整体组件库的架构设计更是一个高可用,易扩展组件库的核心,那么应当如何搭建一个优质的组件库?
0、组件库的价值
在应用端的体现:统一视觉规范,保证产品体验的一致性。
在开发侧的体现:组件的可复用性削减了开发成本,也变相地减小了页面及项目体积。
1、typescript支持
相比于1.0的版本,wandui2.0采用了ts的写法,强类型的校验可以将许多语法语义上的错误扼杀在萌芽之中。类型的指定也像是注释一般增强了代码的可读性。另外,在开发完成之后通过指定package.json
内的types
字段,配合编辑器还有导入的语法提示功能,组件库内置的组件一览无余。
2、一键组件生成
{
"create": "node build/create-component"
}
一键生成新的组件规则,仅需执行一行npm run create
,通过命令行inquiry式调用,传入需要新生成的组件名,脚本便会通过读取模板,进行新的组件的初始化,包括生成
- 组件主文件
- 组件样式文件
- 组件文档
- 将组件添加进路由
等等一系列重复操作,开发人员仅需关注组件本身的实现,大大提升了开发者的开发效率。
3、打包配置
3.1、为了实现按需加载的处理
为了实现按需加载引入,我们常用babel-plugin-component
或babel-plugin-syntax-dynamic-import
这两个babel插件来使得import { WandUploader } from @weiyi/wand-ui
在打包阶段转化成import /node_modules/@weiyi/wand-ui/libaryName/uploader/index.js
+组件css引入(通用css会从libaryName下引入)来实现。
因此我们要为每个组件作为打包出index.js和组件自身的的css文件,为此在按需打包的配置中我们为组件设置单独入口,样式文件使用MiniCssExtractPlugin
来实现拆分,最后得到如下效果。
3.2、组件库的通用样式
当业务方使用按需加载的方式引入我们的组件库,如果每个组件都对组件库通用样式进行引用,无疑会有大量的重复样式代码,由于babel-plugin-component
插件已经提供了style选项,当该选项设置为true
时,会将组件库通用样式额外引入,因此,为了单独打包出一份通用css文件,我们引入了gulp额外为通用css进行一次打包。
// less编译
gulp.task('css', done => {
gulp
.src('../../src/styles/base.less')
.pipe(less())
.pipe(concat('base.css'))
.pipe(autoprefixer({
overrideBrowserslist: ['last 2 versions', 'ie > 8']
}))
.pipe(cleanCSS())
.pipe(rename(function (path) {
path.dirname = path.dirname.replace('less', 'css')
}))
.pipe(gulp.dest('../../lib/theme-chalk/'))
done()
})
3.3、获取运行时依赖
配置中需要外置化vue,防止vue被打包进入组件bundle中,而是通过运行时再去外部获取这些扩展依赖,配合指定package.json
中的peerDependencies,强制要求接入方对我们外置的扩展依赖进行安装和打包。
// webpack.base.conf.js
externals: [
{
vue: {
root: 'Vue',
commonjs: 'vue',
commonjs2: 'vue',
amd: 'vue'
}
}
]
// package.json
"peerDependencies": {
"vue": ">=2.5.15"
}
4、组件库文档
一份优质的文档是一个成功的组件库的基础,element-ui的官网文档是经受了无数厂商的考验的,仔细观察可以发现element-ui的组件案例和组件文档是一一对应的,难道element-ui的组件demo写了两次?仔细阅读后可以发现,element-ui的文档路由是使用的md类型的文件,而在打包配置中指定了一个自定义的md-loader来处理该路由文件,md文件中的被:::demo
和:::
所包裹的代码块会被执行并编译,内部执行流程大概是这个样子的:
在展示demo的项目里,我们只需要注册demo-block
组件,并提供source
插槽承载编译后的组件demo
,highlight
插槽承载md中的源代码,即可得到文档即demo的效果了。效果图:
另一个值得权衡的问题是,文档是新开一个项目还是就放在组件库的项目里?
如果放在项目里,组件库一大,项目会略显臃肿,并且每次跑项目都需要把组件进行重新编译,速度堪忧。而如果分离出一个新的项目,则享受不到开发时热更新的遍历,需要把打包文件频繁输出,才能实时看到组件的效果,而且另起一个工程也是有成本的,需要安装两次通用的依赖。而且从维护成本的角度来看,还是放在一个项目内会方便得多,除非你的目标是做一个大而美的业界组件库,而不是为了业务需要快速迭代的产物。(然而没有资本支撑,估计也没人愿意做这么一套东西)
5、发包规范
早期wand-ui2.0刚出时采用指定负责人手动发包的形式,当时刚出的组件库有很多小问题,频繁的发包步骤十分繁琐。后期为了解决这一问题,项目使用gitlab-ci,配合gitlab-runner,在yml配置文件中划分了打包和发包两个stage,打包机完成文档和组件库打包后进行压缩,再远程传输到虚拟机,执行publish脚本,比对线上和当前package.json
内的组件库版本,检测到落后后自动publish,再也不用手动发包了。
6、轻量的组件库脚手架
文档打包,组件打包,全量、按需、开发、生产,require("package.json").script
会越来越大build目录下的文件也会越来越多,使项目变得臃肿不堪。我们尝试对组件库基本不再修改的打包配置进行了封装,通过命令式的调用形式,读取项目配置,执行命令内置的打包配置,即可完成打包。项目内甚至无需build目录。
同时我们还抽离了wand-template
一份组件库最小单元的模板,配合抽离的wand-cli
,可以一键式地生成一个组件即demo的vue移动端组件库,并提供了ts环境开发,提供md即文档的特性。
结语
一个好的组件库不仅在组件本身的通用性,易用性,也在于整体组件库架构设计的可重用性,规范性,易扩展性,市面上的组件库层次不齐,组件库搭建可以参考学习业内如element-ui,ant-design,vant等优秀组件库的设计理念,如果有错误烦请在评论区指正交流,谢谢。
文中组件库由wand-ui2.0开发小分队共同完成。