开发组件库(Monorepo策略),使用storybook搭建组件库文档,并用Lerna发布到npm

4,524 阅读6分钟

前置知识

快速原型开发

  • VueCli 中提供了一个插件可以进行原型快速开发(能单独运行某个组件)
  • 需要先额外安装一个全局的扩展: npm install -g @vue/cli-service-global
  • 使用vue serve快速查看组件的运行效果

vue serve 如果不指定参数默认会在当前目录下找以下入口文件:main.js、 index.js、 App.vue、 app.vue

  • 可以指定要加载的组件

vue serve ./src/login.vue

Multirepo(Multiple Repository)

每一个包对应一个项目

Monorepo策略

Monorepo 是一种将多个项目代码存储在一个仓库里的软件开发策略。(一个项目仓库中管理多个模块/包)

由于介绍两种策略的文章很多,这里只简单介绍一下优势,劣势就不过多介绍了

优势:

  • 灵活:每个repo可以灵活选择开发工具和环境配置等

  • 安全:自然的权限控制,发布上线对其他项目无影响

storybook

  • 可视化的组件展示平台
  • 在隔离的开发环境中,以交互式的方式展示组件
  • 独立开发组件
  • 支持的框架:⬇️

React、React Native、Vue、Angular、Ember、HTML、Svelte、Mithril、Riot

storybook 安装

npx -p @storybook/cli sb init --type vue
npm i vue
npm i vue-loader vue-template-compiler --dev

storybook 配置

.sotrybook文件中的main.js中配置匹配路径

module.exports = {
  "stories": [
    "../stories/**/*.stories.mdx",
    "../stories/**/*.stories.@(js|jsx|ts|tsx)",
+  "../packages/**/*.stories.@(js|jsx|ts|tsx)"
  ],
  "addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials"
  ],
  "framework": "@storybook/vue"
}

使用storybook搭建组件库文档

传送门官方文档

yarn工作区的使用

在项目根目录的package.json文件中设置如下代码来开启工作区。

"private": true,
"workspaces": [
	"./packages/*"
]

其次给工作区根目录安装开发依赖

yarn add lodash@4 -D -W

给指定工作区安装依赖

yarn workspace j-button add lodash@3

给所有工作区安装依赖

yarn install

Lerna

Lerna是一种工具,针对 使用 git 和 npm 管理多软件包代码仓库的工作流程进行优化。官网传 送门。用于管理具有多个包的 JavaScript 项目,它可以一键把代码提交到gitnpm仓库。

项目初始化

首先使用 npm 将 Lerna 安装到全局环境中:

推荐使用 Lerna 2.x 版本。

npm install --global lerna

接下来,我们将创建一个新的 git 代码仓库:

git init lerna-repo && cd lerna-repo

现在,我们将上述仓库转变为一个 Lerna 仓库:

lerna init && npm i

你的代码仓库目前应该是如下结构:

lerna-repo/
  packages/
  package.json
  lerna.json

还有加入 Babel 插件(给代码降级,提高兼容性)

npm i babel-core@bridge -D

常用指令

  • lerna init => 初始化

  • lerna bootstrap => 在当前 Lerna 仓库中执行引导流程(bootstrap)。安装所有 依赖项并链接任何交叉依赖。

  • lerna import => 将本地路径 中的软件包导入(import) packages/ 中并提交 commit。

  • lerna publish => 将本地路径 中的软件包导入(import) packages/ 中并提交 commit。

  • lerna changed => 检查自上次发布以来哪些软件包被修改过。

  • lerna diff => 列出所有或某个软件包自上次发布以来的修改情况。

  • lerna run [script] => 在每一个包含 [script] 脚本的软件包中运行此 npm 脚本。

  • lerna ls => 列出当前 Lerna 仓库中的所有公共软件包(public packages)。

  • lerna add => 安装依赖: lerna add 依赖文件  安装地址

  • lerna link => 链接互相引用的库

关于打包

相比webpack来说,打包组件库或者框架Rollup更方便一些。因为webpack需要自己配置Tree-shaking,即使配置了Tree-shaking,打包出来的结果也要比Rollup更加臃肿,而Rollup默认就支持Tree-shaking

安装依赖:

yarn add rollup rollup-plugin-terser rollup-plugin-vue@5.1.9 vue-template-compiler -D -W

配置文件rollup.config.js

