热更新 #

什么是热更新? #

热更新(Hot Module Replacement,HMR)允许在开发过程中修改 Vuex 模块代码后,无需刷新页面即可看到更新效果,同时保留当前的应用状态。

基本配置 #

Webpack 配置 #

javascript
// store/index.js
import { createStore } from 'vuex'
import user from './modules/user'
import cart from './modules/cart'

const store = createStore({
  modules: {
    user,
    cart
  }
})

export default store

// 热更新配置
if (import.meta.hot) {
  import.meta.hot.accept(['./modules/user', './modules/cart'], (newModules) => {
    const newUser = newModules[0].default
    const newCart = newModules[1].default
    
    store.hotUpdate({
      modules: {
        user: newUser,
        cart: newCart
      }
    })
  })
}

Vite 配置 #

javascript
// store/index.js
import { createStore } from 'vuex'
import user from './modules/user'
import cart from './modules/cart'

const store = createStore({
  modules: {
    user,
    cart
  }
})

export default store

// Vite 热更新
if (import.meta.hot) {
  import.meta.hot.accept('./modules/user', (newModule) => {
    store.hotUpdate({
      modules: {
        user: newModule.default
      }
    })
  })
  
  import.meta.hot.accept('./modules/cart', (newModule) => {
    store.hotUpdate({
      modules: {
        cart: newModule.default
      }
    })
  })
}

动态导入模块 #

自动热更新所有模块 #

javascript
// store/index.js
import { createStore } from 'vuex'

// 动态导入所有模块
const moduleFiles = import.meta.glob('./modules/*.js')

const modules = {}
for (const path in moduleFiles) {
  const moduleName = path.replace(/^\.\/modules\/(.+)\.\w+$/, '$1')
  modules[moduleName] = moduleFiles[path]
}

export const createStoreInstance = async () => {
  const loadedModules = {}
  
  for (const [name, importFn] of Object.entries(modules)) {
    const module = await importFn()
    loadedModules[name] = module.default
  }
  
  return createStore({
    modules: loadedModules
  })
}

export default await createStoreInstance()

热更新辅助函数 #

javascript
// store/hmr.js
export function setupHMR(store, moduleMap) {
  if (import.meta.hot) {
    Object.entries(moduleMap).forEach(([moduleName, modulePath]) => {
      import.meta.hot.accept(modulePath, (newModule) => {
        store.hotUpdate({
          modules: {
            [moduleName]: newModule.default
          }
        })
      })
    })
  }
}

// 使用
import { setupHMR } from './hmr'

const store = createStore({ /* ... */ })

setupHMR(store, {
  user: './modules/user',
  cart: './modules/cart'
})

export default store

保留状态 #

基本状态保留 #

热更新默认会保留当前状态:

javascript
// 修改模块代码后,状态会保留
// 例如:修改 user 模块的 mutation
// 当前登录状态不会丢失

重置状态 #

如果需要在热更新时重置状态:

javascript
if (import.meta.hot) {
  import.meta.hot.accept('./modules/user', (newModule) => {
    // 获取新模块
    const newUser = newModule.default
    
    // 重置状态
    const oldState = store.state.user
    const newState = newUser.state()
    
    store.hotUpdate({
      modules: {
        user: {
          ...newUser,
          state: () => newState
        }
      }
    })
  })
}

完整配置示例 #

项目结构 #

text
store/
├── index.js
├── hmr.js
└── modules/
    ├── user.js
    ├── cart.js
    └── products.js

入口文件 #

javascript
// store/index.js
import { createStore } from 'vuex'
import user from './modules/user'
import cart from './modules/cart'
import products from './modules/products'

const store = createStore({
  modules: {
    user,
    cart,
    products
  },
  
  strict: process.env.NODE_ENV !== 'production'
})

// 热更新配置
if (import.meta.hot) {
  // 用户模块
  import.meta.hot.accept('./modules/user', (newModule) => {
    console.log('[HMR] Updating user module')
    store.hotUpdate({
      modules: {
        user: newModule.default
      }
    })
  })
  
  // 购物车模块
  import.meta.hot.accept('./modules/cart', (newModule) => {
    console.log('[HMR] Updating cart module')
    store.hotUpdate({
      modules: {
        cart: newModule.default
      }
    })
  })
  
  // 产品模块
  import.meta.hot.accept('./modules/products', (newModule) => {
    console.log('[HMR] Updating products module')
    store.hotUpdate({
      modules: {
        products: newModule.default
      }
    })
  })
}

export default store

模块文件 #

javascript
// store/modules/user.js
const state = () => ({
  profile: null,
  token: null
})

const mutations = {
  SET_PROFILE(state, profile) {
    state.profile = profile
  },
  SET_TOKEN(state, token) {
    state.token = token
  }
}

const actions = {
  async login({ commit }, credentials) {
    // ...
  }
}

const getters = {
  isLoggedIn: state => !!state.token
}

export default {
  namespaced: true,
  state,
  mutations,
  actions,
  getters
}

调试热更新 #

检查热更新状态 #

javascript
if (import.meta.hot) {
  console.log('HMR is enabled')
  
  import.meta.hot.on('vite:beforeUpdate', (update) => {
    console.log('Module will update:', update)
  })
  
  import.meta.hot.on('vite:afterUpdate', (update) => {
    console.log('Module updated:', update)
  })
}

Vue DevTools #

热更新后,Vue DevTools 可能需要重新连接。可以添加以下代码:

javascript
if (import.meta.hot) {
  import.meta.hot.dispose(() => {
    // 清理
  })
  
  import.meta.hot.accept(() => {
    // 更新后通知 DevTools
    if (window.__VUE_DEVTOOLS_GLOBAL_HOOK__) {
      window.__VUE_DEVTOOLS_GLOBAL_HOOK__.Vue.util.defineReactive(
        store,
        '_vm',
        store._vm
      )
    }
  })
}

常见问题 #

1. 状态丢失 #

javascript
// 确保模块导出的是函数形式
export default {
  state: () => ({ ... }),  // 正确
  // state: { ... }        // 可能导致状态丢失
}

2. 热更新不生效 #

javascript
// 确保路径正确
import.meta.hot.accept('./modules/user', ...)  // 相对路径

3. 类型错误 #

typescript
// TypeScript 中需要类型声明
declare const import.meta: {
  hot: {
    accept: (cb: (mod: any) => void) => void
  }
}

最佳实践 #

1. 开发环境启用 #

javascript
if (import.meta.hot) {
  // 只在开发环境配置
}

2. 模块独立更新 #

javascript
// 每个模块独立配置热更新
import.meta.hot.accept('./modules/user', ...)
import.meta.hot.accept('./modules/cart', ...)

3. 添加日志 #

javascript
import.meta.hot.accept('./modules/user', (newModule) => {
  console.log('[HMR] User module updated')
  store.hotUpdate({ modules: { user: newModule.default } })
})

总结 #

热更新要点:

要点 说明
配置方式 import.meta.hot.accept
更新方法 store.hotUpdate
状态保留 默认保留当前状态
调试 使用 console.log 和 DevTools

继续学习 useStore,了解组合式 API 中的使用。

最后更新:2026-03-28