bencodezen / vue-enterprise-boilerplate

An ever-evolving, very opinionated architecture and dev environment for new Vue SPA projects using Vue CLI.
7.78k stars 1.32k forks source link

Vuex Hot Reloading? #108

Closed vietquocnguyen closed 5 years ago

vietquocnguyen commented 5 years ago

Can someone please help me implement the Hot Reloading for Vuex? I checked out https://vuex.vuejs.org/guide/hot-reload.html. I understand the sample code, but the fancy way of autoloading the modules in this boilerplate confuses me. Thank you.

chrisvfritz commented 5 years ago

I just added a commit with support for hot reloading Vuex modules. Thanks for the suggestion! 🙂

mikerockett commented 5 years ago

I'm trying to implement this as a separate class utility as part of a package I'm putting together. Right now, I have a StoreFactory class (collapsed at the end of this comment) that uses static methods to do 'all the things', and is used as follows:

//src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import { StoreFactory } from 'utilities/factories'

Vue.use(Vuex)

const context = require.context('./modules', true, /^((?!\.unit\.).)*\.js$/)

const options = {
  createNestedMutations: true,
  createSyncHelpers: true,
}

export let store = StoreFactory.createStore(context, module, options)

Here, the factory accepts the context, current module (will get to this now) and some options. Internally, it builds up the modules the same way you do, also popping on some extra stuff (as seen in options). Then, it passes what it build into a new Vuex Store, which then gets passed back for export.

In experimenting with a way to handle HMR in this context, the closest I can get to it actually working is to pass module from store/index.js into the utility. HMR didn't even kick in when using module from the store factory class file. This could be where things are going wrong:

An initial change to any of the store modules does result in an HMR update, however a warning is emitted for each module:

[HMR] unexpected require(./src/store/modules/auth.js) from disposed module ./src/store/modules sync recursive ^((?!.unit.).)*.js$

At this point, store.hotUpdate does its thing, as expected, but then any further changes result in a page reload. I can't figure out what's wrong here. Could it be because of the aforementioned module being passed into createStore? As I'm not familiar with the internals of HMR, I don't know why that would be, if it were the case.

I've perused google-land for some insight into the actual warning, but none of what's been discussed around it is relevant to my case – guess I'm just misunderstanding how HMR actually works.

Have you bumped into this before? Any clues as to how I could get it working?

store-factory.js

```js import { Store } from 'vuex' import { normaliseFilename } from '../../utilities/string' import { getter, mutator } from '../../vuex/mapper' import { createNestedMutations } from '../../vuex/nested-mutations' export class StoreFactory { static init(context, module, options = {}) { this.context = context this.cachedModules = {} this.module = module this.store = { modules: {} } this.options = { createSyncHelpers: false, createNestedMutations: false, plugins: [], strict: false, devtools: true, cache: true, ...options } } static createStore(context, module, options = {}) { this.init(context, module, options) this.requireModules() return this.buildStore() } static buildStore() { const { plugins, strict, devtools } = this.options const store = new Store({ modules: this.store.modules, plugins, strict, devtools }) this.handleHotUpdates(store) return store } static requireModules() { this.context.keys().forEach(filename => { const definition = this.context(filename) if (this.options.cache) { if (this.cachedModules[filename] === definition) return this.cachedModules[filename] = definition } const module = { namespaced: true, ...definition } this.createNestedMutations(module) this.createSyncHelpers(module) const path = normaliseFilename(filename), { modules } = this.getNamespace(this.store, path) modules[path.pop()] = module }) } static getNamespace(subtree, path) { if (path.length === 1) return subtree const namespace = path.shift() subtree.modules[namespace] = { modules: {}, namespaced: true, ...subtree.modules[namespace] } return this.getNamespace(subtree.modules[namespace], path) } static createNestedMutations(module) { if (this.options.createNestedMutations) { module.mutations = createNestedMutations(module.mutations || {}) } } static createSyncHelpers(module) { if (this.options.createSyncHelpers) { module.getters = { ...module.getters || {}, getter } module.mutations = { ...module.mutations || {}, mutator } } } static handleHotUpdates(store) { if (this.options.cache && this.module.hot && this.module.hot.active) { this.module.hot.accept(this.context.id, () => { this.requireModules() this.handleHotUpdates(store) store.hotUpdate({ modules: this.store.modules }) }) } } } ```