Vue3组件库项目基础搭建

总结我司组件库项目的基础搭建部分

 Monorepo环境

就是指在一个大的项目仓库中,管理多个模块/包(package),这种类型的项目大都在项目根目录下有一个packages文件夹,分多个项目管理。大概结构如下:

-- packages
  -- pkg1
    --package.json
  -- pkg2
    --package.json
--package.json

简单来说就是单仓库 多项目。

使用pnpm

npm install pnpm -g && pnpm init

monorepo的实现

接下就是pnpm如何实现monorepo的了。

为了我们各个项目之间能够互相引用我们要新建一个pnpm-workspace.yaml文件将我们的包关联起来

packages:
    - 'packages/**'
    - 'examples'

这样就能将我们项目下的packages目录和examples目录关联起来了,当然如果你想关联更多目录你只需要往里面添加即可。根据上面的目录结构很显然你在根目录下新packages和examples文件夹,packages文件夹存放我们开发的包,examples用来调试我们的组件。examples里可以自行通过vue-cli创建vue3+vite项目来作为本地调试组件库的环境,很简单这里就不展开说了。

新建包文件

接下来就是要往我们的packages文件夹冲填充内容了。

utils包

一般packages要有utils包来存放我们公共方法,工具函数等

既然它是一个包,所以我们新建utils目录后就需要初始化它,让它变成一个包;终端进入utils文件夹执行:pnpm init 然后会生成一个package.json文件;这里需要改一下包名,我这里将name改成比如@myown-ui/utils表示这个utils包是属于myown-ui这个组织下的。所以记住发布之前要登录npm新建一个组织;例如myown-ui。

