构建完自动打包压缩,一个插件搞定 Vite / Webpack / Rollup / ESBuild

之前写了一个 Vite 专用的打包归档插件 vite-plugin-pack-orchestrator,文章地址。能用,但只支持 Vite --- 老项目用 Webpack,库开发用 Rollup,ESBuild(小型库) 也越来越主流,总不能每个构建器各写一套打包逻辑。

所以用 unplugin 重写了一版:unplugin-pack-orchestrator,一套 API,四个构建器通用。


🤔 unplugin是啥好玩意?

一句话:Unplugin 是一套通用插件开发框架,写一套插件代码,就能同时适配 Vite、Webpack、Rollup、esbuild、Rspack 等几乎所有前端打包工具,不用为每个工具单独写一套插件。 作者就是大家熟悉的Anthony Fu小哥。

核心解决什么痛点?

前端打包工具太多:Vite、Webpack、Rollup、esbuild......它们的插件 API 完全不一样:

  • Webpack 用自己的 compiler 钩子
  • Vite 复用 Rollup 钩子,但细节不同
  • esbuild 又是另一套

以前写插件,要分别维护多套代码,非常麻烦。

Unplugin 做了一层适配层:你只写一套基于 Rollup 风格的统一钩子,它自动帮你翻译成各个打包工具能识别的格式。


📑 你肯定用过的 Unplugin 插件(高频)

这些都是用 Unplugin 做的,Vite/Webpack 通用:

  1. unplugin-auto-import:自动导入 Vue、React、工具函数,不用手动 import
  2. unplugin-vue-components:Vue 组件自动按需导入
  3. unplugin-icons:图标组件按需引入
  4. unplugin-turbo-console:美化 console 日志
  5. ...

所以写了这个插件,把这些问题一块解决了。


⚡ 和其他插件有什么不同

功能 多数打包插件 unplugin-pack-orchestrator
构建器支持 仅 Vite Vite / Webpack / Rollup / ESBuild
压缩格式 仅 ZIP ZIP / TAR / TAR.GZ / 7Z
校验和 无 MD5 / SHA1 / SHA256
文件名模板 固定命名 [name][version][timestamp][hash] 占位符
Hook 扩展 无 onBeforeBuild / onAfterBuild / onError 等
文件过滤 部分支持 include + exclude glob 模式
7Z 支持 需要系统安装 7z 内置
自动重命名 无 onAfterBuild 返回新路径即自动重命名

📦 安装

perl 复制代码
npm install unplugin-pack-orchestrator -D

🚀 快速上手

四个构建器,同一套 API,只是导入路径不同:

Vite:

ts 复制代码
    // vite.config.ts
    import packOrchestrator from 'unplugin-pack-orchestrator/vite'

    export default defineConfig({
      plugins: [
        packOrchestrator({
          pack: { format: 'zip', fileName: 'myapp-[version]', outDir: 'dist' },
        }),
      ],
    })

Webpack:

js 复制代码
    // webpack.config.js
    const packOrchestrator = require('unplugin-pack-orchestrator/webpack')

    module.exports = {
      plugins: [
        packOrchestrator({
          pack: { format: 'zip', fileName: 'app-[name]-[version]', outDir: 'dist' },
        }),
      ],
    }

Rollup:

js 复制代码
    // rollup.config.js
    import packOrchestrator from 'unplugin-pack-orchestrator/rollup'

    export default {
      plugins: [
        packOrchestrator({
          pack: { format: 'tar.gz', fileName: 'bundle-[name]', outDir: 'dist' },
        }),
      ],
    }

ESBuild:

js 复制代码
    // build.js
    const packOrchestrator = require('unplugin-pack-orchestrator/esbuild')

    require('esbuild').build({
      outdir: 'dist',
      plugins: [
        packOrchestrator({
          pack: { format: '7z', fileName: 'build-[name]-[version]', outDir: 'dist' },
        }),
      ],
    })

构建完成后,项目根目录自动生成归档文件,不用额外操作。


⚙️ 配置项

js 复制代码
    pack: {
      outDir: 'dist',               // 要打包的源目录
      fileName: '[name]-[version]', // 文件名,支持占位符
      format: 'zip',                // zip | tar | tar.gz | 7z
      compressionLevel: 9,          // 压缩级别 0-9
      archiveOutDir: './releases',  // 归档输出目录,默认项目根目录
      include: ['**/*'],            // 包含文件(glob)
      exclude: ['**/*.map'],        // 排除文件(glob)
    }

