首頁  >  問答  >  主體

javascript - 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')

初次登入後點選左側選單正常

#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>
天蓬老师天蓬老师2708 天前2530

全部回覆(3)我來回復

  • 为情所困

    为情所困2017-06-12 09:32:35

    經過測試下來,發現發生這種問題的根本原因在於路由component參數在被json轉換成字串之後,然後轉回物件的時候無法解析,我嘗試了一下直接使用import Home from 'xxx' 來取代component的值之後便正常顯示路由資訊了。但依然沒從根本解決該問題,頭痛

    回覆
    0
  • 三叔

    三叔2017-06-12 09:32:35

    大概看了一下 我覺得你沒必要把路由資訊都存到sessionStorage中去的 只要存登入取得的資訊就行了吧

    回覆
    0
  • 某草草

    某草草2017-06-12 09:32:35

    我覺得可以參考這篇文章/a/11...

    回覆
    0
  • 取消回覆