需求是做一套后台管理界面,要有权限管理的相关功能.相关思路是参照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 (app.js:770), :2533:20) at VueComponent.Vue._init (eval at (app.js:770), :3969:5) at new VueComponent (eval at (app.js:770), :4140:12) at createComponentInstanceForVnode (eval at (app.js:770), :3495:10) at init (eval at (app.js:770), :3329:45) at createComponent (eval at (app.js:770), :4871:9) at createElm (eval at (app.js:770), :4814:9) at VueComponent.patch [as __patch__] (eval at (app.js:770), :5309:9) at VueComponent.Vue._update (eval at (app.js:770), :2300:19) at VueComponent.updateComponent (eval at (app.js:770), :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 ---> at C:\Users\Dio\git\vue-admin\src\views\Home.vue at C:\Users\Dio\git\vue-admin\src\App.vue
经过测试下来,发现发生这种问题的根本原因在于路由component参数在被json转换成字符串之后,然后转回对象的时候无法解析,我尝试了一下直接使用import Home from ‘xxx’ 来替代component的值之后便正常显示路由信息了。但是依然没从根本上解决该问题,头疼
大概看了一下 我觉得你没必要把路由信息都存到sessionStorage中去的 只要存登录获取的信息就行了吧
我觉得可以参考一下这篇文章/a/11...