使用 Vite 和 TypeScript 从零打造一个属于自己的 Vue3 组件库
前言
随着前端技术的发展,业界涌现出了许多的UI组件库。例如我们熟知的ElementUI,Vant,AntDesign等等。但是作为一个前端开发者,你知道一个UI组件库是如何被打造出来的吗?
读完这篇文章你将学会:
如何使用pnpm搭建出一个Monorepo环境
如何使用vite搭建一个基本的Vue3脚手架项目
如何开发调试一个自己的UI组件库
如何使用vite打包并发布自己的UI组件库
作为一个前端拥有一个属于自己的UI组件库是一件非常酷的事情。它不仅能让我们对组件的原理有更深的理解,还能在找工作的时候为自己增色不少。试问有哪个前端不想拥有一套属于自己的UI组件库呢?
本文将使用Vue3和TypeScript来编写一个组件库,使用Vite+Vue3来对这个组件库中的组件进行调试,最后使用vite来对组件库进行打包并且发布到npm上。最终的产物是一个名为kitty-ui的组件库。
话不多说~ 接下来让我们开始搭建属于我们自己的UI组件库吧
Monorepo环境
首先我们要了解什么是menorepo及它是如何搭建的吧
就是指在一个大的项目仓库中,管理多个模块/包(package),这种类型的项目大都在项目根目录下有一个packages文件夹,分多个项目管理。大概结构如下:
-- packages
-- pkg1
--package.json
--pkg2
--package.json
--package.json
复制代码
简单来说就是单仓库 多项目
目前很多我们熟知的项目都是采用这种模式,如Vant,ElementUI,Vue3等。打造一个menorepo环境的工具有很多,如:lerna、pnpm、yarn等,这里我们将使用pnpm来开发我们的UI组件库。
为什么要使用pnpm?
因为它简单高效,它没有太多杂乱的配置,它相比于lerna操作起来方便太多
好了,下面我们就开始用pnpm来进行我们的组件库搭建吧
使用pnpm
安装
1 npm install pnpm -g
复制代码
初始化package.json
1pnpm init
复制代码
新建配置文件 .npmrc
1shamefully-hoist = true
复制代码
这里简单说下为什么要配置shamefully-hoist。
如果某些工具仅在根目录的node_modules时才有效,可以将其设置为true来提升那些不在根目录的node_modules,就是将你安装的依赖包的依赖包的依赖包的...都放到同一级别(扁平化)。说白了就是不设置为true有些包就有可能会出问题。
monorepo的实现
接下就是pnpm如何实现monorepo的了。
为了我们各个项目之间能够互相引用我们要新建一个pnpm-workspace.yaml文件将我们的包关联起来
packages:
- 'packages/**'
- 'examples'
这样就能将我们项目下的packages目录和examples目录关联起来了,当然如果你想关联更多目录你只需要往里面添加即可。根据上面的目录结构很显然你在根目录下新packages和examples文件夹,packages文件夹存放我们开发的包,examples用来调试我们的组件
examples文件夹就是接下来我们要使用vite搭建一个基本的Vue3脚手架项目的地方
安装对应依赖
我们开发环境中的依赖一般全部安装在整个项目根目录下,方便下面我们每个包都可以引用,所以在安装的时候需要加个 -w
1pnpm i vue@next typescript less -D -w
复制代码
因为我们开发的是vue3组件, 所以需要安装vue3,当然ts肯定是必不可少的(当然如果你想要js开发也是可以的,甚至可以省略到很多配置和写法。但是ts可以为我们组件加上类型,并且使我们的组件有代码提示功能,未来ts也将成为主流);less为了我们写样式方便,以及使用它的命名空间(这个暂时这里没用到,后面有时间再补
配置tsconfit.json
这里的配置就不细说了,可以自行搜索都是代表什么意思。或者你可以先直接复制
1npx tsc --init
复制代码
tsconfig.json:
1{
2 "compilerOptions": {
3 "baseUrl": ".",
4 "jsx": "preserve",
5 "strict": true,
6 "target": "ES2015",
7 "module": "ESNext",
8 "skipLibCheck": true,
9 "esModuleInterop": true,
10 "moduleResolution": "Node",
11 "lib": ["esnext", "dom"]
12 }
13}
复制代码
手动搭建一个基于vite的vue3项目
其实搭建一个vite+vue3项目是非常容易的,因为vite已经帮我们做了大部分事情
初始化仓库
进入examples文件夹,执行
1pnpm init
复制代码
安装vite和@vitejs/plugin-vue
@vitejs/plugin-vue用来支持.vue文件的转译
1pnpm install vite @vitejs/plugin-vue -D -w
复制代码
这里安装的插件都放在根目录下
配置vite.config.ts
新建vite.config.ts
1import { defineConfig } from 'vite'
2import vue from '@vitejs/plugin-vue'
3
4export default defineConfig({
5 plugins:[vue()]
6})
复制代码
新建html文件
@vitejs/plugin-vue 会默认加载examples下的index.html
新建index.html
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta http-equiv="X-UA-Compatible" content="IE=edge">
6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <title>Document</title>
8</head>
9<body>
10 <div id="app"></div>
11 <script src="main.ts" type="module"></script>
12</body>
13</html>
复制代码
注意: vite 是基于esmodule的 所以type="module"
新建app.vue模板
1<template>
2 <div>
3 启动测试
4 </div>
5</template>
复制代码
新建main.ts
1import {createApp} from 'vue'
2import App from './app.vue'
3
4const app = createApp(App)
5
6app.mount('#app')
复制代码
此时会发现编译器会提示个错误:找不到模块“./app.vue”或其相应的类型声明
因为直接引入.vue文件 TS会找不到对应的类型声明;所以需要新建typings(命名没有明确规定,TS会自动寻找.d.ts文件)文件夹来专门放这些声明文件。
1typings/vue-shim.d.ts
TypeScriptTS默认只认ES 模块。如果你要导入.vue文件就要declare module把他们声明出来。
1declare module '*.vue' {
2 import type { DefineComponent } from "vue";
3 const component:DefineComponent<{},{},any>
4}
配置脚本启动项目
最后在package.json文件中配置scripts脚本
1"scripts": {
2 "dev": "vite"
3 },
然后终端输入我们熟悉的命令:
1pnpm run dev
vite启动默认端口为3000;在浏览器中打开localhost:3000 就会看我们的“启动测试”页面。
本地调试
新建包文件
本节可能和目前组件的开发关联不大,但是未来组件需要引入一些工具方法的时候会用到
接下来就是要往我们的packages文件夹冲填充内容了。
utils包
一般packages要有utils包来存放我们公共方法,工具函数等
既然它是一个包,所以我们新建utils目录后就需要初始化它,让它变成一个包;终端进入utils文件夹执行:pnpm init 然后会生成一个package.json文件;这里需要改一下包名,我这里将name改成@kitty-ui/utils表示这个utils包是属于kitty-ui这个组织下的。所以记住发布之前要登录npm新建一个组织;例如kitty-ui
1{
2 "name": "@kitty-ui/utils",
3 "version": "1.0.0",
4 "description": "",
5 "main": "index.ts",
6 "scripts": {
7 "test": "echo "Error: no test specified" && exit 1"
8 },
9 "keywords": [],
10 "author": "",
11 "license": "ISC"
12}
复制代码
因为我们使用ts写的,所以需要将入口文件index.js改为index.ts,并新建index.ts文件:(先导出一个简单的加法函数)
1export const testfun = (a:number,b:number):number=>{
2 return a + b
3}
复制代码
组件库包(这里命名为kitty-ui)
components是我们用来存放各种UI组件的包
新建components文件夹并执行 pnpm init 生成package.json
1{
2 "name": "kitty-ui",
3 "version": "1.0.0",
4 "description": "",
5 "main": "index.ts",
6 "scripts": {
7 "test": "echo "Error: no test specified" && exit 1"
8 },
9 "keywords": [],
10 "author": "",
11 "license": "ISC"
12}
复制代码
新建index.ts入口文件并引入utils包
1import {testfun} from '@kitty-ui/utils'
2
3const result = testfun (1,1)
4
5console.log(result)
由于组件库是基于ts的,所以需要安装esno来执行ts文件便于测试组件之间的引入情况
控制台输入esno xxx.ts即可执行ts文件
1npm i esno -g
复制代码
包之间本地调试
进入components文件夹执行
1pnpm install @kitty-ui/utils
复制代码
你会发现pnpm会自动创建个软链接直接指向我们的utils包;此时components下的packages:
1{
2 "name": "kitty-ui",
3 "version": "1.0.0",
4 "description": "",
5 "main": "src/index.ts",
6 "scripts": {
7 "test": "echo "Error: no test specified" && exit 1"
8 },
9 "keywords": [],
10 "author": "",
11 "license": "ISC",
12 "dependencies": {
13 "@kitty-ui/utils": "workspace:^1.0.1"
14 }
15}
复制代码
你会发现它的依赖@kitty-ui/utils对应的版本为:workspace:^1.0.0;因为pnpm是由workspace管理的,所以有一个前缀workspace可以指向utils下的工作空间从而方便本地调试各个包直接的关联引用。
到这里基本开发方法我们已经知道啦;接下来就要进入正题了,开发一个button组件
试着开发一个button组件
在components文件夹下新建src,同时在src下新建button组件目录和icon组件目录(新建icon为了便于调试);此时components文件目录如下
-- components
-- src
-- button
-- icon
-- index.ts
-- package.json
复制代码
让我们先测试一下我们的button组件能否在我们搭建的examples下的vue3项目本引用~
首先在button下新建一个简单的button.vue
<template>
<button>测试按钮</button>
</template>
复制代码
然后在button/index.ts将其导出
1import Button from './button.vue'
2
3export default Button
复制代码
因为我们开发组件库的时候不可能只有button,所以我们需要一个components/index.ts将我们开发的组件一个个的集中导出
1import Button from './button'
2
3export {
4 Button
5}
复制代码
好了,一个组件的大体目录差不多就是这样了,接下来请进入我们的examples来看看能否引入我们的button组件
vue3项目使用button
上面已经说过执行在workspace执行 pnpm i xxx的时候pnpm会自动创建个软链接直接指向我们的xxx包。
所以这里我们直接在examples执行:pnpm i kitty-ui
此时你就会发现packages.json的依赖多了个
1"kitty-ui": "workspace:^1.0.0"
复制代码
这时候我们就能直接在我们的测试项目下引入我们本地的components组件库了,启动我们的测试项目,来到我们的 examples/app.vue 直接引入Button
1<template>
2 <div>
3 <Button />
4 </div>
5</template>
6<script lang="ts" setup>
7import { Button } from 'kitty-ui'
8</script>
复制代码
不出意外的话你的页面就会展示我们刚刚写的button组件了
好了万事具...(其实还差个打包,这个后面再说~);接下来的工作就是专注于组件的开发了;让我们回到我们的button组件目录下(测试页面不用关,此时我们已经可以边开发边调试边看效果了)
因为我们的button组件是需要接收很多属性的,如type、size等等,所以我们要新建个types.ts文件来规范这些属性
在button目录下新建types.ts
1
2import { ExtractPropTypes } from 'vue'
3
4
5export const ButtonType = ['default', 'primary', 'success', 'warning', 'danger']
6
7export const ButtonSize = ['large', 'normal', 'small', 'mini'];
8
9
10export const buttonProps = {
11 type: {
12 type: String,
13 values: ButtonType
14 },
15 size: {
16 type: String,
17 values: ButtonSize
18 }
19}
20
21export type ButtonProps = ExtractPropTypes<typeof buttonProps>
22
23
TIPS
import type 表示只导入类型;ExtractPropTypes是vue3中内置的类型声明,它的作用是接收一个类型,然后把对应的vue3所接收的props类型提供出来,后面有需要可以直接使用
很多时候我们在vue中使用一个组件会用的app.use 将组件挂载到全局。要使用app.use函数的话我们需要让我们的每个组件都提供一个install方法,app.use()的时候就会调用这个方法;
我们将button/index.ts调整为
1import button from './button.vue'
2import type {App,Plugin} from "vue"
3type SFCWithInstall<T> = T&Plugin
4const withInstall = <T>(comp:T) => {
5 (comp as SFCWithInstall<T>).install = (app:App)=>{
6 //注册组件
7 app.component((comp as any).name,comp)
8 }
9 return comp as SFCWithInstall<T>
10}
11const Button = withInstall(button)
12export default Button
此时我们就可以使用app.use来挂载我们的组件啦
其实withInstall方法可以做个公共方法放到工具库里,因为后续每个组件都会用到,这里等后面开发组件的时候再调整
到这里组件开发的基本配置已经完成,最后我们对我们的组件库以及工具库进行打包,打包之前如果要发公共包的话记得将我们的各个包的协议改为MIT开源协议
1"license": "MIT",
复制代码
vite打包
配置文件
打包们这里选择vite,它有一个库模式专门为我们来打包这种库组件的。
前面已经安装过vite了,所以这里直接在components下直接新建vite.config.ts(配置参数文件中已经注释):
1import { defineConfig } from "vite";
2import vue from "@vitejs/plugin-vue"
3export default defineConfig(
4 {
5 build: {
6 target: 'modules',
7 //打包文件目录
8 outDir: "es",
9 //压缩
10 minify: false,
11 //css分离
12 //cssCodeSplit: true,
13 rollupOptions: {
14 //忽略打包vue文件
15 external: ['vue'],
16 input: ['src/index.ts'],
17 output: [
18 {
19 format: 'es',
20 //不用打包成.es.js,这里我们想把它打包成.js
21 entryFileNames: '[name].js',
22 //让打包目录和我们目录对应
23 preserveModules: true,
24 //配置打包根目录
25 dir: 'es',
26 preserveModulesRoot: 'src'
27 },
28 {
29 format: 'cjs',
30 entryFileNames: '[name].js',
31 //让打包目录和我们目录对应
32 preserveModules: true,
33 //配置打包根目录
34 dir: 'lib',
35 preserveModulesRoot: 'src'
36 }
37 ]
38 },
39 lib: {
40 entry: './index.ts',
41 formats: ['es', 'cjs']
42 }
43 },
44 plugins: [
45 vue()
46 ]
47 }
48)
这里我们选择打包cjs(CommonJS)和esm(ESModule)两种形式,cjs模式主要用于服务端引用(ssr),而esm就是我们现在经常使用的方式,它本身自带treeShaking而不需要额外配置按需引入(前提是你将模块分别导出),非常好用~
其实到这里就已经可以直接打包了;components下执行:pnpm run build你就会发现打包了es和lib两个目录
到这里其实打包的组件库只能给js项目使用,在ts项目下运行会出现一些错误,而且使用的时候还会失去代码提示功能,这样的话我们就失去了用ts开发组件库的意义了。所以我们需要在打包的库里加入声明文件(.d.ts)。
那么如何向打包后的库里加入声明文件呢?其实很简单,只需要引入
vite-plugin-dts
1pnpm i vite-plugin-dts -D -w
复制代码
然后修改一下我们的vite.config.ts引入这个插件
1import { defineConfig } from "vite";
2import vue from "@vitejs/plugin-vue"
3import dts from 'vite-plugin-dts'
4
5export default defineConfig(
6 {
7 build: {...},
8 plugins: [
9 vue(),
10 dts({
11 //指定使用的tsconfig.json为我们整个项目根目录下掉,如果不配置,你也可以在components下新建tsconfig.json
12 tsConfigFilePath: '../../tsconfig.json'
13 }),
14 //因为这个插件默认打包到es下,我们想让lib目录下也生成声明文件需要再配置一个
15 dts({
16 outputDir:'lib',
17 tsConfigFilePath: '../../tsconfig.json'
18 })
19
20 ]
21 }
22)
因为这个插件默认打包到es下,我们想让lib目录下也生成声明文件需要再配置一个dts插件,暂时没有想到其它更好的处理方法~
然后执行打包命令你就会发现你的es和lib下就有了声明文件
其实后面就可以进行发布了,发布之前更改一下我们components下的package.json如下:
1{
2 "name": "kitty-ui",
3 "version": "1.0.0",
4 "main": "lib/index.js",
5 "module":"es/index.js",
6 "scripts": {
7 "build": "vite build"
8 },
9 "files": [
10 "es",
11 "lib"
12 ],
13 "keywords": [
14 "kitty-ui",
15 "vue3组件库"
16 ],
17 "author": "小月",
18 "license": "MIT",
19 "description": "",
20 "typings": "lib/index.d.ts"
21}
解释一下里面部分字段
pkg.module
我们组件库默认入口文件是传统的CommonJS模块,但是如果你的环境支持ESModule的话,构建工具会优先使用我们的module入口
pkg.files
files是指我们1需要发布到npm上的目录,因为不可能components下的所有目录都被发布上去
开始发布
做了那么多终于到发布的阶段了;其实npm发包是很容易的,就拿我们的组件库kitty-ui举例吧
发布之前记得到npm[1]官网注册个账户,如果你要发布@xx/xx这种包的话需要在npm新建个组织组织组织名就是@后面的,比如我建的组织就是kitty-ui,注册完之后你就可以发布了
首先要将我们代码提交到git仓库,不然pnpm发布无法通过,后面每次发版记得在对应包下执行 pnpm version patch你就会发现这个包的版本号patch(版本号第三个数) +1 了,同样的 pnpm version major major和 pnpm version minor 分别对应版本号的第一和第二位增加。
如果你发布的是公共包的话,在对应包下执行
1pnpm publish --access public
输入你的账户和密码(记得输入密码的时候是不显示的,不要以为卡了)正常情况下应该是发布成功了
注意
发布的时候要将npm的源切换到npm的官方地址(registry.npmjs.org/[2]); 如果你使用了其它镜像源的话
样式问题
引入我们打包后的组件你会发现没有样式,所以你需要在全局引入我们的style.css才行;如 main.ts中需要
1import 'kitty-ui/es/style.css';
很显然这种组件库并不是我们想要的,我们需要的组件库是每个css样式放在每个组件其对应目录下,这样就不需要每次都全量导入我们的css样式。
下面就让我们来看下如何把样式拆分打包
处理less文件
首先我们需要做的是将less打包成css然后放到打包后对应的文件目录下,我们在components下新建build文件夹来存放我们的一些打包工具,然后新建buildLess.ts,首先我们需要先安装一些工具cpy和fast-glob
1pnpm i cpy fast-glob -D -w
复制代码
cpy
它可以直接复制我们规定的文件并将我们的文件copy到指定目录,比如buildLess.ts:
1import cpy from 'cpy'
2import { resolve } from 'path'
3
4const sourceDir = resolve(__dirname, '../src')
5//lib文件
6const targetLib = resolve(__dirname, '../lib')
7//es文件
8const targetEs = resolve(__dirname, '../es')
9console.log(sourceDir);
10const buildLess = async () => {
11 await cpy(`${sourceDir}/**/*.less`, targetLib)
12 await cpy(`${sourceDir}/**/*.less`, targetEs)
13}
14buildLess()
复制代码
然后在package.json中新增命令
1"scripts": {
2 "build": "vite build",
3 "build:less": "esno build/buildLess"
4 },
终端执行 pnpm run build:less 你就会发现lib和es文件对应目录下就出现了less文件.
但是我们最终要的并不是less文件而是css文件,所以我们要将less打包成css,所以我们需要用的less模块.在ts中引入less因为它本身没有声明文件所以会出现类型错误,所以我们要先安装它的 @types/less
1pnpm i --save-dev @types/less -D -w
复制代码
buildLess.ts如下(详细注释都在代码中)
1import cpy from 'cpy'
2import { resolve, dirname } from 'path'
3import { promises as fs } from "fs"
4import less from "less"
5import glob from "fast-glob"
6const sourceDir = resolve(__dirname, '../src')
7//lib文件目录
8const targetLib = resolve(__dirname, '../lib')
9//es文件目录
10const targetEs = resolve(__dirname, '../es')
11
12//src目录
13
14const srcDir = resolve(__dirname, '../src')
15
16const buildLess = async () => {
17 //直接将less文件复制到打包后目录
18 await cpy(`${sourceDir}/**/*.less`, targetLib)
19 await cpy(`${sourceDir}/**/*.less`, targetEs)
20
21 //获取打包后.less文件目录(lib和es一样)
22 const lessFils = await glob("**/*.less", { cwd: srcDir, onlyFiles: true })
23
24 //遍历含有less的目录
25 for (let path in lessFils) {
26
27 const filePath = `${srcDir}/${lessFils[path]}`
28 //获取less文件字符串
29 const lessCode = await fs.readFile(filePath, 'utf-8')
30 //将less解析成css
31
32 const code = await less.render(lessCode, {
33 //指定src下对应less文件的文件夹为目录
34 paths: [srcDir, dirname(filePath)]
35 })
36
37 //拿到.css后缀path
38 const cssPath = lessFils[path].replace('.less', '.css')
39
40
41 //将css写入对应目录
42 await fs.writeFile(resolve(targetLib, cssPath), code.css)
43 await fs.writeFile(resolve(targetEs, cssPath), code.css)
44 }
45
46
47
48}
49buildLess()
50
执行打包命令之后你会发现对应文件夹下多了.css文件
现在我已经将css文件放入对应的目录下了,但是我们的相关组件并没有引入这个css文件;所以我们需要的是每个打包后组件的index.js中出现如:
1import "xxx/xxx.css"
复制代码
之类的代码我们的css才会生效;所以我们需要对vite.config.ts进行相关配置
首先我们先将.less文件忽略**external: ['vue', /.less/],**这时候打包后的文件中如button/index.js就会出现
1import "./style/index.less";
复制代码
然后我们再将打包后代码的.less换成.css就大功告成了
1plugins: [
2 ...
3
4 {
5 name: 'style',
6 generateBundle(config, bundle) {
7 //这里可以获取打包后的文件目录以及代码code
8 const keys = Object.keys(bundle)
9
10 for (const key of keys) {
11 const bundler: any = bundle[key as any]
12 //rollup内置方法,将所有输出文件code中的.less换成.css,因为我们当时没有打包less文件
13
14 this.emitFile({
15 type: 'asset',
16 fileName: key,//文件名名不变
17 source: bundler.code.replace(/\.less/g, '.css')
18 })
19 }
20 }
21 }
22 ...
23 ]
复制代码
我们最终的vite.config.ts如下
1import { defineConfig } from "vite";
2import vue from "@vitejs/plugin-vue"
3import dts from 'vite-plugin-dts'
4
5export default defineConfig(
6 {
7 build: {
8 target: 'modules',
9 //打包文件目录
10 outDir: "es",
11 //压缩
12 minify: false,
13 //css分离
14 //cssCodeSplit: true,
15 rollupOptions: {
16 //忽略打包vue和.less文件
17 external: ['vue', /\.less/],
18 input: ['src/index.ts'],
19 output: [
20 {
21 format: 'es',
22 //不用打包成.es.js,这里我们想把它打包成.js
23 entryFileNames: '[name].js',
24 //让打包目录和我们目录对应
25 preserveModules: true,
26 //配置打包根目录
27 dir: 'es',
28 preserveModulesRoot: 'src'
29 },
30 {
31 format: 'cjs',
32 //不用打包成.mjs
33 entryFileNames: '[name].js',
34 //让打包目录和我们目录对应
35 preserveModules: true,
36 //配置打包根目录
37 dir: 'lib',
38 preserveModulesRoot: 'src'
39 }
40 ]
41 },
42 lib: {
43 entry: './index.ts',
44 formats: ['es', 'cjs']
45 }
46 },
47
48
49
50
51 plugins: [
52 vue(),
53 dts({
54 //指定使用的tsconfig.json为我们整个项目根目录下掉,如果不配置,你也可以在components下新建tsconfig.json
55 tsConfigFilePath: '../../tsconfig.json'
56 }),
57 //因为这个插件默认打包到es下,我们想让lib目录下也生成声明文件需要再配置一个
58 dts({
59 outputDir: 'lib',
60 tsConfigFilePath: '../../tsconfig.json'
61 }),
62
63 {
64 name: 'style',
65 generateBundle(config, bundle) {
66 //这里可以获取打包后的文件目录以及代码code
67 const keys = Object.keys(bundle)
68
69 for (const key of keys) {
70 const bundler: any = bundle[key as any]
71 //rollup内置方法,将所有输出文件code中的.less换成.css,因为我们当时没有打包less文件
72
73 this.emitFile({
74 type: 'asset',
75 fileName: key,//文件名名不变
76 source: bundler.code.replace(/\.less/g, '.css')
77 })
78 }
79 }
80 }
81
82 ]
83 }
84)
85
最后我们将打包less与打包组件合成一个命令(package.json):
1"scripts": {
2 "build": "vite build & esno build/buildLess"
3 },
后续直接执行pnpm run build
即可完成所有打包啦
直接使用
如果你不想一步步的搭建,想直接使用现成的话,你可以直接把项目clone下来-> kittyui[3],然后你只需要以下几步便可将其完成
安装pnpm npm i pnpm -g
安装esno npm i esno -g
安装所有依赖 pnpm install
本地测试 进入examples文件夹执行 pnpm run dev
启动vue3项目
打包 pnpm run build
写在最后
由于作者水平有限,难免会存在一些错误或不妥之处,希望各位能够不吝指出,一定及时修改。如果你对这个项目有更好的想法或者建议也欢迎在评论区提出,不胜感激。
后续我会对一些常用组件进行开发,每个组件的开发都会以文章的形式展现出来以供大家参考