如何解决 monorepo 中 npm workspaces 与 Vite 的缓存问题
项目基于SvelteKit + Vite + npm workspaces,架构如下:
- apps/ - project-a/ - package.json - vite.config.ts - packages/ - common-lib-a/ - package.json - vite.config.ts - package.json
project-a的package.json中依赖@namespace/common-lib-a(版本^0.0.1),其vite.config.ts配置:
import { sveltekit } from '@sveltejs/kit/vite'; import dns from 'dns'; import Icons from 'unplugin-icons/vite'; import { defineConfig } from 'vite'; import { purgeCss } from 'vite-plugin-tailwind-purgecss'; const hash = Math.floor(Math.random() * 90000) + 10000; dns.setDefaultResultOrder('verbatim'); export default defineConfig({ plugins: [ sveltekit(), purgeCss(), Icons({ compiler: 'svelte' }) ], resolve: { preserveSymlinks: true, dedupe: ['@namespace/common-lib-a'] }, optimizeDeps: { exclude: ['@namespace/common-lib-a'] }, build: { rollupOptions: { output: { entryFileNames: `[name]${hash}.js`, chunkFileNames: `[name]${hash}.js`, assetFileNames: `[name]${hash}.[ext]` } } } });
根package.json的脚本用于监听common-lib-a变更并重启服务:
"scripts": { "dev-project-a": "npm run build -w @namespace/common-lib-a && npm run dev -w @namespace/project-a", "watch-project": "nodemon -x 'npm run dev-project-a' -e svelte,js,ts,json --watch packages/common-lib-a/src" }
问题:修改common-lib-a后,必须清除浏览器缓存,变更才会在project-a的dev服务中生效,如何解决?
1. 让common-lib-a开启watch模式,避免重复重启project-a
现在每次改库都要重新构建再重启dev服务,既慢又容易触发缓存。直接给common-lib-a加个watch脚本,实时编译:
- 修改
common-lib-a/package.json的scripts:"scripts": { "build": "vite build", "watch": "vite build --watch" } - 根package.json的scripts改成并行启动:
"scripts": { "dev-project-a": "npm run watch -w @namespace/common-lib-a & npm run dev -w @namespace/project-a", "watch-project": "npm run dev-project-a" }
这样库改了会自动编译,不用重启dev服务,缓存问题自然少很多。
2. 给Vite加配置,禁用依赖缓存
在project-a/vite.config.ts里加这些配置,让Vite不对common-lib-a搞缓存:
export default defineConfig({ // ...原有配置 server: { watch: { // 监听库的构建产物变化 include: ['../packages/common-lib-a/dist/**/*'] } }, optimizeDeps: { exclude: ['@namespace/common-lib-a'], // 直接禁用预构建缓存 cacheDir: false }, // 关闭Vite的文件系统缓存 cacheDir: false });
要是极端情况HMR不生效,还能把server.hmr.fullReload设为true,强制全页刷新。
3. 给库的构建产物加动态哈希
在common-lib-a/vite.config.ts里配置构建输出带哈希,这样每次编译出来的文件名都不一样,浏览器自动加载新文件,不用手动清缓存:
import { defineConfig } from 'vite'; export default defineConfig({ build: { rollupOptions: { output: { entryFileNames: '[name].[hash].js', chunkFileNames: '[name].[hash].js', assetFileNames: '[name].[hash].[ext]' } } } });
4. 直接链接库的源码,跳过构建环节
确保npm workspaces已经把common-lib-a软链接到project-a/node_modules(根目录跑npm install就行),然后在project-a/vite.config.ts里加别名指向源码:
resolve: { preserveSymlinks: true, dedupe: ['@namespace/common-lib-a'], alias: { '@namespace/common-lib-a': '../packages/common-lib-a/src' } }
这样Vite直接监听源码变化,HMR实时生效,连库的构建步骤都省了,效率更高。
5. 开发环境禁用浏览器缓存
在project-a/src/hooks.server.ts里加响应头,强制浏览器不缓存:
import type { Handle } from '@sveltejs/kit'; export const handle: Handle = async ({ event, resolve }) => { const response = await resolve(event); if (import.meta.env.DEV) { response.headers.set('Cache-Control', 'no-cache, no-store, must-revalidate'); response.headers.set('Pragma', 'no-cache'); response.headers.set('Expires', '0'); } return response; };
内容的提问来源于stack exchange,提问作者Pandorque

