vue動態路由,前端請求路由。addRoutes。
需求:
之前都是前端固定寫好路由,然後跳來跳去,雖然做的有根據登錄者類型去做左側菜單篩選(隱藏,顯示),也添加了路由守衛對路由進行攔截處理。但是並不安全,因爲在初次加載時就默認已經加載了全部路由信息,而且登陸角色類型是存儲在緩存中的,稍微修改就會導致菜單重新刷新獲取,這樣路由、權限也就亂了。具體做法請看 靜態路由,分角色進行權限控制。綜合考慮後,決定採用動態獲取路由的方式去做。
代碼 :
router文件
首先,router/index中引入必須的路由。比如登錄,像登陸這種是不需要放在後臺進行獲取的。其中Home路由是我左右佈局的路由,這個就固定寫在上面了。login登錄需要,也是放在固定的路由中。
import Vue from 'vue'
import Router from 'vue-router'
const Home = () => import('@/components/Home') //主菜單
Vue.use(Router)
export default new Router({
routes: [
//此路由位置,誰排在前面先加載誰
{
path: '/',
redirect: '/login',
hidden:true,
userTypeArry:['1','2','3']
},
{
path: '/login',
name: 'login',
leaf: true,
component: resolve =>require(['@/components/Login'],resolve),
userTypeArry:['1','2','3'],
hidden:true
},
]
});
模擬請求路由
去模擬一個請求,其中post這是我封裝的一個axios。其實就是返回來了一個對象數組,至於res.data.data.permissions[3]這個是我的模擬數據,模擬取一條出來。裏面的child裏面的path就是別名,component就是後臺返回的組件路由url,name就是你給的名字,其中conponent中的路由信息不能全部放在後臺,比如這樣 ‘@/components/view/braceletManage/braceletList’,這樣是加載不成功的,會報錯‘ the request of a dependency is an expression’,所以要採用拼接的寫法。
import axios from 'axios';
import {get,post} from './request' //封裝axios
function getMenuList(objdatas){
let data = post('/eBackUser/login',objdatas).then(function(res){
console.log(res);
let menulist = null;
if(res.data){
let routerInfo = res.data.data.permissions[3];
//routerInfo.childPermission[0].url;
menulist= [{
path: '',
name: routerInfo.name,
component: resolve =>require(['@/components/Home'],resolve),
userTypeArry:['1','2','3'],
iconCls: 'iconmenu iconfont icon-zhinengshouhuani',
children: [
{ path: routerInfo.childPermission[0].permission, component: resolve =>require(['@/components/view/'+routerInfo.childPermission[0].url],resolve), name: routerInfo.childPermission[0].name }
]
}
];
}
return menulist;
})
return data;
};
export default getMenuList;
登錄
點擊登錄去調用獲取動態路由的函數(getMenuList),注意獲取路由時,因爲要發請求,所以要搞成同步的,要使用async和awit,不然添加路由會出錯。outer.options.routes.push放的是一個個對象,router.addRoutes放的是一個數組對象,添加路由後,我用一個 “l” 把添加後的路由長度進行了記錄存儲,在刷新頁面路由丟失時用到。這個時候不出意外應該是添加成功了,可以登錄進去看到正常的菜單了。
methods: {
login() {
let that = this;
that.loginicon = "el-icon-loading";
if(that.user_name != "" && that.password!= ""){
let obj={
user_name:'admin_ahjd',
password:'5a673451b6974da765d58f9845ebac2c'
}
that.baseFun.setSessionStorage("uandp",JSON.stringify(obj));
that.$store.commit("setUandp",that.baseFun.getSessionStorage("uandp"));
getInfo(obj);
}
async function getInfo(obj){
let infos = await getMenuList(obj);
if(infos!=null){
// return false;
router.options.routes.push(infos[0]);
router.addRoutes(infos);
window.sessionStorage.setItem("l",router.options.routes.length);
that.$router.push("/UserList");
}else{
that.$message({
type: 'error',
message: '接口異常!'
});
return false;
}
}
}
},
解析菜單
解析菜單沒什麼好說的,就不寫了。
刷新頁面菜單丟失!
路由添加成功後,點擊正常,但是刷新頁面後addRoutes添加的路由就全部沒了,這個時候需要在main.js中就行邏輯處理,main.js中的代碼,每次頁面在刷新時都會重新執行一遍,所以要在main.js中重新去獲取一遍路由,注意:main.js中的代碼初次進入就會執行一次,爲了防止初次進入就執行跟登陸時執行 導致添加兩次路由的情況出現,需要進行判斷,在初次進入時判斷 “l” 有無值,初次進入時 “l”肯定沒有值,所以初次進入時是不會執行獲取請求的操作,而有值的情況下(有值的情況就是你登錄之後的情況),這個時候因爲你剛登陸。“l”的長度肯定是和路由長度相等的,這個時候也不會進行重複請求路由操作。但是如果你登陸後做刷新頁面操作,這個時候路由的長度就變了,就和"l"不相等了,這個時候就會重新去請求路由菜單。
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import 'babel-polyfill'
import Vue from 'vue'
import App from './App'
import baseFun from './baseFun'// 引入公共方法函數
import {get,post} from './request' //封裝axios
Vue.prototype.baseFun = baseFun
Vue.prototype.server = {get,post}
import router from './router' //路由
import ElementUI from 'element-ui'; //餓了麼UI
import 'element-ui/lib/theme-chalk/index.css'; //餓了麼UI
import store from './store'
import Viewer from 'v-viewer'//安裝點擊放大視圖依賴
import 'viewerjs/dist/viewer.css'//放大視圖
import getMenuList from './getMenu'
Vue.use(Viewer)
Vue.config.productionTip = false
Vue.use(ElementUI); //餓了麼UI
//路由跳轉後返回頂部
router.afterEach((to,from,next) => {
window.scrollTo(0,0);
});
async function getMenu(){
let upObj = JSON.parse(baseFun.getSessionStorage("uandp"));
let infos = await getMenuList(upObj);
router.options.routes.push(infos[0]);
router.addRoutes(infos);
}
if(baseFun.getSessionStorage("l") && typeof(baseFun.getSessionStorage("l")) != "undefined"){
if(baseFun.getSessionStorage("l") != router.options.routes.length){
console.log(router);
console.log("!===");
getMenu();
}
}
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
退出系統,清空添加的路由。
登陸後添加成功路由後,這個時候如果點擊退出,退到登陸頁面。這個時候需要清空路由,不然這個時候再點擊登錄,就會重複添加。刷新當前頁面就可以清空。window.locaton.reload();但是這個刷新操作放在什麼地方呢,起初我是放在退出的vue文件裏面,但是不行,執行刷新操作後後面的代碼就會阻斷,就不會跳往登錄了。如果放在登錄組件中也不行,會導致死循環刷新,後來我想到通過傳參的方式解決。傳參有兩種方式 一種是push一種是params。通過push傳遞的參數在刷新頁面後會丟失。所以我就用這種方式來做。在login的生命週期函數中通過判斷有無參數進行刷新操作。另外退出時要刪除 "l"
退出
signOut() {
this.$confirm('確定要退出嗎?', '提示', {
confirmButtonText: '確定',
cancelButtonText: '取消',
type: 'warning',
customClass: "messageBox-customClass"
}).then(() => {
window.sessionStorage.removeItem("l"); //退出時刪除l
this.$router.push({
name:"login",
params:{
from:'goout'
}
}) ;
}).catch(() => {
});
},
login組件內
mounted() {
if(this.$route.params.from == "goout"){ //如果地址欄有from參數則進行刷新當前頁面。
console.log("執行刷新,清空路由!");
window.location.reload();
}
}
做的時候借鑑了這位大神的文章-----------鏈接,感謝她的分享。