“[vuex] 状态字段 foo 被 foobar 上的同名模块覆盖”,在玩笑中使用 deepmerge 辅助函数
Posted
技术标签:
【中文标题】“[vuex] 状态字段 foo 被 foobar 上的同名模块覆盖”,在玩笑中使用 deepmerge 辅助函数【英文标题】:"[vuex] state field foo was overridden by a module with the same name at foobar" with deepmerge helper function in jest 【发布时间】:2020-08-22 11:15:32 【问题描述】:我正在使用辅助函数在我的笑话中创建一个商店。辅助函数使用 deepmerge 将基本配置与自定义配置合并。这会导致多个控制台警告
[vuex] state field "cart" was overridden by a module with the same name at "cart"
[vuex] state field "customer" was overridden by a module with the same name at "customer"
[vuex] state field "checkout" was overridden by a module with the same name at "checkout"
store.js(为了演示目的而减少到最低限度)
import cart from './modules/cart'
import checkout from './modules/checkout'
import customer from './modules/customer'
Vue.use(Vuex)
export const config =
modules:
cart,
customer,
checkout,
,
export default new Vuex.Store(config)
test-utils.js
import merge from 'deepmerge'
import config as storeConfig from './vuex/store'
// merge basic config with custom config
export const createStore = config =>
const combinedConfig = (config)
? merge(storeConfig, config)
: storeConfig
return new Vuex.Store(combinedConfig)
利用里面的辅助函数
somejest.test.js
import createStore from 'test-utils'
const wrapper = mount(ShippingComponent,
store: createStore(
modules:
checkout:
state:
availableShippingMethods:
flatrate:
carrier_title: 'Flat Rate',
,
,
,
,
,
),
localVue,
)
如何解决控制台警告?
【问题讨论】:
【参考方案1】:我认为在这种情况下,警告有点误导。这在技术上是正确的,只是没有帮助。
以下代码将生成相同的警告。它不使用deepmerge
、vue-test-utils
或jest
,但我相信根本原因与原始问题相同:
const config =
state: ,
modules:
customer:
const store1 = new Vuex.Store(config)
const store2 = new Vuex.Store(config)
<script src="https://unpkg.com/vue@2.6.11/dist/vue.js"></script>
<script src="https://unpkg.com/vuex@3.4.0/dist/vuex.js"></script>
触发警告需要此示例的两个关键部分:
-
多家商店。
配置中的根
state
对象。
问题中的代码肯定有多个商店。一个在store.js
末尾创建,另一个由createStore
创建。
该问题未显示根 state
对象,但确实提到代码已减少。我假设完整的代码确实有这个对象。
那么为什么会触发这个警告呢?
模块state
存储在根state
对象中。即使我的示例中的模块没有明确包含任何state
,它仍然存在。此state
将存储在state.customer
。因此,当创建第一个商店时,它会向该根 state
对象添加一个 customer
属性。
目前没有问题。
但是,当创建第二个存储时,它使用相同的根 state
对象。在此阶段复制或合并配置将无济于事,因为复制的 state
也将具有 customer
属性。第二个商店也尝试将customer
添加到根state
。但是,它发现该属性已经存在,会感到困惑并记录警告。
官方文档中对此有一些报道:
https://vuex.vuejs.org/guide/modules.html#module-reuse
解决此问题的最简单方法是改用根 state
的函数:
state: () => ( /* all the state you currently have */ ),
每个商店都会调用该函数并获取自己的状态副本。这与对组件使用data
函数相同。
如果您实际上不需要 root state
,您也可以通过完全删除它来修复它。如果没有指定state
,那么 Vuex 每次都会创建一个新的根 state
对象。
【讨论】:
【参考方案2】:当状态中的属性名称与模块名称冲突时记录,如下所示:
new Vuex.Store(
state:
foo: 'bar'
,
modules:
foo:
)
因此会引发警告。
new Vuex.Store((
state:
cart: '',
customer: '',
checkout: ''
,
modules:
cart: ,
customer: ,
checkout: ,
))
最有可能在这里
export const createStore = config =>
const combinedConfig = (config)
? merge(storeConfig, config)
: storeConfig
return new Vuex.Store(combinedConfig)
来自 vuex 的源代码,它有助于指出这些错误在哪里引发以进行日志记录。
如果您在生产环境中运行应用程序,您知道不会引发此警告......或者您可能会拦截警告并立即返回;
vuex source code
const parentState = getNestedState(rootState, path.slice(0, -1))
const moduleName = path[path.length - 1]
store._withCommit(() =>
if (__DEV__)
if (moduleName in parentState)
console.warn(
`[vuex] state field "$moduleName" was overridden by a module with the same name at "$path.join('.')"`
)
Vue.set(parentState, moduleName, module.state)
)
vuex tests
jest.spyOn(console, 'warn').mockImplementation()
const store = new Vuex.Store(
modules:
foo:
state ()
return value: 1
,
modules:
value:
state: () => 2
)
expect(store.state.foo.value).toBe(2)
expect(console.warn).toHaveBeenCalledWith(
`[vuex] state field "value" was overridden by a module with the same name at "foo.value"`
)
【讨论】:
【参考方案3】:好吧,我相信没有必要在 test-utils.ts 中使用deepmerge
。我们最好使用 Vuex 本身来处理模块的合并,而不是与其他方法合并。
如果您在模拟模块上看到 Vuex testing with Jest 的文档
您需要传递所需的模块。
import createStore, createLocalVue from 'test-utils';
import Vuex from 'vuex';
const localVue = createLocalVue()
localVue.use(Vuex);
// mock the store in beforeEach
describe('MyComponent.vue', () =>
let actions
let state
let store
beforeEach(() =>
state =
availableShippingMethods:
flatrate:
carrier_title: 'Flat Rate',
,
,
actions =
moduleActionClick: jest.fn()
store = new Vuex.Store(
modules:
checkout:
state,
actions,
getters: myModule.getters // you can get your getters from store. No need to mock those
)
)
);
吹口哨,在测试用例中:
const wrapper = shallowMount(MyComponent, store, localVue )
希望这会有所帮助!
【讨论】:
以上是关于“[vuex] 状态字段 foo 被 foobar 上的同名模块覆盖”,在玩笑中使用 deepmerge 辅助函数的主要内容,如果未能解决你的问题,请参考以下文章