vite+typescript实现vue3组件库

本文主要参考:Vite+TS带你搭建一个属于自己的Vue3组件库 - 公众号-web前端进阶 - 博客园

环境配置

一文吃透 pnpm 如何使用 workspace 构建 monorepo,与 npm、yarn 的用法对比(pnpm 9.x 内部安装依赖问题 link-workspace-packages) | kshao-blog - 前端知识记录

monorepo & workspace

**monorepo: **利用单一仓库来管理多个 packages 的一种策略,很多项目如Vue3,ElementUI都采用了此模式。可以使用pnpm、yarn、lerna等工具打造menorepo,作为组件库的环境。

**workspace: **由上述单仓多包催生的管理方式,workspace 是 npm、yarn、pnpm 等包管理工具提供的一种特性,用于管理多个包的依赖关系。合理配置 workspace 后,包之间的依赖将在 install 时中直接处理。

这里使用pnpm进行搭建。

pnpm

安装:npm install pnpm -g

根目录初始化package.json:pnpm init

根目录创建pnpm-workspace.yaml:用于启用workspace

文件配置:

1
2
3
packages:
- 'packages/*'
- 'examples'

指定项目中的packages/*examples为工作区。将packages的子文件夹和examples目录关联起来,关联更多目录直接添加即可。

此处packages存放我们开发的包,examples用于调试组件

安装对应依赖

安装vue3,ts,scss:pnpm i vue@latest typescript sass -D -w

初始化并配置tsconfit.json:npx tsc --init

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// tsconfig.json:
{
"compilerOptions": {
"baseUrl": ".",
"jsx": "preserve",
"strict": true,
"target": "ES2015",
"module": "ESNext",
"skipLibCheck": true,
"esModuleInterop": true,
"moduleResolution": "Node",
"lib": ["esnext", "dom"]
}
}

测试用例:手动搭建基于vite的vue3实例

进入examples文件夹,进行vue3初始化

初始化仓库:pnpm init

安装vite:pnpm install vite @vitejs/plugin-vue -D -w

  • @vitejs/plugin-vue用来支持.vue文件的转译,这里安装的插件都放在根目录下

新建并配置vite.config.ts:

1
2
3
4
5
6
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
plugins:[vue()]
})

新建index.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script src="main.ts" type="module"></script>
</body>
</html>

新建app.vue:

1
2
3
<template>
<div>test</div>
</template>

新建main.ts:

1
2
3
4
5
6
import {createApp} from 'vue'
import App from './app.vue'

const app = createApp(App)

app.mount('#app')

package.json添加调试命令:

1
2
3
"scripts": {
"dev": "vite"
},

pnpm run dev后vue成功启动

npm包本地调试

npm 包本地调试(详细流程:包本地路径、npm link 、yalc)_npm包本地调试-CSDN博客

创建新包

根目录中的packages文件夹用于放置我们的本地npm包。我们可以在此创建新的文件夹用来分类放置包里的函数内容等。packages一般使用utils包来存放公共方法函数,在此新建一个utils目录并使用pnpm init生成packages.json:

1
2
3
4
5
6
7
8
9
10
11
12
{
"name": "@weird-dream-ui/utils",
"version": "1.0.0",
"description": "",
"main": "index.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

修改npm包名。@weird-dream-ui/utils表明此包为weird-dream-ui组织下的utils部分。

将主文件入口改为ts,并新建index.ts文件用于写入函数方法。

1
2
3
4
// index.ts
export const addFunc = (a:number,b:number):number=>{
return a+b
}

包与包之间的引入

packages一般使用components作为存放UI组件的包。在packages目录新建一个components目录并使用pnpm init生成packages.json。修改包名和主文件格式。

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"name": "weird-dream",
"version": "1.0.0",
"description": "",
"main": "index.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
}

将utils引入到components中使用,可以通过workspace进行引入:

1
pnpm add @weird-dream-ui/utils --workspace

也可以使用相对路径引入:pnpm add ../utils/weird-dream-ui/utils

成功引入后,components的packages.json会显示依赖:

1
2
3
"dependencies": {
"@weird-dream-ui/utils": "workspace:^"
}

在index.ts应用:

1
2
3
import {addFunc} from '@weird-dream-ui/utils'

console.log(addFunc(1,2))

由于组件库是基于ts的,无法通过tsc直接编译测试,需要安装esno用于执行ts文件,以便测试组件间的引入情况:npm i esno -g,使用esno index.ts执行文件

组件开发及使用

组件库开发之基础样式环境架构

完善工程环境开发第一个组件 - 掘金

组件基本开发及引入

在components文件夹下新建src,同时在src下新建button组件目录

在button目录下创建button.vue:

1
2
3
4
<!-- components/button/button.vue -->
<template>
<button>test1</button>
</template>

在button目录下的index.ts导出button组件:

1
2
3
4
5
6
// components/button/index.ts
import Button from "./button.vue";

export{
Button
}

在components目录下的index.ts将开发的所有组件集中导出:

1
2
3
4
5
6
7
8
9
10
// components/index.ts
import {addFunc} from '@weird-dream-ui/utils'
import {Button} from './src/button'
import {weirdOption} from './src/option'

export{
Button,
weirdOption,
addFunc
}

一个最基本的组件就完成了。组件的目录如下所示:

image-20241027013132634

在项目中引入

进入examples文件夹,终端安装本地npm包:

1
pnpm i weird-dream-ui --workspace

即可在项目中食用:

1
2
3
4
5
6
7
8
9
<template>
<div>
<Button />
</div>
</template>

<script lang="ts" setup>
import {Button} from 'weird-dream-ui';
</script>

组件规范及挂载

组件类型封装ExtractPropTypes

一个正常组件会接收如size,color等许多属性,通过类型定义来规范这些属性的使用

为button.vue在相同目录下新建button.ts文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { ExtractPropTypes } from 'vue'
export const ButtonType = ['primary', 'success', 'info', 'warning', 'danger','text']
export const ButtonSize = ['large', 'medium', 'samll', 'mini']
export const buttonProps = {
type: {
type: String,
values:ButtonType
},
size: {
type: String,
values:ButtonSize
}
}
export type ButtonProps = ExtractPropTypes<typeof buttonProps>

ExtractPropTypes是vue用于定义类型的属性,定义后vue文件可以直接通过props传入属性。

组件挂载app.use

app.use(plugin, options)用于插件的安装,将组件挂载到全局,一般用于安装npm的第三方库。

其中 plugin 表示要传递的插件对象, options 参数可选,表示选项配置,plugin包括了install方法。

app.use(plugin) 中的核心部分是 “plugin(app, …options)”和 “plugin.install(app, …options)”,调用app.use本质上是把plugin.install()调用一遍,并将app实例作为参数传入,将plugin挂载到app上。

1
2
3
4
5
6
install(Vue,option){
组件
指令
混入
挂载vue原型
}

我们可以通过app.use将编写的组件挂载到全局。所以在此之前需要给插件添加一个install方法。

修改button/index.ts文件,为导出的组件添加方法:

1
2
3
4
5
6
7
8
import wdButton from "./src/button.vue";
import type { App } from "vue";

wdButton.install = function(app:App){
app.component(<any>wdButton.name,wdButton)
}

export default wdButton

在examples的main.ts挂载:

1
2
3
4
5
6
7
import {createApp} from 'vue'
import App from './app.vue'
import {wdButton} from 'weird-dream-ui'
const app = createApp(App)

app.use(wdButton)
app.mount('#app')

你会发现这样不生效,因为组件的类型不匹配(wdButton默认没有Plugin类型),如果用的js是可以正常运行的。This is TypeScript。

我们可以通过定义泛型来为组件添加上Plugin插件对象:

1
2
3
4
5
6
7
8
9
10
11
12
import wdButton from "./src/button.vue";
import type { App,Plugin } from "vue";

// 定义泛型对象T & Plugin
type SFCWithInstall<T> = T & Plugin
// 利用泛型合并为wdButton添加上Plugin类型
const _wdButton = wdButton as SFCWithInstall<typeof wdButton>

_wdButton.install = function(app:App){
app.component(<string>_wdButton.name,_wdButton)
}
export default _wdButton

这样就成功生效了。

组件样式设计

SCSS

官方文档:Sass教程 Sass中文文档 | Sass中文网

基本语法:揭开 SCSS 神秘面纱:利用预处理器升级你的样式表技术 - 掘金

SCSS 进阶之道:探索更多样式表达的可能性 - 掘金

SCSS是一种CSS的预处理器,是CSS的超集。使用 SCSS 可以大大简化 CSS 的编写过程,提高样式表的可维护性和可读性。同时,它还可以通过编译器将 SCSS 代码转换为标准的 CSS 代码,使其可以在浏览器中正常运行。

BEM命名规范

Element Plus 组件库相关技术揭秘:6. CSS 架构模式之 BEM 在组件库中的实践本文通过 OOCSS、B - 掘金

组件库打包

从0搭建Vue3组件库(五): 如何使用Vite打包组件库本篇文章将介绍如何使用 vite 打包我们的组件库,同时告诉大 - 掘金

从0搭建Vue3组件库(七):使用 gulp 打包组件库并实现按需加载使用 glup 打包组件库并实现按需加载 当我们使 - 掘金

发布NPM包:

更换官方源:npm config set registry https://registry.npmjs.org/

登录:npm login

换回淘宝源:npm config set registry https://registry.npmmirror.com