小白也可以拥有自己的组件库啦!

1,097 阅读5分钟

作为一个前端人,如果可以拥有一个自己的组件库真的非常有成就感!!!

这是我上星期刚手撸的一个组件库-JAUNS-UI,组件较少,主要是为了学习,感兴趣的掘友们可移步体验。

pnpm + monorepo 环境搭建

今天通过 pnpmmonorepo 项目模式来手撸一个组件库。

安装 pnpm

npm i pnpm -g

开始搭建项目环境

新建一个项目文件夹,执行初始化操作,会发现多了 package.json 文件

pnpm init

增加配置项,根目录下新建文件 .npmrc,写入如下配置

shamefully-hoist = true

为什么要这么配置呢?

  • 提升依赖到根级别:允许在npm安装时跨范围提升依赖(所有依赖都安装在项目的根 node_modules 目录下),这可能会导致某些依赖版本的不一致。

比如你根目录下有两个目录 ABAB 都安装了 lodash 依赖,配置了这个之后,根目录的 node_modules 也会出现 lodash 依赖,这种配置适合我们现有的场景,多个包使用相同的依赖,如果多个包使用相同的依赖但版本却不一样就会出问题,不能这么配置

接下来我们来新建文件 pnpm-workspace.yaml 其作用是定义 monorepo 中的工作区范围和配置

packages:
  - packages/*
  - play
  - docs

以上我们就是定义了三个目录

  • packages 存放组件包的代码
  • play 测试组件库运行的前端项目
  • docs 组件库的文档

如果不配置pnpm-workspace.yaml文件

  • 依赖项不共享:每个包之间的依赖都是独自安装,当多个包有相同依赖时,不能共享,导致依赖项冗余
  • 版本一致性:不同包之间相同依赖的版本不一致,导致不一样的行为

所以通过 pnpm 结合配置实现性能的优化处理

开发组件

接下来我们就可以直接开始开发组件了,因本例子组件库是通过vite + vue + ts 来搭建,先安装一下相关的依赖最为全局依赖,这样我们工作区域内的包就无需重复安装

pnpm i vue @vitejs/plugin-vue typescript vite vue-tsc -D -w

其中 -w 代表在根目录下安装依赖

基础配置完成之后,我们来开始开发组件

packages 目录下新建 components 文件夹,执行初始化 pnpm init ,并修改 package.json 文件的包名 name 和入口文件main

// package.json
{
  "name": "@test-ui/components",
  "version": "0.0.1",
  "description": "",
  "main": "index.ts",
  "peerDependencies": {
    "vue": "^3.4.35"
  },
  "keywords": [],
  "author": "",
  "license": "MIT"
}

button 组件为例,创建如下目录

packages/components
├─ src
  ├─ button
     ├─ src
       └─ button.vue // 组件代码
  └─ index.ts // 用于导出button组件
└─ index.ts // 集中导出src下的所有组件

我们来写一下 button 组件,并默认导出组件

// button.vue
<template>
  <button>按钮组件</button>
</template>

<script lang='ts' setup>
defineOptions({
  name: 'TeButton'
})
</script>

// button/index.ts
import TeButton from './src/button.vue'
export {
  TeButton
}

// index.ts
export * from './button'

基本的 button 组件就已经开发完成了,我们来测试一下

测试组件

首先切换到 play 目录,我们快速创建一个 vite + vue + ts的项目

npm create vite@latest

接着我们通过 -w 将我们的私有包安装到根目录下

pnpm i @test-ui/components -w

发现根目录的 package.json的依赖中多了 "@test-ui/components": "workspace:^" ,其中的 workspace 代表安装的是工作区的依赖包,因为在执行pnpm安装的时候,pnpm会优先在工作区的目录下找对应的依赖,如果找到了则会建立对应的软连接。

我们在 play 目录新建的项目中的 App.vue 来引入对应组件

<script setup lang="ts">
import { TeButton } from '@test-ui/components'
</script>

<template>
  <te-button></te-button>
</template>

运行效果如下

button.png

哇!!! 成功了欸

这样一个简单的组件就完成了!

全局引入组件库

假设我们需要全局使用组件库的话怎么办?

我们在组件包里就需要提供一个install的方法来全部引入。

// packages/components/index.ts
import { App } from 'vue'
import { TeButton } from './button'

const components =  [
  TeButton
]

export default {
  install: (app: App) => {
    for (const c of components) {
      app.component(c.name, c)
    }
  }
}

play 目录的项目中的 main.ts 中全局引入,app页面中不单独引入按钮组件

// main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import testUi from '@test-ui/components' // 新增

createApp(App).use(testUi).mount('#app') // 变更

// App.vue
<script setup lang="ts">
</script>
<template>
  <te-button></te-button>
</template>

看看效果

button.png

全局引入成功了!

组件打包

我们使用vite来打包,首先在 packages/components 目录下创建个vite的配置文件 vite.config.ts ,如下

// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
  build: {
    rollupOptions: {
      external: ["vue"], // 忽略打包vue文件
      output: [
        {
          format: 'es',
          entryFileNames: '[name].js',
          dir: './dist',
          preserveModulesRoot: 'src'
        }
      ]
    },
    lib: {
      entry: "./index.ts"
    },
  },
  plugins: [vue()],
})

打包我们的入口文件 index.ts

package.json 文件中增加脚本执行命令

// package.json
"scripts": {
    "build": "vite build"
  }

执行命令 pnpm build

可以发现多了一个dist的目录,里面有一个 index.js 的文件,现在我们来测试一下打包的文件是否有效

我们来更改当前包的入口文件为打包后的文件如下

// package.json
"main": "dist/index.js"

然后看看 play 目录下运行的项目是否正常

button.png

哇,效果一样,代表打包成功咯!

npm 发布

npm 发布就很简单,切换到packages/components目录,配置需要发布到npm上的包的内容,增加如下配置,只发布 dist 里的内容

// package.json
"files": [
    "dist"
  ],

其中package.json中的 name 就是代表的包名,即你 pnpm install [name]name

npm login
npm publish

执行以上命令,npm login 先获取npm账号的认证,然后就可以发布!

总结

这是一个最简单的一个组件库方案,欠缺了很多细节处理,业内中的组件库会涉及到的细节很多,比如全局引入组件,按需引入组件,样式的打包等等。

如果要了解完整的组件库的方案,可以看看 element plus 的源码,我自己开发的 组件库-JAUNS-UI 也是参考了element plus的源码来开发的,受益匪浅。