mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-23 14:15:13 +00:00
feat: add store module HMR (#4582)
* feat: add store module HMR * fix: replace export with window.$nuxt Thanks to @pi0 for the suggestion :) * refactor: apply only in dev mode on client side * test: make store module test more descriptive * fix: clear modules to apply HMR * fix: remove console.log * fix: e2e tests * refactor: use void
This commit is contained in:
parent
5b58272d1a
commit
b2eee1772e
@ -3,73 +3,87 @@ import Vuex from 'vuex'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
const files = require.context('@/<%= dir.store %>', true, /^\.\/(?!<%= ignorePrefix %>)[^.]+\.(<%= extensions %>)$/)
|
||||
const filenames = files.keys()
|
||||
|
||||
// Store
|
||||
let storeData = {}
|
||||
|
||||
// Check if {dir.store}/index.js exists
|
||||
const indexFilename = filenames.find(filename => filename.includes('./index.'))
|
||||
let files;
|
||||
|
||||
if (indexFilename) {
|
||||
storeData = getModule(indexFilename)
|
||||
}
|
||||
void function updateModules() {
|
||||
files = require.context('@/<%= dir.store %>', true, /^\.\/(?!<%= ignorePrefix %>)[^.]+\.(<%= extensions %>)$/)
|
||||
const filenames = files.keys()
|
||||
|
||||
// If store is not an exported method = modules store
|
||||
if (typeof storeData !== 'function') {
|
||||
// Store modules
|
||||
if (!storeData.modules) {
|
||||
storeData.modules = {}
|
||||
// Check if {dir.store}/index.js exists
|
||||
const indexFilename = filenames.find(filename => filename.includes('./index.'))
|
||||
|
||||
if (indexFilename) {
|
||||
storeData = getModule(indexFilename)
|
||||
}
|
||||
|
||||
for (const filename of filenames) {
|
||||
let name = filename.replace(/^\.\//, '').replace(/\.(<%= extensions %>)$/, '')
|
||||
if (name === 'index') continue
|
||||
|
||||
const namePath = name.split(/\//)
|
||||
|
||||
name = namePath[namePath.length - 1]
|
||||
if (['state', 'getters', 'actions', 'mutations'].includes(name)) {
|
||||
const module = getModuleNamespace(storeData, namePath, true)
|
||||
appendModule(module, filename, name)
|
||||
continue
|
||||
// If store is not an exported method = modules store
|
||||
if (typeof storeData !== 'function') {
|
||||
// Store modules
|
||||
if (!storeData.modules || module.hot) {
|
||||
storeData.modules = {}
|
||||
}
|
||||
|
||||
// If file is foo/index.js, it should be saved as foo
|
||||
const isIndex = (name === 'index')
|
||||
if (isIndex) {
|
||||
namePath.pop()
|
||||
}
|
||||
for (const filename of filenames) {
|
||||
let name = filename.replace(/^\.\//, '').replace(/\.(<%= extensions %>)$/, '')
|
||||
if (name === 'index') continue
|
||||
|
||||
const module = getModuleNamespace(storeData, namePath)
|
||||
const fileModule = getModule(filename)
|
||||
const namePath = name.split(/\//)
|
||||
|
||||
name = namePath.pop()
|
||||
module[name] = module[name] || {}
|
||||
|
||||
// if file is foo.js, existing properties take priority
|
||||
// because it's the least specific case
|
||||
if (!isIndex) {
|
||||
module[name] = Object.assign({}, fileModule, module[name])
|
||||
module[name].namespaced = true
|
||||
continue
|
||||
}
|
||||
|
||||
// if file is foo/index.js we want to overwrite properties from foo.js
|
||||
// but not from appended mods like foo/actions.js
|
||||
const appendedMods = {}
|
||||
if (module[name].appends) {
|
||||
appendedMods.appends = module[name].appends
|
||||
for (const append of module[name].appends) {
|
||||
appendedMods[append] = module[name][append]
|
||||
name = namePath[namePath.length - 1]
|
||||
if (['state', 'getters', 'actions', 'mutations'].includes(name)) {
|
||||
const module = getModuleNamespace(storeData, namePath, true)
|
||||
appendModule(module, filename, name)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
module[name] = Object.assign({}, module[name], fileModule, appendedMods)
|
||||
module[name].namespaced = true
|
||||
// If file is foo/index.js, it should be saved as foo
|
||||
const isIndex = (name === 'index')
|
||||
if (isIndex) {
|
||||
namePath.pop()
|
||||
}
|
||||
|
||||
const module = getModuleNamespace(storeData, namePath)
|
||||
const fileModule = getModule(filename)
|
||||
|
||||
name = namePath.pop()
|
||||
module[name] = module[name] || {}
|
||||
|
||||
// if file is foo.js, existing properties take priority
|
||||
// because it's the least specific case
|
||||
if (!isIndex) {
|
||||
module[name] = Object.assign({}, fileModule, module[name])
|
||||
module[name].namespaced = true
|
||||
continue
|
||||
}
|
||||
|
||||
// if file is foo/index.js we want to overwrite properties from foo.js
|
||||
// but not from appended mods like foo/actions.js
|
||||
const appendedMods = {}
|
||||
if (module[name].appends) {
|
||||
appendedMods.appends = module[name].appends
|
||||
for (const append of module[name].appends) {
|
||||
appendedMods[append] = module[name][append]
|
||||
}
|
||||
}
|
||||
|
||||
module[name] = Object.assign({}, module[name], fileModule, appendedMods)
|
||||
module[name].namespaced = true
|
||||
}
|
||||
// If the environment supports hot reloading...
|
||||
<% if (isDev) { %>
|
||||
if (process.client && module.hot) {
|
||||
// Whenever any Vuex module is updated...
|
||||
module.hot.accept(files.id, () => {
|
||||
// Update `root.modules` with the latest definitions.
|
||||
updateModules()
|
||||
// Trigger a hot update in the store.
|
||||
window.<%= globals.nuxt %>.$store.hotUpdate({ modules: storeData.modules })
|
||||
})
|
||||
}<% } %>
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// createStore
|
||||
export const createStore = storeData instanceof Function ? storeData : () => {
|
||||
|
@ -78,8 +78,10 @@ describe('basic browser', () => {
|
||||
test('/store', async () => {
|
||||
await page.nuxt.navigate('/store')
|
||||
|
||||
expect(await page.$text('h1')).toBe('Vuex Nested Modules')
|
||||
expect(await page.$text('p')).toBe('1')
|
||||
expect(await page.$text('h1')).toBe('foo/bar/baz: Vuex Nested Modules')
|
||||
expect(await page.$text('h2')).toBe('index/counter: 1')
|
||||
expect(await page.$text('h3')).toBe('foo/blarg/getVal: 4')
|
||||
expect(await page.$text('h4')).toBe('foo/bab/getBabVal: 10')
|
||||
})
|
||||
|
||||
test('/head', async () => {
|
||||
|
10
test/fixtures/basic/pages/store.vue
vendored
10
test/fixtures/basic/pages/store.vue
vendored
@ -1,12 +1,14 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>{{ baz }}</h1>
|
||||
<h1>foo/bar/baz: {{ baz }}</h1>
|
||||
<br>
|
||||
<p>{{ $store.state.counter }}</p>
|
||||
<h2>index/counter: {{ $store.state.counter }}</h2>
|
||||
<br>
|
||||
<h2>{{ getVal }}</h2>
|
||||
<h3>foo/blarg/getVal: {{ getVal }}</h3>
|
||||
<br>
|
||||
<h3>{{ getBabVal }}</h3>
|
||||
<h4>foo/bab/getBabVal: {{ getBabVal }}</h4>
|
||||
<br>
|
||||
<button @click="$store.commit('increment')">+1</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -70,8 +70,10 @@ describe('basic ssr', () => {
|
||||
|
||||
test('/store', async () => {
|
||||
const { html } = await nuxt.server.renderRoute('/store')
|
||||
expect(html).toContain('<h1>Vuex Nested Modules</h1>')
|
||||
expect(html).toContain('<p>1</p>')
|
||||
expect(html).toContain('<h1>foo/bar/baz: Vuex Nested Modules</h1>')
|
||||
expect(html).toContain('<h2>index/counter: 1</h2>')
|
||||
expect(html).toContain('<h3>foo/blarg/getVal: 4</h3>')
|
||||
expect(html).toContain('<h4>foo/bab/getBabVal: 10</h4>')
|
||||
})
|
||||
|
||||
test('/head', async () => {
|
||||
|
Loading…
Reference in New Issue
Block a user