“[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

我正在使用辅助函数在我的笑话中创建商店。 helper 函数使用 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)

测试-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,
})

如何解决控制台警告?

当状态中的 属性 名称与模块名称冲突时记录,如下所示:

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 的源代码中,它有助于指出这些错误在何处引发以进行日志记录。

如果您 运行 生产中的应用程序,您知道不会引发此警告...或者您可能会拦截警告并立即 return;

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"`
)

嗯,我觉得没必要在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 })

希望对您有所帮助!

我认为在这种情况下警告有些误导。这在技术上是正确的,只是没有帮助。

以下代码将生成相同的警告。它不使用 deepmergevue-test-utilsjest,但我相信根本原因与原始问题相同:

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>

此示例中有两个关键部分需要触发警告:

  1. 多家商店。
  2. 配置中的根 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 对象。