Hot Module Reload - Vite
Changes in code are not updated on the client, requiring manual full page reload.
Steps to Reproduce
-
Add HMR support to
{zsh} vite.config.jsexport default defineConfig(({ command, mode }) => { return { // ... server: { watch: { usePolling: true, } }, // ... } } -
Start application in debug mode and update file
Settings.vuevite --debug hmr
- components are lazy loaded in router and their module graph is loaded separately from the main bundle
- hmr needs to track and update this separate module boundary, using the new hmr reload async feature in vue runtime-core
Code Update
Currently, we are using node 16 module resolution, default to commonjs. Here are the files of package @vue/runtime-core:
├── runtime-core.d.ts
├── runtime-core.cjs.prod.js
├── runtime-core.cjs.js
└── runtime-core.esm-bundler.js
Only {bash} runtime-core.esm-bundler.js contains:
- Full HMR state management
- Dynamic import boundary handling
- Module graph tracking for updates
- Hot reload handlers for Vue components
We need to update module resolution in {zsh} vite.config.js
import { defineConfig, loadEnv } from 'vite'
import { resolve } from 'path'
import alias from '@rollup/plugin-alias'
import vue from '@vitejs/plugin-vue'
import svgLoader from 'vite-svg-loader'
import childProcess from 'child_process'
import Components from 'unplugin-vue-components/vite'
const commitHash = childProcess.execSync('git rev-parse --short HEAD').toString().slice(0, 8).replace('\n', '')
export default defineConfig(({ command, mode }) => {
// Load env file based on `mode` in the current working directory.
// Set the third parameter to '' to load all env regardless of the `VITE_` prefix.
return {
server: {
watch: {
usePolling: true,
},
headers: {
'Cross-Origin-Opener-Policy': 'unsafe-none',
'Cross-Origin-Embedder-Policy': 'unsafe-none',
}
},
resolve: {
alias: [
{
find: '@vue/runtime-core',
replacement: '@vue/runtime-core/dist/runtime-core.esm-bundler.js',
},
],
},
rollupPluginVueOptions: {
cssModulesOptions: {
generateScopedName: '[hash:base64:8]',
}
},
build: {
manifest: true,
target: 'esnext',
emptyOutDir: false,
rollupOptions: {
input: {
main: resolve(__dirname, 'index.html'),
theme: resolve(__dirname, 'index_themes.html')
},
output: {
assetFileNames: `assets/[name].${commitHash}[extname]`,
chunkFileNames: `assets/[name].${commitHash}.js`,
entryFileNames: `assets/[name].${commitHash}.js`
}
},
minify: 'esbuild'
},
plugins: [
Components({
dts: false,
globs: ['!src/components/*.{vue}'],
resolvers: [
(name) => {
const componentName = name
const regex = /^Ph[A-Z][a-zA-Z]*$/
if (regex.test(componentName)) {
return {
importName: componentName,
path: '@phosphor-icons/vue',
sideEffect: true
}
}
}
]
}),
vue(
{
template: {
compilerOptions: {
isCustomElement: (tag) => ['a-col', 'a-row'].includes(tag),
}
}
}
),
svgLoader(),
alias({
entries: [
{
find: '@',
replacement: resolve(__dirname, 'src')
}
]
}),
]
}
})
Results
- All files now are hot reloaded dynamically in development
- Fix full page reload takes 5-10 seconds to 0 second.