実装のアイデア
1. フロントエンドは静的ルート (ログイン ログイン ページ、アクセス許可を必要としないデフォルト ルート) を定義します。
2. ユーザーがログインすると、インターフェイスを調整してユーザー情報を取得し、ホームページにログインします。
3フロントエンドとリアエンドから返されるルートの形式を定義します
4. beforeEach のルート ナビゲーション フックで、動的ルーティングを取得するようにインターフェイスを調整し、フロントエンドで利用可能なルーティング データとしてデータを再帰的に処理し、router.addRouters を使用します。 () ルーティングを追加します
ステップ 1: フロントエンドで静的ルートを定義します (アクセス許可を必要としないログイン ページのデフォルト ルート)
ルーター/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
// 解决重复点击路由报错的BUG
// 下面这段代码主要解决这个问题 :Uncaught (in promise) Error: Redirected when going from "/login" to "/index" via a navigation guard.
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => err)
}
// 定义好静态路由
const routes = [
{
path: '/login',
name: 'login',
component: () => import('../views/login'),
hidden: true,
},
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes,
})
export default router
ステップ 2: ユーザーがログインすると、インターフェースを呼び出してユーザー情報を取得し、ホームページにログインします。
ログイン/index.vue
methods: {
login () {
this.$refs.userForm.validate((valid) => {
if (valid) {
// 模拟登录接口去请求用户数据
setTimeout(() => {
// 这里的res就是模拟后台返回的用户数据
const res = dynamicUserData.filter((item) => item.username === this.user.username)[0]
console.log(res)
// 存储用户的信息及token到vuex,并做sessionStorage持久化处理
this.$store.commit('User/saveUserInfo',res)
Message({
type: 'success', message: "登录成功", showClose: true, duration: 3000 })
this.$router.push({
path: "/index" })
}, 1000)
} else return false
})
}
}
添付ファイル: vuex 永続化処理:vuex-persistedstate
プラグインを使用してユーザー ウェアハウスのコンテンツを sessionStorage に保存する
import Vue from 'vue'
import Vuex from 'vuex'
import User from './modules/user'
import permission from './modules/permission'
import createPersistedState from 'vuex-persistedstate'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
},
modules: {
User,
permission,
},
plugins: [
createPersistedState({
storage: window.sessionStorage, // 可选sessionStorage localStorage
reducer(val) {
return {
User: val.User,
}
},
}),
],
})
ステップ 3: フロントエンドとバックエンドでルートによって返される形式を定義する
// 后台返回的数据结构
const dynamicUser = [
{
name: '管理员',
avatar: 'https://sf3-ttcdn-tos.pstatp.com/img/user-avatar/ccb565eca95535ab2caac9f6129b8b7a~300x300.image',
desc: '管理员 - admin',
username: 'admin',
password: '654321',
token: 'rtVrM4PhiFK8PNopqWuSjsc1n02oKc3f',
routes: [
{
id: 1,
name: '/',
path: '/',
component: 'Layout',
redirect: '/index',
hidden: false,
children: [{
name: 'index', path: '/index', meta: {
title: 'index' }, component: 'index/index' }],
},
{
id: 2,
name: '/form',
path: '/form',
component: 'Layout',
redirect: '/form/index',
hidden: false,
children: [{
name: '/form/index', path: '/form/index', meta: {
title: 'form' }, component: 'form/index' }],
},
{
id: 3,
name: '/example',
path: '/example',
component: 'Layout',
redirect: '/example/tree',
meta: {
title: 'example' },
hidden: false,
children: [
{
name: '/tree', path: '/example/tree', meta: {
title: 'tree' }, component: 'tree/index' },
{
name: '/copy', path: '/example/copy', meta: {
title: 'copy' }, component: 'tree/copy' },
],
},
{
id: 4,
name: '/table',
path: '/table',
component: 'Layout',
redirect: '/table/index',
hidden: false,
children: [{
name: '/table/index', path: '/table/index', meta: {
title: 'table' }, component: 'table/index' }],
},
{
id: 5,
name: '/admin',
path: '/admin',
component: 'Layout',
redirect: '/admin/index',
hidden: false,
children: [{
name: '/admin/index', path: '/admin/index', meta: {
title: 'admin' }, component: 'admin/index' }],
},
{
id: 6,
name: '/people',
path: '/people',
component: 'Layout',
redirect: '/people/index',
hidden: false,
children: [{
name: '/people/index', path: '/people/index', meta: {
title: 'people' }, component: 'people/index' }],
},
],
},
{
name: '普通用户',
avatar: 'https://sf1-ttcdn-tos.pstatp.com/img/user-avatar/6364348965908f03e6a2dd188816e927~300x300.image',
desc: '普通用户 - people',
username: 'people',
password: '123456',
token: '4es8eyDwznXrCX3b3439EmTFnIkrBYWh',
routes: [
{
id: 1,
name: '/',
path: '/',
component: 'Layout',
redirect: '/index',
hidden: false,
children: [{
name: 'index', path: '/index', meta: {
title: 'index' }, component: 'index/index' }],
},
{
id: 2,
name: '/form',
path: '/form',
component: 'Layout',
redirect: '/form/index',
hidden: false,
children: [{
name: '/form/index', path: '/form/index', meta: {
title: 'form' }, component: 'form/index' }],
},
{
id: 3,
name: '/example',
path: '/example',
component: 'Layout',
redirect: '/example/tree',
meta: {
title: 'example' },
hidden: false,
children: [
{
name: '/tree', path: '/example/tree', meta: {
title: 'tree' }, component: 'tree/index' },
{
name: '/copy', path: '/example/copy', meta: {
title: 'copy' }, component: 'tree/copy' },
],
},
{
id: 4,
name: '/table',
path: '/table',
component: 'Layout',
redirect: '/table/index',
hidden: false,
children: [{
name: '/table/index', path: '/table/index', meta: {
title: 'table' }, component: 'table/index' }],
},
{
id: 6,
name: '/people',
path: '/people',
component: 'Layout',
redirect: '/people/index',
hidden: false,
children: [{
name: '/people/index', path: '/people/index', meta: {
title: 'people' }, component: 'people/index' }],
},
],
},
]
export default dynamicUser
ステップ 4: 各処理の前にナビゲーション フックをルーティングする
ルーティングフックロジック:
是否为白名单:
是: 直接进入
不是:判断是否有token
无token:跳转到login登录页
有token:判断用户路由状态(是否有路由)
有路由: 直接进入
无路由: 调接口获取动态路由
递归处理返回的路由
将递归处理好的路由存储到vuex,设置用户路由状态为true
使用router.addRouters()添加路由
ルーティング ナビゲーション ガード:
import router from './index'
import Layout from '../layout/index'
import NProgress from 'nprogress' // progress bar
import store from '@/store'
import menu from '@/mock/menu.js'
// 路由拼接
function loadView(view) {
return () => import(`@/views/${
view}`)
}
// 路由过滤 遍历路由 转换为组件对象和路径
function filterASyncRoutes(data) {
// console.log(data)
const routes = data.filter(item => {
if (item['component'] === 'Layout') {
item.component = Layout
} else {
item['component'] = loadView(item['component'])
}
// 路由递归,转换组件对象和路径
if (item['children'] && item['children'].length > 0) {
item['children'] = filterASyncRoutes(item.children)
}
return true
})
// 排序
routes.sort((a, b) => a['id'] - b['id'])
return routes
}
NProgress.configure({
showSpinner: false }) // NProgress Configuration
// 白名单页面直接进入
const whiteList = ['/login']
router.beforeEach((to, from, next) => {
NProgress.start()
// 白名单页面,不管是否有token,是否登录都直接进入
if (whiteList.indexOf(to.path) !== -1) {
next()
return false
}
// 有token(代表了有用户信息,但是不确定有没有路由信息)
if (store.state.User.token) {
// 判断当前用户是否是登录状态, 是登录状态则一定有路由,直接放行,不是登录状态则去获取路由菜单登录
// 刷新时store.state.routerList.hasRoutes会重置为false,重新去获取 异步路由
if (!store.state.routerList.hasRoutes) {
setTimeout(() => {
const res = menu.filter(item => item.token === store.state.User.token)[0].routes
const asyncRouter = filterASyncRoutes(res) // 递归处理后台返回的路由
store.commit('routerList/setRouterList', asyncRouter) // 存储到异步的路由到vuex
store.commit('routerList/setHasRoutes', true) // 设置登录状态为true
router.addRoutes(asyncRouter) // 动态添加可访问路由表
next({
...to, replace: true }) // hack方法 router.addRoutes之后的next()可能会失效,可能next()的时候路由并没有完全add完成 通过next(to)解决
}, 500)
} else {
next() //当有用户权限的时候,说明所有可访问路由已生成 如访问没权限的全面会自动进入404页面
}
}else {
next({
path:'/login'})
}
})
router.afterEach(() => {
// finish progress bar
NProgress.done()
})
注意
ここでは、非同期ルーティングとルーティング ステータスのデータが routerList.js に入れられていますが、
キャッシュがないのはこれが理由です。ユーザーが更新すると、ここのデータはリセットされ、フックが再ウォークされると、インターフェースはルーティング情報を取得するように調整されます。
(なぜルートを sessionStorage に置かないのでしょうか?その理由は、ルート配列内のコンポーネントを sessionStorage に保存できないためです。() => import(@/views/${userAuth.component}) は sessionStorage に保存できません。)
取得したユーザー情報は vuex に保存して永続化する必要がありますが、取得したメニューは vuex に永続化されないため、ユーザーは更新時に情報をクリアし、メニューを取得するインターフェイスを再取得します。これは次のコードです。
if (!store.state.routerList.hasRoutes) {
setTimeout(() => {
const res = menu.filter(item => item.token === store.state.User.token)[0].routes
const asyncRouter = filterASyncRoutes(res) // 递归处理后台返回的路由
store.commit('routerList/setRouterList', asyncRouter) // 存储到异步的路由到vuex
store.commit('routerList/setHasRoutes', true) // 设置登录状态为true
router.addRoutes(asyncRouter) // 动态添加可访问路由表
next({
...to, replace: true }) // hack方法 router.addRoutes之后的next()可能会失效,可能next()的时候路由并没有完全add完成 通过next(to)解决
}, 500)
}
当社は現在バックエンドリターンルーティングを使用して権限管理を行っています