开发组件库从按钮开始,从这个比较简单的组件入手,体验开发、调试、打包、发布的整个流程。组件目录结构为:
├─ examples
├─ packages
│ ├─ components
│ │ ├─ package.json
│ │ ├─ src
│ │ │ ├─ Button
│ │ │ │ ├─ index.scss
│ │ │ │ ├─ index.ts
│ │ │ │ └─ index.vue
│ │ │ ├─ common.css
│ │ │ ├─ constants.ts
│ │ │ └─ index.ts
│ │ ├─ tsconfig.json
└─ └─ └─ vite.config.ts
组件功能介绍
按钮属性props
type
plain
按钮的基本样式;size
按钮尺寸大小;round
slant
circle
按钮的形状;disabled
是否可点击;hoverType
鼠标hover的样式;
按钮事件 emits
click
当props.disabled
为false
时不可点击。
基本样式 type plain
type
包含 default
primary
success
warning
danger
;
plain
为 boolean
类型;
Button/index.vue
根据传入的不同类型增加不同的样式class
:j_button-${props.type}
和is-plain
common.css
定义通用样式及变量;Button/index.css
定义不同class
对应的样式及变量;
组件开发
<script setup lang="ts">
import { computed } from 'vue';
import './index.scss';
const types = ['', 'primary', 'success', 'warning', 'danger'];
const props = defineProps({
"type": {
"type": String,
"values": constants.types,
"default": 'default'
},
"plain": Boolean
});
const styleClass = computed(() => {
return {
[`j_button-${props.type}`]: props.type,
"is_plain": props.plain
};
});
</script>
<script lang="ts">
export default {
"name": 'JButton'
};
</script>
<template>
<button class="j_button" :class="styleClass">
<slot></slot>
</button>
</template>
样式开发
common.css
定义一些通用样式的变量,如颜色、尺寸等。
:root {
--el-font-size-base: 14px;
--j-border-radius-base: 4px;
--j-border-radius-small: 2px;
--j-border-radius-round: 20px;
--j-color-primary: #3f51b5;
--j-color-primary-light: #949dd7;
--j-color-primary-light1: #5766bb;
--j-color-primary-light2: #7986cb;
--j-color-primary-light3: #c0c6ef;
--j-color-primary-light4: #e9eaf6;
--j-color-success: #28b78d;
--j-color-success-light: #87c5b2;
--j-color-success-light1: #61c5a4;
--j-color-success-light2: #83d1b7;
--j-color-success-light3: #caede3;
--j-color-success-light4: #e1f3d8;
--j-color-warning: #cb892e;
--j-color-warning-light: #dfba84;
--j-color-warning-light1: #d6a15c;
--j-color-warning-light2: #fec475;
--j-color-warning-light3: #fed5a2;
--j-color-warning-light4: #ffe5c7;
--j-color-error: #ff5964;
--j-color-error-light: #efaeae;
--j-color-error-light1: #ff7683;
--j-color-error-light2: #ff909c;
--j-color-error-light3: #ffa9b3;
--j-color-error-light4: #fef0f0;
}
Button/index.css
定义按钮的相关变量以及样式,会用到 common.css
的变量。
.j_button {
--j-button-text-color: #606266;
--j-button-bg-color: #fff;
--j-button-border-color: #dcdfe6;
--j-button-hover-text-color: var(--j-color-primary);
--j-button-hover-bg-color: var(--j-color-primary-light4);
--j-button-hover-border-color: var(--j-color-primary);
--j-button-active-bg-color: var(--j-color-primary-light1);
--j-button-active-text-color: var(--j-color-primary);
--j-button-active-border-color: var(--j-color-primary);
--j-button-disabled-text-color: #a8abb2;
--j-button-disabled-bg-color: var(--j-button-bg-color);
--j-button-disabled-border-color: #e4e7ed;
--el-button-size: 32px;
--j-button-border: 1px solid var(--j-button-border-color);
}
.j_button {
border-radius: var(--j-border-radius-base);
border: 1px solid transparent;
padding: 8px 15px;
height: var(--el-button-size);
font-size: var(--el-font-size-base);
background-color: var(--j-button-bg-color);
color: var(--j-button-text-color);
border-color: var(--j-button-border-color);
border: var(--j-button-border);
cursor: pointer;
transition: all 0.25s;
position: relative;
z-index: 0;
}
.j_button:hover,
.j_button::active,
.j_button::focus,
.j_button::focus-visible {
color: var(--j-button-hover-text-color);
border-color: var(--j-button-hover-border-color);
background-color: var(--j-button-hover-bg-color);
outline: 0;
}
type
plain
传入不同的值,会生成不同的 class
,主要是通过控制不同 class
的变量值不同控制按钮的展示样式。👇以primary
类型为例,其余类型控制方式相同,知识色值不同。
// Button/index.css
.j_button.j_button-primary {
--j-button-text-color: #fff;
--j-button-bg-color: var(--j-color-primary);
--j-button-border-color: var(--j-color-primary);
--j-button-hover-text-color: #fff;
--j-button-hover-bg-color: var(--j-color-primary-light1);
--j-button-hover-border-color: var(--j-color-primary-light1);
--j-button-active-bg-color: var(--j-color-primary-light1);
--j-button-active-text-color: #fff;
--j-button-active-border-color: var(--j-color-primary-light1);
--j-button-disabled-text-color: var(--j-color-primary-light);
--j-button-disabled-bg-color: var(--j-color-primary-light4);
--j-button-disabled-border-color: var(--j-color-primary-light2);
}
.j_button.j_button-primary.is_plain {
--j-button-bg-color: var(--j-color-primary-light3);
--j-button-text-color: var(--j-color-primary);
--j-button-hover-bg-color: var(--j-color-primary);
}
size 尺寸
size
包含 default
small
large
三种尺寸;
根据传入不同的 size
生成不同的样式class
: j_button-${props.size}
// Button/index.vue
const props = defineProps({
"size": {
"type": String,
"value": constants.componentSizes
}
});
const styleClass = computed(() => {
return {
[`j-button-${props.size}`]: props.size
};
});
// Button/index.css
.j_button.j-button-small {
--el-button-size: 24px;
padding: 5px 11px;
font-size: 12px;
}
.j_button.j-button-large {
--el-button-size: 40px;
padding: 12px 19px;
}
round
slant
circle
按钮的形状
round
胶囊形、 circle
圆形、slant
平行四边形;接受参数类型均为Boolean
类型。
// Button/index.vue
const props = defineProps({
"round": Boolean,
"slant": Boolean,
"circle": Boolean
});
const styleClass = computed(() => {
return {
"is_round": props.round,
"is_slant": props.slant,
"is_circle": props.circle
};
});
// Button/index.css
.j_button.is_round {
border-radius: var(--j-border-radius-round);
}
.j_button.is_circle {
border-radius: 50%;
padding: 8px;
}
.j_button.is_slant {
border: transparent;
background-color: transparent;
}
.j_button.is_slant::before {
position: absolute;
top: 0;
left: 0;
z-index: -1;
height: 100%;
content: '';
transform: skewX(20deg);
width: 100%;
border: var(--j-button-border);
box-sizing: border-box;
background-color: var(--j-button-bg-color);
transition: box-shadow .2s cubic-bezier(.02,.01,.47,1);
}
.j_button.is_slant:hover {
border: transparent;
background-color: transparent;
}
.j_button.is_slant:hover::before,
.j_button.is_slant:active::before,
.j_button.is_slant:focus::before,
.j_button.is_slant:focus-visible::before {
border-color: var(--j-button-hover-border-color);
background-color: var(--j-button-hover-bg-color);
}
disabled 不可点击
disabled
同样接受 Boolean 类型参数,css
设置不可点击的色值变量和鼠标样式。
// Button/index.vue
const props = defineProps({
"disabled": Boolean
});
const styleClass = computed(() => {
return {
"is_disabled": props.disabled
};
});
// Button/index.css
.j_button.is_disabled {
--j-button-text-color: var(--j-button-disabled-text-color);
--j-button-bg-color: var(--j-button-disabled-bg-color);
--j-button-border-color: var(--j-button-disabled-border-color);
--j-button-hover-text-color: var(--j-button-text-color);
--j-button-hover-bg-color: var(--j-button-bg-color);
--j-button-hover-border-color: var(--j-button-border-color);
}
.j_button.is_disabled {
cursor: not-allowed;
}
hoverType hover样式
hoverType
包含 fill
fill-pill
fill-unite
。
主要是通过控制:before
:after
样式实现的hover
效果。
本地调试
导出组件
- 按钮组件导出。
// packages/components/Button/index.ts
import { App } from 'vue';
import JButton from './index.vue';
JButton.install = (app: App) => {
app.component(JButton.name, JButton);
};
export default JButton;
- 导出整个组件库,支持全局导入和单个组件导入
// packages/components/index.ts
import { App, Plugin } from 'vue';
import JButton from './Button';
import JIcon from './Icon';
const components = {
JButton,
JIcon
};
const UnclePlugin: Plugin = {
install(app: App) {
Object.values(components).forEach(item => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
app.use(item);
});
}
};
export default UnclePlugin;
export { default as JButton } from './Button';
export { default as JIcon } from './Icon';
- 修改入口文件,改为
index.ts
// packages/components/package.json
"main": "./src/index.ts"
本地调用
examples
目录下存放的是组件示例,引入本地开发的
- 安装依赖
@unclejia/components
pnpm --filter @unclejia/examples install @unclejia/components
跟目录下配置了
workspaces
所以本地安装@unclejia/components
安装包链接到路径packages/components
- 使用组件
// examples/main.ts
import { createApp } from 'vue';
import './style.css';
import App from './App.vue';
import { JButton } from '@unclejia/components';
createApp(App)
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
.use(JButton)
.mount('#app');
// Home.vue
<j-button>Default</j-button>
- 启动项目
pnpm run dev
跟目录下 package.json
配置过 "dev": "pnpm run -C examples dev",
启动的项目为 examples
打包组件库
将来 @unclejia/components
会发布到 npm
上,发布的包就是打包之后的lib
,下面来看一下本地使用打包后的组件库需要做的修改有哪些。
- 使用
vite
打包,首先安装依赖
pnpm --filter @unclejia/components install vite @vitejs/plugin-vue -D
package.json
增加一个运行脚本
"build": "vue-tsc && vite build"
- 创建配置文件
vite.config.ts
打包成库模式
// packages/components/vite.config.ts
import { resolve } from 'path';
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
"plugins": [vue()],
"build": {
// 指定输出路径为 lib
"outDir": 'lib',
// 库模式构建配置
"lib": {
"entry": resolve(__dirname, './src/index.ts'),
"name": '@unclejia/components',
"formats": ['es'],
"fileName": (format) => `unclejia-components.${format}.js`
},
"rollupOptions": {
"external": ['vue'],
"output": {
// 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
"globals": {
"vue": 'Vue'
}
}
},
"emptyOutDir": false // 确保在执行tsc的时候生成types
}
});
- 修改组件库入口 main 和 类型文件 types
// packages/components/package.json
"main": "./lib/unclejia-components.es.js",
"types": "./lib/types/index.d.ts",
examples
引入样式文件
// examples/src/main.ts
import '@unclejia/components/lib/style.css';
发布
首先在 npm 官网注册账号。如果使用了@XX/XXX (例如@unclejia/components)则需要在 npm 官网新建一个名为 unclejia
的组织。
将所有代码都提交到 git
仓库,执行发布命令 pnpm publish
第一次发布会要求输入 npm 官网注册的账号密码。
@unclejia/components
包仅发布组件库代码,所以发布时需要先进入到组件库文件夹内。
cd packages/components pnpm publish --access public cd -
发布完成后可以在 npm 中找到发布的包 www.npmjs.com/package/@un…
按钮预览 Button