更新2
链接到在VueX git repo上创建的问题:https://github.com/vuejs/vuex/issues/1509
链接到仓库可以复制:https://github.com/djam90/vuex-vue-router-issue
更新:
此问题发生在VueX的此代码中,其中rawModule为undefined
,键为getters
。我认为这是因为循环依赖关系,即商店导入路由器但路由器导入商店。
function assertRawModule (path, rawModule) {
Object.keys(assertTypes).forEach(function (key) {
debugger;
if (!rawModule[key]) { return }
var assertOptions = assertTypes[key];
forEachValue(rawModule[key], function (value, type) {
assert(
assertOptions.assert(value),
makeAssertionMessage(path, key, type, value, assertOptions.expected)
);
});
});
}
我的项目是使用Vue CLI 3设置的,并使用Vue,Vue路由器,VueX和Mocha进行测试。
我遇到的情况是,我需要将路由器导入商店,但也需要将商店导入路由器。在浏览器中运行应用程序时,这似乎可以正常工作,但是如果我将商店或路由器导入到测试中,则会出现如下随机错误:
MOCHA Testing...
RUNTIME EXCEPTION Exception occurred while loading your tests
TypeError: Cannot read property 'getters' of undefined
at /home/dan/code/garage/ui/dist/webpack:/node_modules/vuex/dist/vuex.esm.js:261:1
at Array.forEach (<anonymous>)
at assertRawModule (/home/dan/code/garage/ui/dist/webpack:/node_modules/vuex/dist/vuex.esm.js:260:1)
at ModuleCollection.register (/home/dan/code/garage/ui/dist/webpack:/node_modules/vuex/dist/vuex.esm.js:186:1)
at /home/dan/code/garage/ui/dist/webpack:/node_modules/vuex/dist/vuex.esm.js:200:1
at /home/dan/code/garage/ui/dist/webpack:/node_modules/vuex/dist/vuex.esm.js:75:44
at Array.forEach (<anonymous>)
at forEachValue (/home/dan/code/garage/ui/dist/webpack:/node_modules/vuex/dist/vuex.esm.js:75:1)
at ModuleCollection.register (/home/dan/code/garage/ui/dist/webpack:/node_modules/vuex/dist/vuex.esm.js:199:1)
at new ModuleCollection (/home/dan/code/garage/ui/dist/webpack:/node_modules/vuex/dist/vuex.esm.js:160:1)
at new Store (/home/dan/code/garage/ui/dist/webpack:/node_modules/vuex/dist/vuex.esm.js:311:1)
at Module../src/store/store.js (/home/dan/code/garage/ui/dist/webpack:/src/store/store.js:12:16)
at __webpack_require__ (/home/dan/code/garage/ui/dist/webpack:/webpack/bootstrap:25:1)
at Module../src/router.js (/home/dan/code/garage/ui/dist/webpack:/src/router.js:1:1)
at __webpack_require__ (/home/dan/code/garage/ui/dist/webpack:/webpack/bootstrap:25:1)
at Module../src/store/modules/bookings.js (/home/dan/code/garage/ui/dist/webpack:/src/store/modules/bookings.js:1:1)
at __webpack_require__ (/home/dan/code/garage/ui/dist/webpack:/webpack/bootstrap:25:1)
at Module../tests/unit/store/bookings.spec.js (/home/dan/code/garage/ui/dist/webpack:/tests/unit/store/bookings.spec.js:1:1)
at __webpack_require__ (/home/dan/code/garage/ui/dist/webpack:/webpack/bootstrap:25:1)
at run (/home/dan/code/garage/ui/dist/webpack:/node_modules/mocha-webpack/lib/entry.js:3:20)
at Array.forEach (<anonymous>)
at Object../node_modules/mocha-webpack/lib/entry.js (/home/dan/code/garage/ui/dist/webpack:/node_modules/mocha-webpack/lib/entry.js:10:1)
at __webpack_require__ (/home/dan/code/garage/ui/dist/webpack:/webpack/bootstrap:25:1)
at /home/dan/code/garage/ui/dist/webpack:/webpack/bootstrap:116:1
at Object.<anonymous> (/home/dan/code/garage/ui/dist/main.js:120:10)
at Module._compile (internal/modules/cjs/loader.js:689:30)
at Object._module2.default._extensions.(anonymous function) [as .js] (/home/dan/code/garage/ui/node_modules/mocha-webpack/lib/util/registerRequireHook.js:148:12)
at Module.load (internal/modules/cjs/loader.js:599:32)
at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
at Function.Module._load (internal/modules/cjs/loader.js:530:3)
at Module.require (internal/modules/cjs/loader.js:637:17)
at require (internal/modules/cjs/helpers.js:22:18)
at /home/dan/code/garage/ui/node_modules/mocha/lib/mocha.js:250:27
at Array.forEach (<anonymous>)
at Mocha.loadFiles (/home/dan/code/garage/ui/node_modules/mocha/lib/mocha.js:247:14)
at Mocha.run (/home/dan/code/garage/ui/node_modules/mocha/lib/mocha.js:576:10)
这是我的路由器:
import Vue from 'vue'
import Router from 'vue-router'
import store from '@/store/store'
import storage from '@/services/storage'
import Login from './views/Login.vue'
import Dashboard from './views/Dashboard.vue'
import Bookings from './views/Bookings'
Vue.use(Router)
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/login',
name: 'login',
meta: {
layout: 'login'
},
component: Login
},
{
path: '/',
name: 'home',
component: Dashboard
},
{
path: '/bookings',
name: 'bookings',
component: Bookings
}
]
})
router.beforeEach((to, from, next) => {
const token = storage.get('token')
if (token && !store.state.auth.token) {
store.commit('auth/setAuthToken', token)
}
next()
})
export default router
这是我的商店预订模块:
import api from '@/api/bookings'
import router from '@/router'
const state = {
bookings: [],
lastAdded: null,
lastEdited: null,
errors: {
get: null,
add: null,
edit: null,
getById: null
},
booking: {}
}
export const getters = {
booking: state => {
return state.booking
}
}
export const actions = {
async getAll ({ commit }) {
try {
const response = await api.getAll()
commit('SET_BOOKINGS', response.data)
} catch (e) {
commit('SET_ERROR_FOR_GET', e)
}
},
async create ({ commit, state }, { booking }) {
try {
const response = await api.create(booking)
commit('SET_LAST_CREATED', response.data)
router.push({ name: 'bookings' })
} catch (e) {
commit('SET_ERROR_FOR_CREATE', e)
}
},
async getById ({ commit, state }, { id }) {
try {
const response = await api.getById(id)
commit('SET_BOOKING', response.data)
} catch (e) {
commit('SET_ERROR_FOR_GET_BY_ID', e)
}
}
}
export const mutations = {
SET_VEHICLES (state, vehicles) {
state.all = vehicles
},
SET_ERROR_FOR_GET (state, error) {
state.errors.get = error
},
SET_LAST_CREATED (state, customer) {
state.lastAdded = customer
},
SET_ERROR_FOR_CREATE (state, error) {
state.errors.add = error
},
SET_BOOKING (state, vehicle) {
state.vehicle = vehicle
},
SET_ERROR_FOR_GET_BY_ID (state, error) {
state.errors.getById = error
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
这是我的测试:
import chai from 'chai'
import sinon from 'sinon'
import sinonChai from 'sinon-chai'
import bookingsStore from '@/store/modules/bookings'
import api from '@/api/bookings'
// import router from '@/router'
import { stubBooking } from '../stubs/stubs'
chai.use(sinonChai)
const expect = chai.expect
const { getters, actions, mutations } = bookingsStore
// test getters
describe('getters', () => {
describe('booking', () => {
it('should return booking from state', () => {
const state = {
booking: {}
}
const actual = getters.booking(state)
expect(actual).to.deep.equal(state.booking)
})
})
})
// test actions
describe('actions', () => {
describe('create', () => {
let apiStub
let routerStub
beforeEach(() => {
apiStub = sinon.stub(api, 'create')
routerStub = sinon.stub(router, 'push')
})
afterEach(() => {
apiStub.restore()
routerStub.restore()
})
it('create success', async () => {
const commit = sinon.spy()
const state = {}
apiStub.resolves({
data: stubBooking
})
await actions.create({ commit, state }, { stubBooking })
expect(commit.args).to.deep.equal([
['SET_LAST_CREATED', stubBooking]
])
expect(router.push).to.have.been.called
})
})
})
如您所见,仅在测试中加入import bookingsStore from '@/store/modules/bookings'
就会发生错误。如果我删除它,则测试可以正常运行。
答案 0 :(得分:0)
我遇到了同样的问题,即存储导入路由器后仍未定义。
最终我选择使用路由器实例本身来访问商店:
router.beforeEach((to, from, next) => {
console.log('authenticated?', router.app.$store.getters['auth/isAuthenticated'])
// ...
})
请注意,我正在使用命名空间存储,因此可能是问题所在。无论如何,我都很喜欢这种解决方案。