{
  "name": "@myown-ui/utils",
  "version": "1.0.0",
  "description": "",
  "main": "index.ts",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

组件库包

components是我们用来存放各种UI组件的包

新建components文件夹并执行 pnpm init 生成package.json

{
  "name": "myown-ui",
  "version": "1.0.0",
  "description": "",
  "main": "index.ts",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

包之间本地调试

进入components文件夹执行

pnpm install @myown-ui/utils

你会发现pnpm会自动创建个软链接直接指向我们的utils包;此时components下的packages:

{
  "name": "myown-ui",
  "version": "1.0.0",
  "description": "",
  "main": "src/index.ts",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@myown-ui/utils": "workspace:^1.0.1"
  }
}

 你会发现它的依赖@myown-ui/utils对应的版本为:workspace:^1.0.0;因为pnpm是由workspace管理的,所以有一个前缀workspace可以指向utils下的工作空间从而方便本地调试各个包直接的关联引用。

到这里基本开发方法我们已经知道啦;接下来就要进入正题了,开发一个button组件

试着开发一个button组件

在components文件夹下新建src,同时在src下新建button组件目录,在button目录下新建button.vue和index.ts。此时文件目录如下

-- components
  -- src
    -- button
      -- button.vue
      -- index.ts
  -- package.json

在button/index.ts将button.vue导出

import Button from './button.vue'

export default Button

因为我们开发组件库的时候不可能只有button,所以我们需要一个components/index.ts将我们开发的组件一个个的集中导出

import Button from './button'

export {
    Button
}

好了,一个组件的大体目录差不多就是这样了,接下来请进入我们的examples来看看能否引入我们的button组件

examples里使用button

上面已经说过执行在workspace执行 pnpm i xxx的时候pnpm会自动创建个软链接直接指向我们的xxx包。

所以这里我们直接在examples执行:pnpm i myown-ui

此时你就会发现packages.json的依赖多了个

"myown-ui": "workspace:^1.0.0"

这时候我们就能直接在我们的测试项目下引入我们本地的components组件库了,启动我们的测试项目,来到我们的 examples/src/app.vue 直接引入Button


不出意外的话你的页面就会展示我们刚刚写的button组件了

组件注册

组件单个注册

很多时候我们在vue中使用一个组件会用的app.use 将组件挂载到全局。比如这样

import { createApp } from 'vue'
import { Button } from 'myown-ui'

const app = createApp(App)
app.use(Button)
app.mount('#app')

要使用app.use函数的话我们需要让我们的每个组件都提供一个install方法,app.use()的时候就会调用这个方法;

我们将button/index.ts调整为

import button from './button.vue'
 
const withInstall = (comp) => {
    comp.install = (app) => {
        //注册组件
        app.component(comp.name, comp)
    }
    return comp
}

const Button = withInstall(button)

export default Button

此时我们就可以使用app.use来挂载我们的组件啦

组件全部注册

在使用组件时我们也会一次性的把组件库的组件都注册了。比如这样

import { createApp } from 'vue'
import MyownUi from 'myown-ui'

const app = createApp(App)
app.use(MyownUi)
app.mount('#app')

在我们上面给每个组件都挂载了一个install方法的前提下,全局的组件注册其实不难,就是套多个循环而已。

在components/index.ts下引入所有组件并导出install方法

import * as components from './src/index'

export * from './src/index'
export default {
    install: (app) => {
        for (const comkey in components) {
            app.component(components[comkey].name, components[comkey])
        }
    }
}

其实withInstall方法可以做个公共方法放到工具库里,因为后续每个组件都会用到,这里等后面开发组件的时候再调整

到这里组件开发的基本配置已经完成,最后我们对我们的组件库以及工具库进行打包,打包之前如果要发公共包的话记得将我们的各个包的协议改为MIT开源协议

...
"license": "MIT",
...

vite打包

打包们这里选择vite,它有一个库模式专门为我们来打包这种库组件的。

前面已经安装过vite了,所以这里直接在components下直接新建vite.config.ts(配置参数文件中已经注释):


import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";

export default defineConfig(
    {
        build: {
            target: 'modules',
            //打包文件目录
            outDir: "es",
            //压缩
            minify: false,
            //css分离
            //cssCodeSplit: true,
            rollupOptions: {
                //忽略打包vue文件
                external: ['vue'],
                input: ['src/index.ts'],
                output: [
                    {
                        format: 'es',
                        //不用打包成.es.js,这里我们想把它打包成.js
                        entryFileNames: '[name].js',
                        //让打包目录和我们目录对应
                        preserveModules: true,
                        //配置打包根目录
                        dir: 'es',
                        preserveModulesRoot: 'src'
                    },
                    {
                        format: 'cjs',
                        entryFileNames: '[name].js',
                        //让打包目录和我们目录对应
                        preserveModules: true,
                        //配置打包根目录
                        dir: 'lib',
                        preserveModulesRoot: 'src'
                    }
                ]
            },
            lib: {
                entry: './index.ts',
                formats: ['es', 'cjs']
            }
        },
        plugins: [
            vue()
        ]
    }
)

这里我们选择打包cjs(CommonJS)和esm(ESModule)两种形式。

其实到这里就已经可以直接打包了;components下执行: pnpm run build你就会发现打包了es和lib两个目录,但是打包的组件只能给js项目使用,在ts项目下运行会出现一些错误,而且使用的时候还会失去代码提示功能,这样的话我们就失去了用ts开发组件库的意义了。所以我们需要在打包的库里加入声明文件(.d.ts)。

那么如何向打包后的库里加入声明文件呢? 其实很简单,只需要引入vite-plugin-dts

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue"
import dts from 'vite-plugin-dts'

export default defineConfig(
    {
        build: {...},
        plugins: [
            vue(),
            dts({
                //指定使用的tsconfig.json为我们整个项目根目录下掉,如果不配置,你也可以在components下新建tsconfig.json
                tsConfigFilePath: '../../tsconfig.json'
            }),
            //因为这个插件默认打包到es下,我们想让lib目录下也生成声明文件需要再配置一个
            dts({
                outputDir:'lib',
                tsConfigFilePath: '../../tsconfig.json'
            })

        ]
    }
)

因为这个插件默认打包到es下,我们想让lib目录下也生成声明文件需要再配置一个dts插件,暂时没有想到其它更好的处理方法~

然后执行打包命令你就会发现你的es和lib下就有了声明文件

其实后面就可以进行发布了,发布之前更改一下我们components下的package.json如下:

{
  ...
  "main": "lib/index.js",
  "module":"es/index.js",
  "files": [
    "es",
    "lib"
  ],
  ...
}

解释一下里面部分字段

module

我们组件库默认入口文件是传统的CommonJS模块,但是如果你的环境支持ESModule的话,构建工具会优先使用我们的module入口

files

files是指我们需要发布到npm上的目录,因为不可能components下的所有目录都被发布上去

开始发布

其实npm发包是很容易的,发布之前记得到npm官网注册个账户,如果你要发布@xx/xx这种包的话需要在npm新建个组织组织组织名就是@后面的,注册后npm login然后npm publish,再次发布的话记得更改版本号。

其实也可以写个脚本自动化打包和发布,这里就不展开说了。

最后

其实开发一个组件库并不难,把基础的架构设计搭好了,剩下的组件更多的是业务功能上的开发。也可以去看看Antd、Element这些组件库的源码,借鉴下它们的设计思想。

你可能感兴趣的:(vue.js,前端,javascript)