先使用vite 搭建基础版本脚手架这里不做讲解、由于vite初始化什么都没有安装、我采用了自己改造过的脚手架
详情见GitHub 内置vue-router、commitlint、stylelint、eslint、prettier、unplugin-vue-components、unplugin-auto-import、lint-staged、sentry、pinia、husky...
基本上可以满足团队日常开发需求。
😅言归正传、生成目录大概是这样的
1├── README.md
2├── commitlint.config.cjs
3├── index.html
4├── package.json
5├── pnpm-lock.yaml
6├── public
7│ ├── version.txt
8│ └── vite.svg
9├── src
10│ ├── App.vue
11│ ├── api
12│ │ └── index.ts
13│ ├── assets
14│ ├── auto-imports.d.ts
15│ ├── components
16│ ├── components.d.ts
17│ ├── global.d.ts
18│ ├── hooks
19│ ├── intercept.ts
20│ ├── main.ts
21│ ├── router
22│ ├── store
23│ ├── utils
24│ ├── views
25│ ├── vite-env.d.ts
26│ └── workers
27│ ├── versionCheckWorker.ts
28│ └── worker.ts
29├── tsconfig.json
30├── tsconfig.node.json
31└── vite.config.ts
一系列的修改操作
- 修改
src
为packages
,删除views
目录 - 修改
alias
把所有和src
相关的别名路径修改为components
、修改目录为vite.config.ts
、tsconfig.json
1
2 alias: [
3 {
4 find: "@",
5 replacement: resolve(__dirname, "../packages"),
6 },
7 ],
1
2{
3...
4 "paths": {
5 "@/*": [
6 "packages/*"
7 ],
8 },
9...
10}
- 删除不需要用到的文件夹
store
… - 修改index.html中
script
标签中src
路径为/packages/main.ts
packages
下新建文件夹theme-chalk
后续用来存放样式
修改后的目录结构
1├── README.md
2├── commitlint.config.cjs
3├── index.html
4├── package.json
5├── pnpm-lock.yaml
6├── public
7│ ├── version.txt
8│ └── vite.svg
9├── packages
10│ ├── App.vue
11│ ├── api
12│ ├── assets
13│ ├── auto-imports.d.ts
14│ ├── components
15│ ├── components.d.ts
16│ ├── global.d.ts
17│ ├── hooks
18│ ├── intercept.ts
19│ ├── main.ts
20│ ├── router
21│ ├── store
22│ ├── utils
23│ └── vite-env.d.ts
24├── tsconfig.json
25├── tsconfig.node.json
26└── vite.config.ts
后续就可以在components
文件夹下开发组件了,查看组件的话直接在router
上配置路径即可
敲黑板:每个组件必须设置
name
,不然后续全局注册会有问题components
整体格式如下
1├── components
2 ├── button
3 │ ├──Button.vue
4 │ └──index.ts
5 └──index.ts
相关代码如下
1<!-- Button.vue -->
2<template>
3 <div class="bq-button">
4 <span>-测试按钮-6</span>
5 </div>
6</template>
7
8<script setup lang="ts">
9defineOptions({
10 name: "BqButton",
11});
12</script>
13
14<style lang="scss" scoped>
15@import "@theme-chalk/button.scss";
16</style>
1
2import Button from "./Button.vue";
3import { withInstall } from "../../utils/tool";
4export const BqButton = withInstall(Button);
5export default BqButton;
withInstall
相关方法如下、主要是为了全局注册
1import type { App } from "vue";
2export const withInstall = <T extends Component>(comp: T) => {
3 (comp as Record<string, unknown>).install = (app: App) => {
4 const compName = comp.name;
5 if (!compName) return;
6 app.component(compName, comp);
7 };
8 return comp;
9};
1
2export * from "./button";
这时候我们的组件基本就写好了,查看组件只需要在router
下配置路径即可
1 {
2 path: "/",
3 name: "button",
4 meta: {
5 title: "login",
6 },
7 component: () => import( "@/components/button/Button.vue"),
8 },
先给大家看配置、再给大家讲为什么
1
2import { defineConfig } from "vite";
3import vue from "@vitejs/plugin-vue";
4import { resolve } from "path";
5import { pluginsConfig, resolveConfig } from "./scripts/preview";
6import dts from "vite-plugin-dts";
7export default defineConfig(() => {
8 return {
9 build: {
10 outDir: "build",
11 cssCodeSplit: true,
12 rollupOptions: {
13 external: ["three", "@ant-design/icons-vue", "ant-design-vue", "unplugin-vue-components", "unplugin-auto-import", "vue"],
14 output: [
15 {
16 format: "es",
17 entryFileNames: "[name].js",
18 exports: "named",
19 name: "BqDesign",
20 dir: "./build/dist",
21 },
22 {
23 format: "es",
24 entryFileNames: "[name].js",
25 exports: "named",
26 preserveModules: true,
27 preserveModulesRoot: "packages",
28 dir: "./build/es",
29 },
30 {
31 format: "cjs",
32 entryFileNames: "[name].js",
33 exports: "named",
34 preserveModules: true,
35 preserveModulesRoot: "packages",
36 dir: "./build/lib",
37 },
38 ],
39 },
40 lib: {
41 entry: resolve(__dirname, "./packages/index.ts"),
42 name: "BqDesign",
43 fileName: (format) => `bq-design.${format}.js`,
44 formats: ["es", "cjs"],
45 },
46 },
47 plugins: [
48 vue(),
49 dts({
50 tsconfigPath: "./tsconfig.prod.json",
51 outDir: "build/lib",
52 }),
53 dts({
54 tsconfigPath: "./tsconfig.prod.json",
55 outDir: "build/es",
56 }),
57 ...pluginsConfig,
58 ],
59 resolve: resolveConfig,
60 };
61});
62
一点一点给大家捋一下为什么这么写
1、为什么output
需要这样配置
首先按照官网给的示例配置并执行
打包后,有问题吗?没问题。但就是有点奇怪,因为我们打包后结构是这样的
1└── dist
2 ├── index.js
3 └── style.css
毕竟我们都是见过世面的🤓,为什么我们打包出来的和ant-design-vue
、ElementPlus
差距这么大, 其实小编在做这个事情的时候也看了这些优秀的开源组件库、毕竟学习的过程就是先模仿
后创造
大概看了一下 ElementPlus
是基于vite开发的,ant-design-vue
还是webpack、那我们先看一下ElementPlus
打包后的结构大概如下
1└── build
2 ├── dist
3 ├── es
4 ├── lib
5 ├── README.md
6 └── package.json
很明显他输出了三个包(说明我们在output中也需要配置3个输出文件)、首先es
文件夹用来兼容esm
语法、lib
文件夹兼容commentJs
、dist
也是esm语法他的css是打包在一个文件
里的主要是为了全局引入css,配置好后大概是这个样子
1output: [
2 {
3 format: "es",
4 entryFileNames: "[name].js",
5 dir: "./build/dist",
6 },
7 {
8 format: "es",
9 entryFileNames: "[name].js",
10 dir: "./build/es",
11 },
12 {
13 format: "cjs",
14 entryFileNames: "[name].js",
15 dir: "./build/lib",
16 },
17 ],
之后我们执行打包脚步 pnpm run build
不报错的情况下打包出来应该已经是有三个文件了,但好像js
和css
还是在一起,怎么办?当然是看文档了🤑
原来如此、配置后执行打包命令,果然没问题
2、为什么cssCodeSplit
是要改为true
?
因为在vite
在lib
模式下cssCodeSplit
默认是false
,其实官方有说明,可只在英文文档做了说明🥲
没注意到的打包后所有组件css都在一个文件、后续就没有办法实现我们的按需引入了
3、如何打包出ts类型标注
使用pnpm
下载 vite-plugin-dts
该插件
pnpm add vite-plugin-dts -D
由于我们在lib
和es
库里都需要打包ts,所以需要配置两个dts,代码如下
1 dts({
2 tsconfigPath: "./tsconfig.prod.json",
3 outDir: "build/lib",
4 }),
5 dts({
6 tsconfigPath: "./tsconfig.prod.json",
7 outDir: "build/es",
8 }),
tsconfigPath,需要单独引入一个新的tsconfig配置、主要是因为我们打包的include和exclude配置和实际开发中还是有一定区别。并且include配置有问题会导致打包出的类型没有放在实际文件夹下。
新建了一个tsconfig.prod.json文件,代码如下
1{
2 "extends": "./tsconfig.json",
3 "include": [
4 "packages/**/*.vue",
5 "packages/**/*.d.ts",
6 "packages/**/*.ts",
7 ],
8 "exclude": [
9 "./packages/main.ts",
10 "node_modules",
11 "./packages/router/*"
12 ]
13}
走到这里其实我们的组件已经小有所成了
一个好的组件离不开一个优秀的文档、这里我推荐大家使用VitePress
,相对于市面上其他的文档生成工具VitePress
拥有着强大的生态环境和相对稳定的版本,文档地址,根据文档操作,之后会在最外层目录下生成一个docs文件,里面就可以快乐的写我们的文档了,这里就不做演示了。
1const BqDesignResolver = () => {
2 return {
3 type: "component" as const,
4 resolve: (name) => {
5 if (name.startsWith("Bq")) {
6 const pathName = name.slice(2).toLowerCase();
7 return {
8 importName: name,
9 from: "bq-design",
10 path: `bq-design/es/components/${pathName}/index.js`,
11 sideEffects: `bq-design/es/components/${pathName}/${name.slice(2)}.css`,
12 };
13 }
14 },
15 };
16};
更改名称为自己组件库即可
在vite中严格意义上是不需要手动导入,因为Vite提供了基于 ES Module 的开箱即用的Tree Shaking
功能,但有一种情况就是我开发的组件库引入了第三方包,比如threeJs,但在实际运用中,我只引用了我的Button按钮,会报错,因为我们的导出模块是有关联的,实际这么引用
上述情况使用
Vite
一定要在optimizeDeps.exclude
添加相应的包如bq-design,因为预加载发现缺少依赖会报错
1import {Button} from 'bq-design'
开发环境下是会导出全部的bq-design(生产环境并不会哦),默认情况按需引入需要这样导入
1import BqButton from "bq-design/es/components/button";
但这样还存在着无法引入样式的问题,所以我们需要自己开发一个vite插件进行转换,代码如下
1export default function importPlugin() {
2 const regStr = /(?<!\/\/.*|\/\*[\s\S]*?\*\/\s*)import\s*{\s*([^{}]+)\s*}\s*from\s*['"]bq-design['"]/g;
3 return {
4 name: "vite-plugin-import",
5 enforce: "pre",
6 transform: (code: string, id: string) => {
7 if (id.endsWith(".vue")) {
8 const str = code.replaceAll(regStr, (match, imports) => {
9 const list = imports.split(",");
10 const newPath: string[] = [];
11 list.forEach((item: string) => {
12 item = item.trim();
13 const name = item.slice(2).charAt(0).toLowerCase() + item.slice(3);
14 const str = `import ${item.trim()} from 'bq-design/es/components/${name.trim()}';
15 import 'bq-design/es/components/${name.trim()}/${item.trim().slice(2)}.css'`;
16 newPath.push(str);
17 });
18 return newPath.join(";");
19 });
20 return str;
21 }
22 return code;
23 },
24 };
25}
Webpack用户可以使用 babel-plugin-import
进行处理同样这也是ant-design-vue
的处理方式
这是小编自己搭建的组件库地址
相关github地址