十分钟搭建一个 Monorepo Vue3 组件库

7,744 阅读3分钟

这是一个使用 pnpm monorepo 来搭建的demo级别的组件库,用来自己练习,也希望给大家有0.0000001% 的帮助。 仓库地址 github.com/kongweigen/…

预览

点击在线体验 没有备案,先用ip image.png

image.png

相关技术栈

node 版本 16.14.0

1. pnpm monorepo 来进行项目管理。

选择 pnpm, 正如官网所描述的 “快、省” 优秀的包管理机制,和 workspace 功能,对 monorepo 有良好的支持。monorepo 是一种项目架构,简单的来说:一个仓库内包含多个开发项目(模块,包)。 后续组件库中的utils、cli 等工具可以进行单独发布,monorepo 就可以很好地来实现。Vite、Vue、element 等大量开源框架都在使用 pnpm monorepo,也说明的他的优秀。

2. turbo 任务编排

选择turbo 主要是解决项目之间的依赖关系,编排构建顺序。比如我们有一个 app 依赖了我们的ui库,那么在都有更改的情况下,build 顺序就有了要求,通过 turbo 的话,一行命令就可以解决,并且可以提高构建效率。

3. vite 打包,vitePress 文档管理

关于 vite 因为他开箱即用,热更新的速度很快,配合 Vue3 食用也更加的友好,自然就选择 vite 来构建。vitePress 来搭建文档效率也特别高,我们直接写 md文档 即可。

项目初始化

  1. 新建一个 pg-kit 文件夹,并执行 pnpm init 初始化 package.json

  2. pg-kit 下新建 pnpm-workspace.yaml 内容如下,packages 中我们主要用于存放 ui 库和工具库等;docs 中存放我们的组件库文档;apps 中就是我们的应用。当然名称大家可以随便更改,我们此处就是个案例。

    packages:
      - packages/* # ui库、工具库等相关库
      - docs # 组件库文档
      - apps/* # 应用
    
  3. 新建目录如下

    image.png

    并修改对应 package.json 的 name 名称。ui 库 "name": "pg-kit"、组件库 "name": "@pg-kit/components"、工具库 "name": "@pg-kit/utils",apps 可以初始化一个web项目,我这边用 Vue3 项目来测试。

  4. ts 配置文件,pg-kit 目录新建一个 tsconfig.base.json 里面保留一些通用的,其余项目中的 tsconfig.json 直接继承即可

    {
      "extends": "../../tsconfig.base.json",
      "include": ["./*.ts", "./**/*.ts"]
    }
    

组件库

1. components 组件编写

基本结构如下,src 中编写组件内容,index.ts 中插件形式导出组件。其余组件可以自行编写。以按钮组件为例,提供一个 type 属性,拼接class,来区分不同按钮的展示形式。defineExpose 暴露实例出去。

.packages
├─ components
  ├─ button
    ├─ src
      └─ index.vue
    └─ index.ts
  └─ tag  
  └─ index.ts
└─ ...
// /button/src/index.vue
<template>
  <button ref="_ref" :class="['pg-button', 'pg-button--' + type]">
    <slot></slot>
  </button>
</template>
<script setup lang="ts">
import { ref } from 'vue'
// 定义组件类型
type ButtonTypes =
  | 'default'
  | 'primary'
  | 'success'
  | 'danger'

defineProps<{
  type: ButtonTypes
}>()

const _ref = ref<HTMLButtonElement>()
defineExpose({
  ref: _ref
})
</script>
<style lang="scss" scoped></style>
--------------------------分割线--------------------------
// /button/index.ts
import { App, Plugin } from 'vue'
import Button from './src/index.vue'

export const PgButton: Plugin = {
  install(app: App) {
    app.component('pg-button', Button)
  }
}

2. UI 库编写

目录如下,主要用来聚合导出组件,定义公共样式

.packages
├─ ui
  ├─ styles
    ├─ button.scss
      └─ indev.vue
    └─ index.ts
  └─ components.ts  
  └─ index.ts
└─ └─ ...
import { PgButton } from '@pg-kit/components/button'
import { PgTag } from '@pg-kit/components/tag'
import Components from './components'
import { App } from 'vue'

const Installer = {
  install(app: App) {
    Components.forEach((c) => {
      debugger
      app.use(c)
    })
  }
}

export default Installer
export { PgButton, PgTag }

3. UI 库打包

我们没有特别要求,使用基本配置即可。ui 目录下 pnpm build 就可以只打包 ui 库。

// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import dts from 'vite-plugin-dts'
export default defineConfig({
  build: {
    lib: {
      entry: './index.ts',
      name: 'ui',
      fileName: (format) => `ui.${format}.js`
    },
    rollupOptions: {
      // 确保外部化处理那些你不想打包进库的依赖,防止多个vue产生冲突
      external: ['vue']
    } 
  },
  plugins: [vue(), dts()]
})

4. 组件库的使用

apps 中我们初始化一个项目 vue3-admin,增加依赖,此处因为我们没有将 ui 库发布到 npm,所以进行本地依赖,vue3-admin 下执行 pnpm add pg-kit(要保证我们的 ui 打包完成哈,否则组件注册会有问题。)使用起来的话和使用 element 没有任何区别。

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
// 引入组件库
import PgKit from 'pg-kit'
// 引入组件库css
import 'pg-kit/styles/button.scss'
// 注册
createApp(App).use(PgKit).mount('#app')

使用 Turborepo 优化构建

Turborepo 是 vercel 开源的针对 monorepo 拓扑依赖关系的并发构建工具,功能很强大,我们使用它主要是用来解决项目之间的依赖关系,编排构建顺序,相类似的工具例如 Nx,不过这个还没去了解。下面我们简单介绍一下 turbo 的配置

1. 安装 & 配置

全局安装 pnpm add turbo -Dw,项目根目录新建 turbo.json

{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"]
    },
    "lint": {
      "dependsOn": ["build"]
    },
    "test": {
      "dependsOn": ["build"],
      "inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts"]
    }
    "dev": {
      "dependsOn": ["lint"]
    }
  }
}
  • $schema:定义了 json 的类型,类似于 ts 对于 js 的约束
  • dependsOn:表示当前命令所依赖的相关流程
  • pipeline.build:表示当执行 turbo build 时会预先执行 ^build, ^build 就是所有项目的 package.json 里的那个 build 脚本,^ 是一个标记。如果像 lint 中的 build,他就指的是 pipeline 中的第一个 build 命令。
  • outputs:指定缓存输出目录
  • inputs: 配置的目录下文件有变化,才会重新执行此命令

2. 运行

turbo build 就会一键拉起所有项目的 build 脚本。 image.png

vitePress 文档

VitePress 是以 Markdown 为中心的静态网站生成器, VuePress 是 VitePress 的孪生兄弟, VitePress 是基于 vite,VuePress 是基于 webpack。VitePress 开箱即用,在这里不是我们的重点,大家可以直接看代码结合官网进行理解。