阅读 1527

谈谈组件库的设计和搭建

前言

组件库是一个老生常谈的话题,谈到公司基建,必然会涉及到组件库的搭建。虽然只要投入一定的人力和时间,似乎组件库的产出并不是一件难事,但是考虑到现在前端市场上有一定流行度的组件库的数量,还是屈指可数。由此可见,产出并不等同于优秀。因此如何在结合公司业务的同时,兼顾组件库的优秀设计和可用性是极其必要的。

背景

笔者所在的团队主要是做互动营销活动,基本上活动的类型是可以枚举出来的,当前的开发现状是:由于业务的特殊性,每个开发在做业务时不约而同的选择自己去开发对应的活动组件,而不是复用别人的,这样导致的结果是降低了开发效率和整个团队的产出,同时不利于推进业务模型和设计标准化,因此当务之急是建立一套抽象化的、高复用性和结合设计标准的业务模型组件库。

目标

  1. 支持Comonjs、EsModule等多种规范,提供全量注册和局部注册的能力;
  2. 包含一份可读性良好的文档说明,并且有示例可交互;
  3. 文档库、组件库发布自动化部署;
  4. 项目开发、提交、版本管理规范;
  5. 组件单元测试;
  6. 组件UI设计规范、API设计命名规范等;
  7. 组件开发者开发流程简单高效,不需要额外的心智负担。

起步

项目架构

技术选型

  • 技术栈:vue >= 2.5.22 + tsx
  • 包管理:vue-cli + yarn workspace
  • 打包工具:rollup + babel
  • 代码规范:stylelint + eslint + prettier
  • 版本日志管理:standard-version + changelog
  • 文档库:vuepress
  • 单元测试:jest

开发流程

如何简化开发流程?

什么是好的开发模式,组件开发者只需要关注自己所开发的组件模块,包括组件源码、文档和demo都应该在当前组件文件模块下开发,整个工程内部负责拿对应的模块进行渲染,简化的方式如下:

  • 组件的README.md文档,需要同步到`vuepress`工程目录下,才能实现文档的热更新,因此项目启动阶段,需要额外再启动一个文件复制的服务,实时监听.md文件的变更,拷贝到对应工程目录下;
  • demo服务的路由直接根据组件demo文件进行注册,demo文件变更,对应的demo服务也会实时热更新。

编译打包

ts/tsx编译

通过`babel`核心库提供的`transformAsync`这个API进行编译即可,同时babel配置中加入一些预设`preset`,包含`preset-typescript`和`preset-jsx`两个预设。

less样式编译

利用less包自己提供的`render`函数就可以把less编译为css,不过还需要对css做进一步的自动浏览器前缀兼容处理,通过`postcss`插件即可实现。

vue单文件编译

一个vue文件包含一个`template`、`script`和多个`style`模块,因此需要对这些模块进行单独编译,由于在`webpack`中,所有的vue单文件都是通过`vue-loader`进行编译解析,所以考虑从loader中入手,包含以下几个API:

compileUtils
vueTemplateCompiler
复制代码

通过这些api可以拿到每个区块的源码,针对`script`模块的源码,需要编译为`render`函数,组装到对应的`render options`中去,`style`样式模块的源码,处理时需要考虑`scoped`特殊情况,编译时需要添加一个`scopedId`作为唯一的元素类名标识。

打包目标

需要支持`esModule`规范和`commonjs`规范,同时为了优化用户使用体验,需要做到按需引入和加载,也即`tree-shaking`的效果。

vuepress踩坑总结

问题1:vuepress默认主题不好看,如何自定义主题?

vuepress默认的主题不能满足所有开发者的需求,所以它提供了对外的自定义主题入口。只需要按照官方文档提供的约定主题的目录结构重新组织主题即可:

按照官方约定规范完成theme主题开发之后,vuepress会自动引用并渲染你开发的主题。

问题2:vuepress如何注册页面路由?

vuepress遵循“约定大于配置的原则”,根据docs所在的目录下的文件地址,自动注册页面路由地址,因此无需手动注册。

问题3:如何渲染markdown的内容?

vuepress提供了markdown插槽,对于一个markdown文件中的普通内容,会成为插槽的默认内容,你可以直接使用`Content`组件来访问它:

<Content />
复制代码

问题4:如何获取全局路由信息和自定义配置侧边栏页面路由的顺序?

  1. vuepress在编译阶段,向开发者提供了全局的计算属性,包含站点数据site和页面数据site和页面数据page,路由信息就包含在其中;

  2. vuepress的Markdown文件可以包含`YAML front matter`,同时必须作为文件中的第一部分,基本例子如下:

    group: 基本组件 level: 1

页面中可以通过$frontmatter获取到该配置项,其中的`group`为当前页面所在分组名称,`level`是当前页面路由在所在分组的层级大小,通过这两个配置项数据就可以对初始化路由进行自定义排序。

问题5:如何在文档中引入demo案例的源码?

vuepress中的markdown文档支持导入代码段,规则如下:

<<< @/filepath
<<< @/filepath{highlightLines}
复制代码

其中的`filepath`为需要引入源码的目标文件的路径,`highlightLines`为高亮的代码行数。

同时为了支持只按需导入目标文件的部分代码,它支持`VS Code region`,即可以在文件路径后方的`#`紧接着提供一个自定义的区域名称(预设为snippet)

输入:

<<< @/../@vuepress/markdown/__tests__/fragments/snippet-with-region.js#snippet
复制代码

如果需要在同一个源文件中添加引入多个代码区块,需要在全局`config.js`增加markdown的配置项,配置如下:

问题6:vuepress启动的文档服务如何去加载vue-cli启动的demo服务

考虑到项目需要同时启动两个服务,必然两个服务的端口号不一致,所以必然产生跨域问题,因此考虑用`iframe`去加载demo服务。

问题7:文档服务和demo服务的路由同步问题如何解决?

情况1:点击文档服务的侧边导航栏路由,demo服务如何同步对应的组件案例页面路由?

方案:同步动态改变`iframe`的`src`地址。

情况2:点击demo服务的组件列表项,文档服务如何同步到对应的组件文档页面路由?

方案:通过`postMessage`进行跨域通讯,子窗口发送消息,父窗口监听`message`事件获取子窗口发送的路由数据,通过路由导航到指定的文档页面。

问题8:demo服务下的页面如何引用本地打包之后的组件源码?

考虑到使用者在使用我们开发的组件库时的引入模式,因此需要通过引入打包后的组件源码来验证打包后的源码的可用性。

通过`vue-cli`提供的webpack配置项`resolve/alias`来配置别名指向到打包之后的目录,同时需要在项目启动阶段,启动一个实时监听文件变化而编译的服务,确保引用的源码始终是最新的:

// 别名-用于引用打包之后的组件源码
const resolve = (dir) => path.join(__dirname, dir)resolve: {
  alias: {    "@tuia/market-ui": resolve("es")  }}
// 组件内部
import { component } from '@tuia/market-ui'
复制代码

后记

未完待续,后续随着项目的进展,会持续更新`组件API设计规范`、`单元测试`和`自动化部署`相关的总结,敬请期待~~~

文章分类
前端