Home >Web Front-end >Vue.js >How does Vue3 use glup to package component libraries and implement on-demand loading?
When we use the Vite library mode to package, vite will package all the style files into the same file, so that we have to load them in full every time Importing all style files does not have the effect of importing them on demand. So when packaging, we can not let vite package the style files, and the style files will be packaged using gulp. So this article will introduce how to use gulp to package style files and how to load style files on demand.
Nowadays, the on-demand introduction of many component libraries is solved with the help of plug-ins. For example, ElementPlus
uses unplugin-vue- components
and unplugin-auto-import
, these two plug-ins can implement
import { Button } from "easyest"; //相当于 import "easyest/es/src/button/style/index.css"; import "easyest/es/src/button/index.mjs";
to achieve on-demand introduction. I will not expand the use of these plug-ins too much here, because this article The focus of the article is not here. If you are interested, you can directly query the usage method unplugin-vue-components
We all know that before packaging, you need to delete the previously packaged files. , so you need to write a delete function first. Before that, we first create a new script folder in components to store our script-related content. The content in the build folder under script is the packaging-related content to be introduced in this article.
Create new paths.ts in script/utils to maintain the component library path. Remember to install it first.
pnpm add @types/node -D -w
import { resolve } from "path"; //组件库根目录 export const componentPath = resolve(__dirname, "../../"); //pkg根目录 export const pkgPath = resolve(__dirname, "../../../");
Deleting the packaging directory function can be placed in delpath.ts in bulid/utils. Pay attention here. Because the packaged easyest package is the package we will eventually publish, we need to retain package.json
and README.md
import fs from "fs"; import { resolve } from "path"; import { pkgPath } from "./paths"; //保留的文件 const stayFile = ["package.json", "README.md"]; const delPath = async (path: string) => { let files: string[] = []; if (fs.existsSync(path)) { files = fs.readdirSync(path); files.forEach(async (file) => { let curPath = resolve(path, file); if (fs.statSync(curPath).isDirectory()) { // recurse if (file != "node_modules") await delPath(curPath); } else { // delete file if (!stayFile.includes(file)) { fs.unlinkSync(curPath); } } }); if (path != `${pkgPath}/easyest`) fs.rmdirSync(path); } }; export default delPath;
We need to use ts and the new es6 syntax, which is not supported by gulp, so we need to install some dependencies to make gulp support these. Sucras allows us to execute gulp to use the latest syntax and support ts
pnpm i gulp @types/gulp sucrase -D -w
Execute the deletion process in build/index.ts
import delPath from "../utils/delpath"; import { series, parallel } from "gulp"; import { pkgPath } from "../utils/paths"; //删除easyest export const removeDist = () => { return delPath(`${pkgPath}/easyest`); }; export default series(async () => removeDist());
Add the script in the root directory easyest/package.json
"scripts": { "build:easyest": "gulp -f packages/components/script/build/index.ts" },
Execute in the root directorypnpm run build:easyest
You will find that the files under easyest have been deleted
After deletion, you can start packaging the style
Because we are using styles written by less, we need to install gulp-less
, and at the same time install an auto-complete css prefix plug-in gulp-autoprefixer
and their corresponding above files
pnpm add gulp-less @types/gulp-less gulp-autoprefixer @types/gulp-autoprefixer -D -w
Then write a packaged style function. The dest
and src
functions of gulp are used here. If you don’t know what it means, I would like to read the introduction of gulp in the previous article
//打包样式 export const buildStyle = () => { return src(`${componentPath}/src/**/style/**.less`) .pipe(less()) .pipe(autoprefixer()) .pipe(dest(`${pkgPath}/easyest/lib/src`)) .pipe(dest(`${pkgPath}/easyest/es/src`)); };
Finally write a function to package the component. Here you need to write a tool function to execute the command. Then introduce the run function in utils/run.ts
import { spawn } from "child_process"; export default async (command: string, path: string) => { //cmd表示命令,args代表参数,如 rm -rf rm就是命令,-rf就为参数 const [cmd, ...args] = command.split(" "); return new Promise((resolve, reject) => { const app = spawn(cmd, args, { cwd: path, //执行命令的路径 stdio: "inherit", //输出共享给父进程 shell: true, //mac不需要开启,windows下git base需要开启支持 }); //执行完毕关闭并resolve app.on("close", resolve); }); };
//打包组件 export const buildComponent = async () => { run("pnpm run build", componentPath); };
Because packaging styles and packaging components can be parallel, the final build/index.ts
is
import delPath from "../utils/delpath"; import { series, parallel, src, dest } from "gulp"; import { pkgPath, componentPath } from "../utils/paths"; import less from "gulp-less"; import autoprefixer from "gulp-autoprefixer"; import run from "../utils/run"; //删除dist export const removeDist = () => { return delPath(`${pkgPath}/easyest`); }; //打包样式 export const buildStyle = () => { return src(`${componentPath}/src/**/style/**.less`) .pipe(less()) .pipe(autoprefixer()) .pipe(dest(`${pkgPath}/easyest/lib/src`)) .pipe(dest(`${pkgPath}/easyest/es/src`)); }; //打包组件 export const buildComponent = async () => { run("pnpm run build", componentPath); }; export default series( async () => removeDist(), parallel( async () => buildStyle(), async () => buildComponent() ) );
The last vite package ignores the less file, components/vite. config.ts
import { defineConfig } from "vite"; import vue from "@vitejs/plugin-vue"; import dts from "vite-plugin-dts"; import DefineOptions from "unplugin-vue-define-options/vite"; export default defineConfig({ build: { //打包文件目录 outDir: "es", //压缩 //minify: false, rollupOptions: { //忽略打包vue和.less文件 external: ["vue", /\.less/], ... } });
In order to better see the effect of packaging, we can write a simple Icon component, the directory is as follows
The final root directory Execute pnpm run build
to complete the packaging
Since vite packaging ignores less file packaging, the packaged file encounters the .less file The import will be automatically skipped, so the import code has not changed
But we have packaged the less file into a css file, so we need to change the .less in the code
Change to .css
Add
plugins: [ vue(), DefineOptions(), dts({ entryRoot: "./src", outputDir: ["../easyest/es/src", "../easyest/lib/src"], //指定使用的tsconfig.json为我们整个项目根目录下,如果不配置,你也可以在components下新建tsconfig.json tsConfigFilePath: "../../tsconfig.json", }), { name: "style", generateBundle(config, bundle) { //这里可以获取打包后的文件目录以及代码code const keys = Object.keys(bundle); for (const key of keys) { const bundler: any = bundle[key as any]; //rollup内置方法,将所有输出文件code中的.less换成.css,因为我们当时没有打包less文件 this.emitFile({ type: "asset", fileName: key, //文件名名不变 source: bundler.code.replace(/\.less/g, ".css"), }); } }, }, ],
In the plugins in components/vite.config.ts, execute pnpm run build: easyest
, and then look at the packaged file introduction
At this time .less
has been replaced by .css
, after packaging is completed, the next thing to do is to publish it!
The above is the detailed content of How does Vue3 use glup to package component libraries and implement on-demand loading?. For more information, please follow other related articles on the PHP Chinese website!