vue+vue-router+vuex+axios+elementUI做后台管理界面在刷新之后菜单点击报错

需求是做一套后台管理界面,要有权限管理的相关功能.相关思路是参照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')

初次登录后点击左侧菜单正常
clipboard.png

F5刷新之后点击菜单抛错
clipboard.png
.]

错误代码

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>
阅读 14k
3 个回答

经过测试下来,发现发生这种问题的根本原因在于路由component参数在被json转换成字符串之后,然后转回对象的时候无法解析,我尝试了一下直接使用import Home from ‘xxx’ 来替代component的值之后便正常显示路由信息了。但是依然没从根本上解决该问题,头疼

大概看了一下 我觉得你没必要把路由信息都存到sessionStorage中去的 只要存登录获取的信息就行了吧

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