需求是做一套后台管理界面,要有权限管理的相关功能.相关思路是参照addRoutes实现后台管理功能这篇文章来的.具体做法是在登录时后台返回角色拥有的权限,通过调用addRoutes方法动态加载,vuex管理路由加载状态以及加载的路由,并在进行commit操作时存入sessionStorage.正常情况下登录后,菜单正常显示并可以点击出相关界面.F5页面刷新后从sessionStorage中取出重新加载路由数据,这时候菜单依然能正常显示,但是点击的时候就抛错:
vue.esm.js?65d7:431 [Vue warn]: Error in beforeCreate hook: "TypeError: Cannot read property 'call' of null"
初学小白,求大牛们帮忙看看问题出在哪里
login.vue代码片段
computed:{
...mapGetters([
'menuitems',
'isLoadRoutes'
// ...
])
},
methods: {
rememberPwd(){
if(this.checked){
localStorage.setItem('account',this.ruleForm2.account);
localStorage.setItem('password',this.ruleForm2.checkPass );
}
},
removePwd(){
if(this.checked){
localStorage.removeItem('account');
localStorage.removeItem('password');
}
},
handleReset2() {
this.$refs.ruleForm2.resetFields();
this.removePwd();
},
handleSubmit2(ev) {
var _this = this;
this.$refs.ruleForm2.validate((valid) => {
if (valid) {
//_this.$router.replace('/table');
this.logining = true;
//NProgress.start();
var loginParams = { username: this.ruleForm2.account, password: this.ruleForm2.checkPass };
requestLogin(loginParams).then(res => {
this.logining = false;
//NProgress.done();
if (res.data.resultCode !== "SUCCESS") {
this.$message({
message: res.data.resultDesc,
type: 'error'
});
} else {
this.loginUser=res.data.userProfile;
this.routes=res.data.routes;
if(this.loginUser.doRemove){
this.removePwd();
}else{
this.rememberPwd();
}
sessionStorage.setItem('user', JSON.stringify(res.data.userProfile));
sessionStorage.setItem('security', JSON.stringify(this.routes));
this.addMenu(this.routes);
/*this.$router.push(this.routes);*/
if (!this.isLoadRoutes) {
this.$router.addRoutes(this.routes);
for(let route of this.routes){
console.info(JSON.stringify(route));
this.$router.options.routes.push(route);
}
this.loadRoutes();
}
console.info('push to home')
this.$router.push({ path: '/' });
}
});
} else {
console.log('error submit!!');
return false;
}
});
},
...mapActions([
'addMenu',
'loadRoutes'
])
}
store.js 相关代码
import {ADD_MENU,LOAD_ROUTES,INIT_FROM_LS} from './mutations_type'
export const state = {
items: [
],
isLoadRoutes: false
}
export const mutations = {
[ADD_MENU] (state, menuItems) {
console.info('addmenu mutations');
if (menuItems.length === 0) {
state.items = []
} else {
state.items = menuItems;
sessionStorage.setItem('state.items',JSON.stringify(state.items));
}
},
[LOAD_ROUTES] (state) {
state.isLoadRoutes = !state.isLoadRoutes;
sessionStorage.setItem('state.isLoadRoutes',JSON.stringify(state.isLoadRoutes));
console.info('change load routes states ' +state.isLoadRoutes);
},
[INIT_FROM_LS](state){
if (sessionStorage.getItem('state.items')) {
state.items = JSON.parse(localStorage.getItem('state.items'));
}
if (sessionStorage.getItem('state.isLoadRoutes')) {
state.isLoadRoutes = JSON.parse(localStorage.getItem('state.isLoadRoutes'));
}
console.info('init from ls '+JSON.stringify(state));
}
}
getters.js
const menuitems = state => state.items
const isLoadRoutes = state => state.isLoadRoutes
export {
menuitems,
isLoadRoutes
}
action.js
import {ADD_MENU,LOAD_ROUTES,INIT_FROM_LS} from './mutations_type'
export const addMenu = ({ commit }, menuItems) => {
if (menuItems.length > 0) {
commit(ADD_MENU, menuItems)
}
}
export const loadRoutes = ({commit}) => {
commit(LOAD_ROUTES)
}
export const initFromLs=({commit})=>{
commit(INIT_FROM_LS)
}
store.js
import Vue from 'vue'
import Vuex from 'vuex'
import * as actions from './actions'
import * as getters from './getters'
import {mutations,state} from './menu'
Vue.use(Vuex)
// 创建 store 实例
export default new Vuex.Store({
state,
actions,
getters,
mutations
})
main.js
import Vue from 'vue'
import App from './App'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-default/index.css'
import VueRouter from 'vue-router'
import store from './vuex/store'
import Vuex from 'vuex'
import 'font-awesome/css/font-awesome.min.css'
import {state} from './vuex/menu'
import Login from './views/Login.vue'
import NotFound from './views/404.vue'
import Home from './views/Home.vue'
Vue.use(ElementUI)
Vue.use(VueRouter)
Vue.use(Vuex)
const router = new VueRouter({
routes:[
{
path: '/login',
component: Login,
name: '',
hidden: true
},
{
path: '/404',
component: NotFound,
name: '',
hidden: true
},
{
path: '/',
component: Home,
hidden: true
},
...generateRoutesFromMenu()
]
})
// Menu should have 2 levels.
function generateRoutesFromMenu (routes = []) {
store.dispatch('initFromLs');
for (let i = 0, l = state.items.length; i < l; i++) {
let item = state.items[i]
if (item.path) {
routes.push(item);
}
}
console.info('generate menu = '+state.items +' routes = '+routes);
return routes
}
router.beforeEach((to, from, next) => {
//NProgress.start();
if (to.path == '/login') {
sessionStorage.removeItem('user');
sessionStorage.removeItem('security');
sessionStorage.removeItem('state.items');
sessionStorage.removeItem('state.isLoadRoutes');
}
let user = JSON.parse(sessionStorage.getItem('user'));
if (!user && to.path != '/login') {
next({ path: '/login' })
} else {
next();
}
})
new Vue({
store,
router,
render: h => h(App)
}).$mount('#app')
初次登录后点击左侧菜单正常
F5刷新之后点击菜单抛错
.]
错误代码
vue.esm.js?65d7:520 TypeError: Cannot read property 'call' of null
at callHook (eval at <anonymous> (app.js:770), <anonymous>:2533:20)
at VueComponent.Vue._init (eval at <anonymous> (app.js:770), <anonymous>:3969:5)
at new VueComponent (eval at <anonymous> (app.js:770), <anonymous>:4140:12)
at createComponentInstanceForVnode (eval at <anonymous> (app.js:770), <anonymous>:3495:10)
at init (eval at <anonymous> (app.js:770), <anonymous>:3329:45)
at createComponent (eval at <anonymous> (app.js:770), <anonymous>:4871:9)
at createElm (eval at <anonymous> (app.js:770), <anonymous>:4814:9)
at VueComponent.patch [as __patch__] (eval at <anonymous> (app.js:770), <anonymous>:5309:9)
at VueComponent.Vue._update (eval at <anonymous> (app.js:770), <anonymous>:2300:19)
at VueComponent.updateComponent (eval at <anonymous> (app.js:770), <anonymous>:2416:10)
handleError @ vue.esm.js?65d7:520
callHook @ vue.esm.js?65d7:2534
Vue._init @ vue.esm.js?65d7:3968
VueComponent @ vue.esm.js?65d7:4139
createComponentInstanceForVnode @ vue.esm.js?65d7:3494
init @ vue.esm.js?65d7:3328
createComponent @ vue.esm.js?65d7:4870
createElm @ vue.esm.js?65d7:4813
patch @ vue.esm.js?65d7:5308
Vue._update @ vue.esm.js?65d7:2299
updateComponent @ vue.esm.js?65d7:2415
get @ vue.esm.js?65d7:2754
run @ vue.esm.js?65d7:2824
flushSchedulerQueue @ vue.esm.js?65d7:2591
(anonymous) @ vue.esm.js?65d7:652
nextTickHandler @ vue.esm.js?65d7:599
vue.esm.js?65d7:431 [Vue warn]: Failed to mount component: template or render function not defined.
found in
---> <Home> at C:\Users\Dio\git\vue-admin\src\views\Home.vue
<App> at C:\Users\Dio\git\vue-admin\src\App.vue
<Root>
为情所困2017-06-12 09:32:35
经过测试下来,发现发生这种问题的根本原因在于路由component参数在被json转换成字符串之后,然后转回对象的时候无法解析,我尝试了一下直接使用import Home from ‘xxx’ 来替代component的值之后便正常显示路由信息了。但是依然没从根本上解决该问题,头疼