一、格式化目录结构
根据以下图片搭建组件库目录
index.js作为入口文件,将所有组件引入,并注册组件名称
import { EButton } from "./Button";
export * from "./Button";
import { ECard } from "./Card";
export * from "./Card";
const cmpts = [EButton, ECard];
const EricUI = {
install(Vue) {
cmpts.forEach(cmpt => {
Vue.component(cmpt.name, cmpt);
});
},
};
export default EricUI;
utils.js:给组件绑定注册方法
export function withInstall(component) {
component.install = app => {
app.component(component.name, component);
};
return component;
}
在main.js中引入,方便后续使用
import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";
import Antd from "ant-design-vue";
import "ant-design-vue/dist/antd.less";
import EricUI from "../components";
const app = createApp(App).use(Antd).use(EricUI).mount("#app");
在docs\.vitepress\theme\index.ts同样引入
// https://vitepress.dev/guide/custom-theme
import { h } from 'vue'
import type { Theme } from 'vitepress'
import DefaultTheme from 'vitepress/theme'
import Antd from 'ant-design-vue';
import './antd-overwrite.less'
import { AntDesignContainer } from '@vitepress-demo-preview/component'
import '@vitepress-demo-preview/component/dist/style.css'
import './style.css'
import HomeImage from './HomeImage.vue'
import EricUI from "../../../components";
export default {
extends: DefaultTheme,
Layout: () => {
return h(DefaultTheme.Layout, null, {
// https://vitepress.dev/guide/extending-default-theme#layout-slots
'home-hero-image': () => h(HomeImage)
})
},
enhanceApp({ app, router, siteData }) {
app.use(Antd)
app.use(EricUI)
app.component('demo-preview', AntDesignContainer)
}
} satisfies Theme
到此为止,组件库开发的组件可以在docs中展示:
EButton是我们开发的button组件,在Button.md中引入
效果:
二、组件库构建
新建build文件夹,以及以下三个文件:
// base.confi.js
import { fileURLToPath, URL } from "node:url";
import { defineConfig } from "vite";
// 文档: https://vitejs.dev/config/
export default defineConfig({
minify: false,
css: {
preprocessorOptions: {
less: {
javascriptEnabled: true,
modifyVars: {
"ant-prefix": "ant",
},
},
},
},
plugins: [],
resolve: {
alias: {
"@": fileURLToPath(new URL("../src", import.meta.url)),
},
},
});
// lib.config.js
import { defineConfig } from "vite";
import { fileURLToPath, URL } from "node:url";
import vue from "@vitejs/plugin-vue";
import lessEntry from "./vite-plugin-less-entry";
import baseConfig from "./base.config";
import vueJsx from "@vitejs/plugin-vue-jsx";
import { viteStaticCopy } from "vite-plugin-static-copy";
export default defineConfig({
...baseConfig,
build: {
sourcemap: true,
outDir: "lib",
lib: {
entry: fileURLToPath(new URL("../components/index.js", import.meta.url)),
name: "EricUI",
fileName: format => `eric-ui.${format}.js`,
},
rollupOptions: {
// 确保外部化处理那些你不想打包进库的依赖
external: [
"vue",
"@ant-design/icons-vue",
"vxe-table",
"xe-utils",
"@vitepress-demo-preview/component",
"@vitepress-demo-preview/plugin",
],
},
},
plugins: [
vue(),
vueJsx(),
viteStaticCopy({
targets: [
{
src: "components/**/*.less",
dest: "/",
},
],
structured: true,
}),
lessEntry({
// 生成的入口文件名
entry: "components",
// libPath需要与viteStaticCopy中的dest保持一致
libPath: "components",
name: "style",
}),
],
});
// vite-plugin-less-entry.js
import path from "node:path";
import fs from "fs-extra";
const name = "vite-plugin-custom-less-entry";
export const formatConsole = msg => `[${name}] ${msg}`;
/**
* 生成项目less的入口文件
*/
export default function lessEntryPlugin({ entry, libPath, name }) {
let outputed = false;
let rootConfig = null;
return {
name,
apply: "build",
order: "post",
configResolved(config) {
rootConfig = config;
},
writeBundle() {
if (outputed) {
return;
}
outputed = true;
// 遍历entry下的index.less文件,生成${name}.less文件
const componentsPath = path.join(rootConfig.root, entry);
let componentsLessContent = "";
fs.readdir(componentsPath, (err, files) => {
files.forEach(file => {
if (fs.existsSync(path.join(componentsPath, file, "index.less"))) {
componentsLessContent += `@import "./${libPath}/${path.posix.join(
file,
"index.less"
)}";\n`;
}
});
const lessEntryFile = path.join(
rootConfig.root,
rootConfig.build.outDir,
`${name}.less`
);
fs.outputFile(lessEntryFile, componentsLessContent, err => {
if (err) {
console.error(formatConsole("Failed to generate less entry file"));
} else {
console.info(
formatConsole("Successfully generated less entry file")
);
}
});
});
},
};
}
配置package.json:
{
"name": "eric-ui-lib",
"version": "0.0.2",
"description": "eric-ui组件库",
"main": "lib/eric-ui.umd.js",
"module": "lib/eric-ui.es.js",
"files": [
"lib"
],
"keywords": [
"eric-ui",
"eric",
"ui"
],
"author": "Eric",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"build:lib": "vite build --config ./build/lib.config.js",
"preview": "vite preview",
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs"
},
"dependencies": {
"@vitepress-demo-preview/component": "^2.3.2",
"@vitepress-demo-preview/plugin": "^1.2.3",
"ant-design-vue": "^3.2.20",
"fs-extra": "^11.2.0",
"less-loader": "^12.2.0",
"vite-plugin-static-copy": "^1.0.6",
"vue": "^3.4.29"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.5",
"@vitejs/plugin-vue-jsx": "^4.0.0",
"less": "^4.2.0",
"vite": "^5.3.1",
"vitepress": "^1.2.3"
}
}
三、npm发布
npm login 登录,没有注册的自行注册
npm publish
查看npm,即发布成功