如何在Vite构建过程中获取构建产物资源?
实现自定义Vite Service Worker插件(含资源注入与Git Hash更新)
核心思路
利用Vite/Rollup的构建钩子直接获取产物资源列表,无需额外构建后脚本;通过Git Commit Hash作为Service Worker的更新标识,确保资源更新时SW能触发重新安装。
步骤1:编写自定义Vite插件
在插件中完成三件事:获取Git Commit Hash、收集构建产物资源、将两者注入到Service Worker文件中。
import { execSync } from 'child_process'; import fs from 'fs'; import path from 'path'; export default function vitePluginCustomSW() { let commitHash = ''; let assetList = []; return { name: 'vite-plugin-custom-sw', // 初始化时获取Git Commit Hash buildStart() { try { commitHash = execSync('git rev-parse HEAD').toString().trim(); } catch (e) { // 本地无Git环境时用随机字符串兜底 commitHash = Math.random().toString(36).substring(2, 10); } }, // 收集所有构建产物资源(chunk和静态资源) generateBundle(_, bundle) { assetList = Object.values(bundle).map(item => { // 排除SW自身文件,避免循环缓存 if (item.fileName.includes('sw.js')) return null; return item.fileName; }).filter(Boolean); }, // 生产环境:修改SW文件,注入资源列表和Commit Hash writeBundle() { const swPath = path.resolve(__dirname, 'src/sw.js'); let swContent = fs.readFileSync(swPath, 'utf-8'); // 替换占位符为实际数据 swContent = swContent.replace( '__COMMIT_HASH__', JSON.stringify(commitHash) ).replace( '__ASSET_LIST__', JSON.stringify(assetList) ); // 写入最终的SW文件到构建目录 fs.writeFileSync(path.resolve(__dirname, 'dist/sw.js'), swContent); }, // 开发环境:注入资源列表到SW(可选,开发环境也支持离线) configureServer(server) { server.middlewares.use((req, res, next) => { if (req.url === '/sw.js') { let swContent = fs.readFileSync(path.resolve(__dirname, 'src/sw.js'), 'utf-8'); // 开发环境下资源路径可能不同,这里可以动态获取当前编译的资源 // 简单处理:直接列出src下的静态资源和入口文件 const devAssets = ['/src/main.js', '/index.html']; swContent = swContent.replace( '__COMMIT_HASH__', JSON.stringify(commitHash) ).replace( '__ASSET_LIST__', JSON.stringify(devAssets) ); res.setHeader('Content-Type', 'application/javascript'); res.end(swContent); return; } next(); }); } }; }
步骤2:编写Service Worker核心逻辑
在src/sw.js中使用占位符,接收插件注入的资源列表和Commit Hash:
const COMMIT_HASH = __COMMIT_HASH__; const CACHE_NAME = `app-cache-${COMMIT_HASH}`; const ASSET_LIST = __ASSET_LIST__; // 安装阶段:缓存所有指定资源 self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_NAME).then(cache => { return cache.addAll(ASSET_LIST); }).then(() => self.skipWaiting()) ); }); // 激活阶段:清理旧缓存 self.addEventListener('activate', (event) => { event.waitUntil( caches.keys().then(cacheNames => { return Promise.all( cacheNames.filter(name => !name.includes(COMMIT_HASH)) .map(name => caches.delete(name)) ); }).then(() => self.clients.claim()) ); }); // 联网时处理资源请求(这里用缓存优先策略,可根据需求调整) self.addEventListener('fetch', (event) => { event.respondWith( caches.match(event.request).then(cachedResponse => { // 优先返回缓存,同时后台更新资源 const fetchPromise = fetch(event.request).then(networkResponse => { caches.open(CACHE_NAME).then(cache => { cache.put(event.request, networkResponse.clone()); }); return networkResponse; }); return cachedResponse || fetchPromise; }) ); });
步骤3:在Vite配置中注册插件
在vite.config.js中引入并使用自定义插件:
import { defineConfig } from 'vite'; import vitePluginCustomSW from './vite-plugin-custom-sw'; export default defineConfig({ plugins: [vitePluginCustomSW()], // 可选:如果有静态资源需要被收集,确保build.assetsDir配置正确 build: { assetsDir: 'assets' } });
关键说明
- 资源收集逻辑:通过Rollup的
generateBundle钩子直接获取构建后的所有产物,无需额外遍历目录,完全依赖Vite的构建流程。 - 更新机制:每次构建时Git Commit Hash变化,会生成新的缓存名称,SW安装时会缓存新资源,激活阶段自动清理旧缓存,实现无缝更新。
- 开发环境支持:通过
configureServer钩子动态注入开发环境的资源列表,开发时也能体验离线功能。
内容的提问来源于stack exchange,提问作者Eric Rovell
相关产品推荐
相关产品推荐

