基于 vue-sfc-rollup 快速搭建业务组件库的踩坑与收获

993 阅读4分钟

为什么需要一个业务组件库

因为我们项目为 polyrepo,众多项目分散在各个仓库中。整个后台系统项目是微前端架构, 应该各个子系统之间没啥关系。但从项目整体角度出发,对于 ui 有一致性的要求,因此难免有部分组件需要复用。

快速开始

采用成熟的脚手架快速搭建一个库。

team-innovation/vue-sfc-rollup: Quickly generate redistributable Vue components with Rollup

运行命令 npx vue-sfc-rollup ,按照提示,选择搭建一个 library。

踩坑

编译 less style

如果 vue sfc 文件中使用了 less 或者其他 css 扩展语言,需要安装对应的包。在我的项目中,我使用了 less , 那么需要运行 npm i -D less 安装 less

TypeError: Cannot read property 'NormalModule' of undefined

主要原因是我安装了不匹配的 less 和 less-loader , 然后运行vue-cli-service serve dev/serve.ts 报错。

搜索了一圈无果,vue.js - TypeError: Cannot read property 'NormalModule' of undefined - Stack Overflow ,试了这个方法也没用

错误的依赖:

 "less": "^4.0.0",
 "less-loader": "^8.0.0",

修改成较低版本就能跑了,修改如下:

 "less": "^3.0.0",
 "less-loader": "^5.0.0",

编译前把之前的产物删掉

这么做可以保证编译产物清晰,不会这次编译产物和上次编译产物混在一起,造成混乱。

在 package.json 的 scripts 文件中,添加

"prebuild": "rimraf ./dist"

运行 yarn serve 报错。 cant find @/entry.esm

主要是 ts 没有解析到这个路径,需要安装 "@vue/cli-plugin-typescript": "^4.5.15" ,然后 在 tsconfig.json 配置一下 paths

"paths": {
  "@/*": ["./src/*"]
}

eslint and prettier

eslint and prettier config

config file inspired from arco-design/arco-design-vue: A Vue.js 3 UI Library based on Arco Design

提交时自动 format

随着 husky 版本升级,配置方式发生了一点点变化。

  1. Edit package.json > prepare script and run it once:
npm set-script prepare "husky install"
npm run prepare
  1. Add a hook:
npx husky add .husky/pre-commit "npm test"
git add .husky/pre-commit
  1. In .husky/pre-commit
#!/usr/bin/env sh
. "$(dirname "$0")/_/husky.sh"

npx lint-staged

文档

组件库搭建好了,需要搭配一个文档,看似选择很多,但是大多对 vue3 支持不是很好。看了一下文档生成工具:

最后选择了成熟,支持 vue3 ,且能灵活自定义的 @storybook/vue3

引入库的全局样式

因为我的组件库是基于 ant-design-vue 的二次封装,因此在用到 ant-design-vue 组件的地方,需要引入 ant-design-vue 对应组件的样式。如果每个 story 都要引入样式的话就太麻烦了,于是查文档可得,在 .storybook/preview.js 里引入库的样式即可

import 'ant-design-vue/dist/antd.css';

export const parameters = {
  /* some code */
};

处理 less

查文档可得,在 .storybook/main.js 里面添加处理 less 文件的配置

module.exports = {
	stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
	addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
	framework: '@storybook/vue3',
	webpackFinal: async (config, { configType }) => {
		config.module.rules.push({
			test: /\.less$/,
			use: ['style-loader', 'css-loader', 'less-loader'],
		});
		return config;
	},
};

如何编写 story

参考文档,感觉比较好的方式是写一个 template,然后基于模板新建 story,修改参数即可

const Template = (args) => ({
	components: { ButtonGroup },
	setup() {
		return { args };
	},
	template: '<ButtonGroup v-bind="args" />',
});

how to write docs

可以参考 ant design 的文档结构,下拉菜单 Dropdown - Ant Design

# 英文 + 中文

一句话描述你的组件作用

## 何时使用

## 代码演示

## API

收获

实时开发

vue-sfc-rollup 提供了便捷开发组件的方式。 巧妙了利用了 esm,直接起了一个 vue 服务。示例代码如下:

// serve.ts

import { createApp } from 'vue';
import Sandbox from './sandbox.vue';
// To register individual components where they are used (serve.vue) instead of using the
// library as a whole, comment/remove this import and it's corresponding "app.use" call

import Components from '@/entry.esm';

const app = createApp(Sandbox);
app.use(Components);

app.mount('#app');
// @/entry/esm

import { App, Plugin } from 'vue';

// Import vue components
import * as components from './components/index';

// install function executed by Vue.use()
const install: Exclude<Plugin['install'], undefined> = function installTtComponents(app: App) {
	Object.entries(components).forEach(([componentName, component]) => {
		app.component(componentName, component);
	});
};

// Create module definition for Vue.use()
export default install;

// To allow individual component use, export components
// each can be registered via Vue.component()
export * from './components/index';

如何处理 peerDependence

package.json | npm Docs

由于 npm 版本 3 到 6 的版本,不会自动安装 peerDependence 中的依赖。但是开发的时候又需要用到这些包,那怎么办呢,最后采用的骚操作是,把 peerDependence 中的包放在 devDependence 中,再安装一遍。

当然,如果你觉得这种做法比较怪异,可以使用 npm 版本为 7 的版本,该版本会默认安装 peerDependence 中的依赖

另外,peerDependence 中的依赖也不需要放在编译产物中,因此,我们可以在 rollup.config.js 中指定 external,防止将一些包打进产物里。