使用 grunt 调用 Vite Library 模式构建组件
解读
国内前端团队普遍已转向 Vite 作为新一代构建工具,但存量项目仍依赖 Grunt 做任务编排。面试官想验证两点:
- 你能否让“老”Grunt 无缝驱动“新”Vite,而不是推翻现有 CI 流程;
- 你是否理解 Vite Library 模式(即 vite build --mode lib)与常规应用构建的差异:入口为组件源码、输出格式为 es + umd、外部化 peerDependencies、生成 d.ts 与 sourcemap。
回答时要体现“渐进升级”思路,既尊重 Grunt 插件体系,又发挥 Vite 的极速优势,避免“二选一”的极端答案。
知识点
- grunt-contrib-clean:清理旧 dist,保证每次构建“零残留”。
- grunt-run / grunt-exec:在 Grunt 任务里同步调用 npx vite build --config vite.lib.config.ts,捕获 exit code,失败即中断后续任务。
- vite.lib.config.ts 关键配置:
– build.lib.entry 指向组件入口(如 src/index.ts);
– build.lib.name 暴露全局变量(供 umd 使用);
– build.lib.fileName 返回带版本号的文件名,方便 Grunt 后续做 CDN 上传;
– rollupOptions.external 把 react、vue 等 peerDependencies 外部化,减小包体;
– plugins 数组加入 dts 插件自动生成类型声明。 - grunt-contrib-copy:把 README、package.json 拷进 dist,方便发 npm 私库。
- grunt-bump + grunt-conventional-changelog:在构建成功后自动抬升 package.json 版本并生成中文 ChangeLog,契合国内“版本号即发布”习惯。
- grunt-contrib-zip:将 dist 打成 tgz,供内网 GitLab CI 做制品归档。
- 错误码约定:Vite 返回非 0 时,Grunt 任务必须 failFatal,防止“带病”制品进入私服。
- 并发安全:若多人同时发布,需在 Gruntfile 里用 grunt-lockfile 插件做分布式锁,避免版本号冲突。
答案
-
安装依赖
npm i -D vite @vitejs/plugin-react rollup-plugin-dts grunt grunt-contrib-clean grunt-run grunt-contrib-copy grunt-bump grunt-conventional-changelog grunt-contrib-zip -
在项目根新建 vite.lib.config.ts
import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import dts from 'rollup-plugin-dts'; import { resolve } from 'path'; export default defineConfig({ plugins: [react(), dts({ insertTypesEntry: true })], build: { lib: { entry: resolve(__dirname, 'src/index.tsx'), name: 'MyUI', fileName: (format) => `my-ui.${format}.js` }, rollupOptions: { external: ['react', 'react-dom'], output: { globals: { react: 'React', 'react-dom': 'ReactDOM' } } }, sourcemap: true, emptyOutDir: true } }); -
编写 Gruntfile.js(CommonJS 写法,兼容老 Node 版本)
module.exports = function (grunt) { grunt.initConfig({ clean: { dist: 'dist' }, run: { viteLib: { cmd: 'npx', args: ['vite', 'build', '--config', 'vite.lib.config.ts'], options: { failOnError: true, stderr: true } } }, copy: { meta: { files: [ { src: 'README.md', dest: 'dist/README.md' }, { src: 'package.json', dest: 'dist/package.json' } ] } }, bump: { options: { files: ['package.json'], commit: false, createTag: false, push: false } }, changelog: { options: { preset: 'conventionalcommits', releaseCount: 1, file: 'dist/CHANGELOG.md' } }, compress: { dist: { options: { archive: 'release/my-ui-v<%= pkg.version %>.tgz', mode: 'tgz' }, expand: true, cwd: 'dist/', src: ['**'] } } }); grunt.loadNpmTasks('grunt-contrib-clean'); grunt.loadNpmTasks('grunt-run'); grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-bump'); grunt.loadNpmTasks('grunt-conventional-changelog'); grunt.loadNpmTasks('grunt-contrib-compress'); grunt.registerTask('build:lib', [ 'clean', 'run:viteLib', 'copy:meta', 'bump', 'changelog', 'compress' ]); }; -
在 CI(如 GitLab Runner)里执行
npm run grunt build:lib
成功后 dist/ 即为可直接发布到 内网 Verdaccio 的目录,release/*.tgz 为归档制品。
拓展思考
- 双轨构建:若仓库同时维护 legacy 页面(requirejs)与新组件库,可在 Gruntfile 里加条件判断:当存在 vite.lib.config.ts 时走 Vite,否则回退到 grunt-contrib-uglify + grunt-browserify,实现“同一代码库,两套出口”。
- 微前端场景:把 Vite 构建的 es 格式产物作为 联邦模块 上传到 qiankun 主应用,Grunt 任务里增加一步“生成 importmap.json”,让主应用运行时动态映射版本,避免手动改 HTML。
- 性能优化:当组件库体积过大,可在 Grunt 任务链中插入 rollup-plugin-visualizer,生成 stats.html 并自动推送到企业微信机器人,提醒开发者及时拆分懒加载子包。
- 合规审计:国内金融客户要求“构建过程可回溯”,可在 Grunt 任务里用 grunt-git-info 把当前 commit、构建人、构建机 IP 写入 dist/buildinfo.json,方便审计追踪。