fileName 占位符

占位符 说明 示例
[name] package.json name my-app
[version] package.json version 1.2.0
[timestamp] 当前时间戳(毫秒) 1714012345678
[hash] 构建产物 MD5(32 位) a1b2c3d4e5f6a7b8...
[hash:8] MD5 前 N 位 a1b2c3d4

文件名不写扩展名,插件根据 format 自动追加。


🔗 hooks --- 生命周期钩子

钩子 参数 触发时机
onBeforeBuild () 构建开始前
onBundleGenerated (bundle) 产物生成后、归档前
onAfterBuild (archivePath, format, checksums) 归档完成后
onError (error) 出错时

onAfterBuild --- 最核心的钩子

归档创建完成后,插件自动计算 MD5 / SHA1 / SHA256 校验和,传给回调。返回一个不同的路径即可自动重命名:

js 复制代码
    // 扩展名前插入 SHA1 短哈希:myapp.zip → myapp-3a7b2c1d.zip
    onAfterBuild: (path, format, checksums) =>
      path.replace(/(.(?:zip|tar.gz|tar|7z))$/, `-${checksums.sha1.slice(0, 8)}$1`)

    // 用 MD5 替换整个文件名:myapp.zip → a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6.zip
    onAfterBuild: (path, format, checksums) =>
      path.replace(/^.+(?=.\w+$)/, checksums.md5)

    // 完全自定义:myapp.zip → release-a1b2c3d4e5f6.zip
    onAfterBuild: (path, format, checksums) =>
      `release-${checksums.md5.slice(0, 12)}.${format}`

    // 不重命名,只拿校验和写文件
    onAfterBuild: (path, format, checksums) => {
      fs.writeFileSync('checksums.json', JSON.stringify(checksums))
    }

checksums 结构:{ md5: string; sha1: string; sha256: string }


🔄 为什么说自动重命名对 CI/CD 很重要?

流水线里每次构建产物都得是唯一可追溯的。文件名固定叫 dist.zip,你分不清这是第几次构建,回滚也不知道拿哪个。

用 onAfterBuild 追加哈希后:

python 复制代码
myapp-1.0.0-3a7b2c1d.zip
myapp-1.0.1-7f9e4b2a.zip

文件名本身就是指纹 🔑 --- 部署脚本按文件名定位版本,回滚找上一个哈希文件名就行。配合 [version][timestamp]占位符,追溯性更强,不需要额外维护版本映射表。


🎯 生产级配置

ts 复制代码
    // vite.config.ts
    import { defineConfig } from 'vite'
    import packOrchestrator from 'unplugin-pack-orchestrator/vite'

    export default defineConfig({
      plugins: [
        packOrchestrator({
          pack: {
            outDir: 'dist',
            fileName: 'release-[name]-v[version]',
            format: 'zip',
            archiveOutDir: './releases',
            exclude: ['**/*.map', '**/*.d.ts', 'node_modules/**'],
          },
          hooks: {
            // 归档后自动追加 SHA1 哈希
            onAfterBuild: (path, format, checksums) =>
              path.replace(/(.(?:zip|tar.gz|tar|7z))$/, `-${checksums.sha1.slice(0, 8)}$1`),
            onError: (err) => console.error('打包失败:', err.message),
          },
        }),
      ],
    })

vite build 一次搞定,不用额外打包脚本。


插件很轻量,代码开源,欢迎试用和提建议 🎉

相关推荐
liangshanbo12155 天前
面试题:Webpack 的 publicPath 有什么作用?
前端·webpack·node.js
cindershade6 天前
首屏不是 dist:用“加载账本”治理 Vite 的大包
性能优化·vite
liangshanbo12157 天前
Webpack vs Vite 底层原理高频追问的问题
前端·webpack·node.js
liangshanbo12157 天前
面试题:AI 对话上下文超限怎么办?
面试·webpack·devops
liangshanbo12158 天前
Webpack 文件指纹面试题整理
前端·webpack·node.js
liangshanbo12158 天前
Webpack 常见 Plugin 面试题
前端·webpack·node.js
liangshanbo12158 天前
Webpack Tree Shaking 面试题整理
前端·webpack·node.js
陪我一起学编程8 天前
vue3笔记
前端·webpack·vue3·vite·脚手架·vue3笔记·组件式
liangshanbo12158 天前
Webpack的分包策略面试题
前端·webpack·node.js
liangshanbo121510 天前
面试题:如何优化 Webpack 的打包速度?
前端·webpack·node.js