import { terser } from 'rollup-plugin-terser'
import vue from 'rollup-plugin-vue'
module.exports = [
    {
        input: 'index.js',
        output: [
            {
                file: 'dist/index.js',
                format: 'es'
            }
        ],
        plugins: [
            vue({
                // Dynamically inject css as a <style> tag
                css: true,
                // Explicitly convert template to render function
                compileTemplate: true
            }),
            terser()
        ]
    }
]

之后在package.json中添加scripts

注意: 此时的package.json并不是根目录下的,而是每个组件自己的package.json

"build": "rollup -c"

运行yarn workspace j-button run build就会在j-button下生成打包之后的文件。这个命令的意思是使用yarn运行名为j-button的工作区下的build命令,即刚在j-button文件夹下的package.json中添加的build命令。

但是这一单独的打包一个组件也太繁琐了些,使用yarn的一些插件可以实现将所有的组件一起打包。

yarn add @rollup/plugin-json rollup-plugin-postcss @rollup/plugin-node-resolve -D -W

其中@rollup/plugin-json可以让rollupjson文件作为模块加载,@rollup/plugin-node-resolve可以将依赖的第三方包打包进来。
之后在项目根目录下创建rollup的配置文件rollup.config.js文件

import fs from 'fs'
import path from 'path'
import json from '@rollup/plugin-json'
import vue from 'rollup-plugin-vue'
import postcss from 'rollup-plugin-postcss'
import { terser } from 'rollup-plugin-terser'
import { nodeResolve } from '@rollup/plugin-node-resolve'

const isDev = process.env.NODE_ENV !== 'production'

// 公共插件配置
const plugins = [
    vue({
        // Dynamically inject css as a <style> tag
        css: true,
        // Explicitly convert template to render function
        compileTemplate: true
    }),
    json(),
    nodeResolve(),
    postcss({
        // 把 css 插入到 style 中
        // inject: true,
        // 把 css 放到和js同一目录
        extract: true
    })
]

// 如果不是开发环境,开启压缩
isDev || plugins.push(terser())

// packages 文件夹路径
const root = path.resolve(__dirname, 'packages')

module.exports = fs.readdirSync(root)
    // 过滤,只保留文件夹
    .filter(item => fs.statSync(path.resolve(root, item)).isDirectory())
    // 为每一个文件夹创建对应的配置
    .map(item => {
        const pkg = require(path.resolve(root, item, 'package.json'))
        return {
            input: path.resolve(root, item, 'index.js'),
            output: [
                {
                    exports: 'auto',
                    file: path.resolve(root, item, pkg.main),
                    format: 'cjs'
                },
                {
                    exports: 'auto',
                    file: path.join(root, item, pkg.module),
                    format: 'es'
                },
            ],
            plugins: plugins
        }
    })

然后在根目录的package.json文件中配置脚本

"build": "rollup -c"

之后在每个组件对应的package.json中配置:

"main": "dist/cjs/index.js",
"module": "dist/es/index.js",

main是组件打包的出口,也是使用时的入口,而module则是存放依赖的第三方包的位置。
配置完成之后在根目录下运行yarn build命令,就会自动打包所有packages下的组件。

关于清理

node_modules的删除可以使用lerna原本的命令lerna clean,而清理dist文件夹则需要使用第三方库来实现,此处使用rimraf:

yarn add rimraf -D -W

之后给每个组件的package.json配置一个命令:

"del": "rimraf dist"

之后运行yarn workspaces run del即可运行所有包下的del命令,把dist目录删除掉,然而每次新建一个组件都要创建相同的文件是不可取的。

Lerna发布

组件库开发完毕,要将其提交到github或者npm上,这个时候可以使用Lerna,可以方便将所有包统一发布。

Lerna是一个优化使用gitnpm管理夺宝仓库的工作流工具,用于管理具有多个包的JavaScript项目,它可以一键把代码提交到gitnpm仓库。

全局安装Lerna之后,使用lerna init来进行初始化lerna的配置。如果当前项目没有使用git进行管理的话,则会自动创建.git文件夹,当然,如果项目根目录下没有packages文件夹的话,也会自动创建出来。

关于发布,使用yarn lerna命令会自动把packages文件夹下的所有组件都发布到npm上。此时要注意可能组件名字会与当前已有的包重复,此时会提示相应信息,只需要改组件名字就好。还要注意此时的yarnnpm的源都要设置成初始的源,不要是淘宝的源,否则会报错。

源码地址

在线 demo