我正在参加「初夏创意投稿大赛」详情请看:初夏创意投稿大赛
其实关于封装个人UI组件库不少大佬们早就已经开始了,我是属于一直再打算但是从未开始。也不属于口嗨,顶多是想象嗨~ 不过,虽迟但到。我还是来蹭一波~
首先是技术栈,要学要用就用最新的哈~ 那就是vite + vue3 + ts(其实也省去了对一些打包以及发布的配置,偷个懒~ )。哈哈,我是跟着掘金一个大佬的文章搭建的,vue3 + vite搭建流程。没有项目或者需要搭建vue3新项目的自行去搭建哈~
创建完新项目后,建议尽量将新项目自带的页面以及没用的组件删除掉。
组件代码
-
第一步:在根目录下创建
packages文件夹。 -
第二步:在
packages文件夹下新建一个自己的组件文件夹名为button。其中要提供两个文件分别为index.ts以及index.vue。index.vue文件则是放我们自己所需要封装的组件;index.ts是作为组件的一个出口,将同一目录下的index.vue引入注册并导出,这样该组件则可以被按需引入。需要注意的是:
index.vue组件中必须要写name名字,否则注册时利用name则找不到该组件。(该
button组件代码,非原创网上找的)
<template>
<button class="xy-button" :class="classes">
<span v-if="loading" class="xy-loadingIndicator"></span>
<slot></slot>
</button>
</template>
<script>
export default {
name: 'xyButton' // 必须要写名字
}
</script>
<script setup>
import { computed } from 'vue';
const props = defineProps({
theme: {// 按钮形态
type: String,
default:"default" // primary, info, success, warning, error
},
dashed: { // 是否为虚线
type: Boolean,
default: false
},
size: { // button大小 small、large
type: String,
default: "default"
},
round: { // 是否有圆角
type: Boolean,
default: false
},
disabled: { // 是否禁用
type: Boolean,
default: false
},
loading: { // 是否为loading状态
type: Boolean,
default: false
}
});
const { theme, dashed, size, round, disabled } = props;
const classes = computed(() => {
return {
[`xy-theme-${theme}`]: theme,
[`xy-theme-dashed`]: dashed,
[`xy-theme-${size}`]: size,
[`is-round`]: round,
[`is-disabled`]: disabled,
}
})
</script>
<style lang="scss">
$h-default: 32px;
$h-small: 20px;
$h-large: 48px;
$white: #fff;
$default-color: #333;
$primary-color: #36ad6a;
$info-color: #4098fc;
$success-color: #85ce61;
$warning-color: #f0a020;
$error-color: #d03050;
$grey: grey;
$default-border-color: #d9d9d9;
$radius: 3px;
$green: #18a058;
.xy-button {
box-sizing: border-box;
height: $h-default;
background-color: #fff;
padding: 0 12px;
cursor: pointer;
display: inline-flex;
justify-content: center;
align-items: center;
white-space: nowrap;
border-radius: $radius;
box-shadow: 0 1px 0 fade-out(black, 0.95);
transition: all 250ms;
color: $default-color;
border: 1px solid $default-border-color;
user-select: none;
&:focus {
outline: none;
}
&::-moz-focus-inner {
border: 0;
}
&.xy-size-large {
font-size: 24px;
height: $h-large;
padding: 0 16px;
}
&.xy-size-small {
font-size: 12px;
height: $h-small;
padding: 0 8px;
}
&.is-round.xy-size-default {
border-radius: calc($h-default / 2);
}
&.is-round.xy-size-large {
border-radius: calc($h-large / 2);
}
&.is-round.xy-size-small {
border-radius: calc($h-small / 2);
}
// button形态
// 默认
&.xy-theme-default {
&:hover {
color: $green;
border-color: $green;
> .xy-loadingIndicator {
border-style: dashed;
border-color: $green $green $green transparent;
}
}
&:active {
color: darken($green, 20%);
border-color: darken($green, 20%);
> .xy-loadingIndicator {
border-style: dashed;
border-color: darken($green, 20%) darken($green, 20%)
darken($green, 20%) transparent;
}
}
&.xy-theme-dashed {
border-style: dashed;
}
> .xy-loadingIndicator {
border-style: dashed;
// border-color: $default-color $default-color $default-color transparent;
border-color: $white $white $white transparent;
}
}
// 常规
&.xy-theme-primary {
background-color: $primary-color;
border-color: $primary-color;
color: $white;
&:hover {
background: lighten($primary-color, 20%);
border-color: lighten($primary-color, 20%);
}
&:active {
background-color: darken($primary-color, 20%);
border-color: darken($primary-color, 20%);
}
&.is-disabled {
cursor: not-allowed;
background: lighten($primary-color, 20%);
border-color: lighten($primary-color, 20%);
&:hover {
background: lighten($primary-color, 20%);
border-color: lighten($primary-color, 20%);
}
}
&.xy-theme-dashed {
border-style: dashed;
background-color: $white !important;
color: $primary-color;
> .xy-loadingIndicator {
border-style: dashed;
border-color: $primary-color $primary-color $primary-color transparent;
}
}
}
// 消息
&.xy-theme-info {
background-color: $info-color;
border-color: $info-color;
color: $white;
&:hover {
background: lighten($info-color, 20%);
border-color: lighten($info-color, 20%);
}
&:active {
background-color: darken($info-color, 20%);
border-color: darken($info-color, 20%);
}
&.is-disabled {
cursor: not-allowed;
background: lighten($info-color, 20%);
border-color: lighten($info-color, 20%);
&:hover {
background: lighten($info-color, 20%);
border-color: lighten($info-color, 20%);
}
}
&.xy-theme-dashed {
border-style: dashed;
background-color: $white !important;
color: $info-color;
> .xy-loadingIndicator {
border-style: dashed;
border-color: $info-color $info-color $info-color transparent;
}
}
}
// 成功
&.xy-theme-success {
background-color: $success-color;
border-color: $success-color;
color: $white;
&:hover {
background: lighten($success-color, 20%);
border-color: lighten($success-color, 20%);
}
&:active {
background-color: darken($success-color, 20%);
border-color: darken($success-color, 20%);
}
&.is-disabled {
cursor: not-allowed;
background: lighten($success-color, 20%);
border-color: lighten($success-color, 20%);
&:hover {
background: lighten($success-color, 20%);
border-color: lighten($success-color, 20%);
}
}
&.xy-theme-dashed {
border-style: dashed;
background-color: $white !important;
color: $success-color;
> .xy-loadingIndicator {
border-style: dashed;
border-color: $success-color $success-color $success-color transparent;
}
}
}
// 警告
&.xy-theme-warning {
background-color: $warning-color;
border-color: $warning-color;
color: $white;
&:hover {
background: lighten($warning-color, 20%);
border-color: lighten($warning-color, 20%);
}
&:active {
background-color: darken($warning-color, 20%);
border-color: darken($warning-color, 20%);
}
&.is-disabled {
cursor: not-allowed;
background: lighten($warning-color, 20%);
border-color: lighten($warning-color, 20%);
&:hover {
background: lighten($warning-color, 20%);
border-color: lighten($warning-color, 20%);
}
}
&.xy-theme-dashed {
border-style: dashed;
background-color: $white !important;
color: $warning-color;
> .xy-loadingIndicator {
border-style: dashed;
border-color: $warning-color $warning-color $warning-color transparent;
}
}
}
// 错误
&.xy-theme-error {
background-color: $error-color;
border-color: $error-color;
color: $white;
&:hover {
background: lighten($error-color, 20%);
border-color: lighten($error-color, 20%);
}
&:active {
background-color: darken($error-color, 20%);
border-color: darken($error-color, 20%);
}
&.is-disabled {
cursor: not-allowed;
background: lighten($error-color, 20%);
border-color: lighten($error-color, 20%);
&:hover {
background: lighten($error-color, 20%);
border-color: lighten($error-color, 20%);
}
}
&.xy-theme-dashed {
border-style: dashed;
background-color: $white !important;
color: $error-color;
> .xy-loadingIndicator {
border-style: dashed;
border-color: $error-color $error-color $error-color transparent;
}
}
}
> .xy-loadingIndicator {
width: 14px;
height: 14px;
display: inline-block;
margin-right: 4px;
border-radius: 8px;
border-color: $white $white $white transparent;
border-style: solid;
border-width: 2px;
animation: xy-spin 1s infinite linear;
}
}
@keyframes xy-spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
import xyButton from './index.vue';
xyButton.install = (app:any) => {
app.component(xyButton.name, xyButton)
}
export default xyButton;
-
第三步:在
packages文件夹下新建一个index.ts,用来管理这些组件。以便于使用者下载该组件库就可以使用其中所有的组件
import xyButton from "./button/index";
// import annularMenu from "./BAnnularMenu/index";
const install = (app:any) => {
app.use(xyButton)
// pp.use(annularMenu)
};
const JUI = {
install
}
export {
xyButton,
// annularMenu,
}
export default JUI;
如果此时需要本地测试使用,则在main.ts中添加以下代码
import { createApp } from 'vue'
import App from './App.vue'
import JUI from "../packages/index"
const app = createApp(App);
app.use(JUI)
app.mount('#app')
文件配置
在发布之前我们需要对文件进行打包。所以接下来:
-
第一步:配置
vite.config.ts文件。下方代码中build模块注意:此项目语言为
ts,组件中不能包含js文件,会导致打包报错失败。
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
build: {
rollupOptions: {
external: ["vue"],// 打包发布时不需要vue依赖
output: {
globals: {
vue: "Vue"
}
}
},
lib: {
entry: './packages/index.ts', // 入口
name: 'JUI'
}
},
base: './', // 打包路径
server: {
port: 3000, // 服务端口号
open: true, // 服务启动时是否自动打开浏览器
cors: true // 允许跨域
}
})
然后执行 yarn build 则会生成dist文件
- 第二步:对
package.json进行部分配置("scripts"上方处配置)
"name": "xueyou-app",
"private": false,// 第一次发布需要改为false,否则npm无法发布
"version": "0.0.1", // 版本号
// 下面所用为打包生成的文件
"main": "./dist/xueyou-app.umd.js",
"module": "./dist/xueyou-app.es.js",
"exports": {
".": {
"import": "./dist/xueyou-app.es.js",
"require": "./dist/xueyou-app.umd.js"
}
},
"files": [
"dist/*"
],
发布
-
第一步:需要进到
npm的官网进行注册npm; -
第二步:在首次发布组件时需要执行
npm login进行登陆。之后根据提示填写用户名,密码以及邮箱。第一次登陆需要一个实时的验证码,如果没开身份二次验证,则验证码会发送到邮箱。如果有兴趣可以添加身份二次验证About two-factor authentication | npm Docs (npmjs.com); -
第三步:通过以上验证后执行命令
npm publish进行发布
发布后如果需要测试使用,则执行命令yarn add xxx
在main.ts中添加以下代码:
import { createApp } from 'vue'
import App from './App.vue'
import JUI from xxx";
const app = createApp(App);
app.use(JUI)
app.mount('#app')
最后,只要页面中正常引用组件即可
也可以按需引入
需要使用组件的页面:
// 按需引入
import { xyButton } from 'xxx';
只是一个熟悉创建个人UI组件库的大体流程,在此之后如果自己有比较好的组件都可以放进来了。。。