詳解基於vue,vue-router, vuex以及addRoutes舉行權限掌握

基於vuex, vue-router,vuex的權限掌握教程,完全代碼地點見 https://github.com/linrunzhen…

接下來讓我們模仿一個普通用戶翻開網站的歷程,一步一步的走完全個流程。

起首從翻開當地的效勞localhost:8080最先,我們曉得翻開後會進入login頁面,那末推斷的依據是什麼。
起首是token。
沒有上岸的用戶是獵取不到token的,而上岸后的角色我們會將token存到local或許seesionStorage 因而,依據當前有無token即可曉得是不是上岸。

為了存取token而且輕易我們操縱,能夠配和vuex完成

/* state.js */
export default {
    get UserToken() {
        return localStorage.getItem('token')
    },
    set UserToken(value) {
        localStorage.setItem('token', value)
    }
}
/* mutation.js */
export default {
    LOGIN_IN(state, token) {
        state.UserToken = token
    },
    LOGIN_OUT(state) {
        state.UserToken = ''
    }
}

阻攔的推斷

  1. 沒有token進入須要權限的頁面:redirect到login頁面
  2. 由於我們路由是動態掛載的,包含 ‘ ‘ 和404,所以當婚配不到路由時,也重定向到login
router.beforeEach((to, from, next) => {
    if (!store.state.UserToken) {
        if (
            to.matched.length > 0 &&
            !to.matched.some(record => record.meta.requiresAuth)
        ) {
            next()
        } else {
            next({ path: '/login' })
        }
    } 
})

好了,此時用戶翻開localhost:8080,默許婚配的是”途徑,此時我們並沒有掛載路由,也沒有token,所以來到了login。

輸入用戶名暗碼后,有token了,經由過程store觸發 commit(‘LOGIN_IN’) 來設置token。

然則照樣沒有路由,如今最最先只要login路由

/* 初始路由 */
export default new Router({
    routes: [
        {
            path: '/login',
            component: Login
        }
    ]
})

/* 預備動態增加的路由 */
export const DynamicRoutes = [
    {
        path: '',
        component: Layout,
        name: 'container',
        redirect: 'home',
        meta: {
            requiresAuth: true,
            name: '首頁'
        },
        children: [
            {
                path: 'home',
                component: Home,
                name: 'home',
                meta: {
                    name: '首頁'
                }
            }
        ]
    },
    {
        path: '/403',
        component: Forbidden
    },
    {
        path: '*',
        component: NotFound
    }
]

我們要依據當前用戶的token去背景獵取權限。

由於權限這塊邏輯還挺多,所以在vuex增加了一個permission模塊來處置懲罰權限。

為了推斷是已有路由列表,須要在vuex的permission模塊存一個state狀況permissionList用來推斷,假如permissionList不為null,即已有路由,假如不存在,就須要我們幹活了。

router.beforeEach((to, from, next) => {
    if (!store.state.UserToken) {
        ...
    } else {
        /* 如今有token了 */
        if (!store.state.permission.permissionList) {
            /* 假如沒有permissionList,真正的事情最先了 */
            store.dispatch('permission/FETCH_PERMISSION').then(() => {
                next({ path: to.path })
            })
        } else {
            if (to.path !== '/login') {
                next()
            } else {
                next(from.fullPath)
            }
        }
    }
})

來看一下 store.dispatch(‘permission/FETCH_PERMISSION’) 都幹了什麼

actions: {
    async FETCH_PERMISSION({ commit, state }) {
       /*  獵取背景給的權限數組 */
        let permissionList = await fetchPermission()

        /*  依據背景權限跟我們定義好的權限對照,篩選出對應的路由並加入到path=''的children */
        let routes = recursionRouter(permissionList, dynamicRouter)
        let MainContainer = DynamicRoutes.find(v => v.path === '')
        let children = MainContainer.children
        children.push(...routes)

        /* 天生左邊導航菜單 */
        commit('SET_MENU', children)

        setDefaultRoute([MainContainer])

        /*  初始路由 */
        let initialRoutes = router.options.routes

        /*  動態增加路由 */
        router.addRoutes(DynamicRoutes)

        /* 完全的路由表 */
        commit('SET_PERMISSION', [...initialRoutes, ...DynamicRoutes])
    }
}

起首,await fetchPermission()獵取背景給的權限數組,花樣也許以下

{
    "code": 0,
    "message": "獵取權限勝利",
    "data": [
        {
            "name": "定單治理",
            "children": [
                {
                    "name": "定單列表"
                },
                {
                    "name": "臨盆治理",
                    "children": [
                        {
                            "name": "臨盆列表"
                        }                     
                    ]
                },
                {
                    "name": "退貨治理"
                }
            ]
        }
    ]
}

其次依據我們寫好的路由數組,舉行對照,過濾獲得我們要的路由

/* 這裡是我們寫好的須要權限推斷的路由 */
const dynamicRoutes = [
    {
        path: '/order',
        component: Order,
        name: 'order-manage',
        meta: {
            name: '定單治理'
        },
        children: [
            {
                path: 'list',
                name: 'order-list',
                component: OrderList,
                meta: {
                    name: '定單列表'
                }
            },
            {
                path: 'product',
                name: 'product-manage',
                component: ProductManage,
                meta: {
                    name: '臨盆治理'
                },
                children: [
                    {
                        path: 'list',
                        name: 'product-list',
                        component: ProductionList,
                        meta: {
                            name: '臨盆列表'
                        }
                    },
                    {
                        path: 'review',
                        name: 'review-manage',
                        component: ReviewManage,
                        meta: {
                            name: '考核治理'
                        }
                    }
                ]
            },
            {
                path: 'returnGoods',
                name: 'return-goods',
                component: ReturnGoods,
                meta: {
                    name: '退貨治理'
                }
            }
        ]
    }
]

export default dynamicRoutes

為了對照,我寫好了一個遞歸函數,用name和meta.name舉行對照 ,依據這個函數就能夠獲得我們想要的結果

/**
 *
 * @param  {Array} userRouter 背景返回的用戶權限json
 * @param  {Array} allRouter  前端設置好的一切動態路由的鳩合
 * @return {Array} realRoutes 過濾后的路由
 */

export function recursionRouter(userRouter = [], allRouter = []) {
    var realRoutes = []
    allRouter.forEach((v, i) => {
        userRouter.forEach((item, index) => {
            if (item.name === v.meta.name) {
                if (item.children && item.children.length > 0) {
                    v.children = recursionRouter(item.children, v.children)
                }
                realRoutes.push(v)
            }
        })
    })
    return realRoutes
}

獲得過濾后的數組后,加入到path為”的children下面

{
        path: '',
        component: Layout,
        name: 'container',
        redirect: 'home',
        meta: {
            requiresAuth: true,
            name: '首頁'
        },
        children: [
            {
                path: 'home',
                component: Home,
                name: 'home',
                meta: {
                    name: '首頁'
                }
            },
            <!-- 將上面獲得的東西加入到這裏 -->
            ...
        ]
    }

這個時刻,path為”的children就是我們左邊的導航菜單了,存到state的sidebarMenu待用。加入到children后,這時候DynamicRoutes就能夠加入到路由了。

/*  動態增加路由 */
router.addRoutes(DynamicRoutes)


 /*  初始路由 */
let initialRoutes = router.options.routes
/* 兼并起來,就是完全的路由了 */
commit('SET_PERMISSION', [...initialRoutes, ...DynamicRoutes])
路由增加完了,也就是action操縱終了了,即可在action.then內里挪用 next({ path: to.path })進去路由,這裏要注意, next內里要傳參數即要進入的頁面的路由信息,由於next傳參數后,當前要進入的路由會被廢除,轉而進入參數對應的路由,雖然是同一個路由,這麼做重要是為了確保addRoutes見效了。

進入路由后,要最先天生左邊菜單,之前我們已存到sidebarMenu了,如今須要做的只是遞歸天生菜單罷了,雖然用了element的導航菜單,然則為了遞歸路由,還須要本身封裝一下。這裏中心的處所是組件的name,在組件內里有children的處所,又再次運用本身,從而遍歷全部tree構造的路由。

<template>
    <div class="menu-container">
        <template v-for="v in menuList">
            <el-submenu :index="v.name" v-if="v.children&&v.children.length>0" :key="v.name">
                <template slot="title">
                    <i class="iconfont icon-home"></i>
                    <span>{{v.meta.name}}</span>
                </template>
                <el-menu-item-group>
                    <my-nav :menuList="v.children"></my-nav>
                </el-menu-item-group>
            </el-submenu>
            <el-menu-item :key="v.name" :index="v.name" @click="gotoRoute(v.name)" v-else>
                <i class="iconfont icon-home"></i>
                <span slot="title">{{v.meta.name}}</span>
            </el-menu-item>
        </template>
    </div>
</template>

<script>
export default {
    name: 'my-nav',
    props: {
        menuList: {
            type: Array,
            default: function() {
                return []
            }
        }
    },
    methods: {
        gotoRoute(name) {
            this.$router.push({ name })
        }
    }
}
</script>

革新頁面后,依據我們router.beforeEach的推斷,有token然則沒permissionList,我們是會從新觸發action去獵取路由的,所以無需憂鬱。然則導航菜單active結果會不見。不過我們已把el-menu-item的key設置為路由的name,那末我們只要在革新后,在afterEach把當前路由的name賦值給el-menu default-active即可。同理,在afterEach階段獵取一切matched的路由,即可完成麵包屑導航。

if (!store.state.permission.permissionList) {
    store.dispatch('permission/FETCH_PERMISSION').then(() => {
        next({ path: to.path })
    })
} 



...
router.afterEach((to, from, next) => {
    var routerList = to.matched
    store.commit('setCrumbList', routerList)
    store.commit('permission/SET_CURRENT_MENU', to.name)
})

退出上岸后,須要革新頁面,由於我們是經由過程addRoutes增加的,router沒有deleteRoutes這個api,所以消滅token,消滅permissionList等信息,革新頁面是最保險的。

末了另有一點,每次要求得帶上token, 能夠對axios封裝一下來處置懲罰
var instance = axios.create({
    timeout: 30000,
    baseURL
})

// 增加要求阻攔器
instance.interceptors.request.use(
    function(config) {
        // 要求頭增加token
        if (store.state.UserToken) {
            config.headers.Authorization = store.state.UserToken
        }
        return config
    },
    function(error) {
        return Promise.reject(error)
    }
)

/* axios要求二次封裝 */
instance.get = function(url, data, options) {
    return new Promise((resolve, reject) => {
        axios
            .get(url, data, options)
            .then(
                res => {
                    var response = res.data
                    if (response.code === 0) {
                        resolve(response.data)
                    } else {
                        Message.warning(response.message)
                        /* reject(response.message) */
                    }
                },
                error => {
                    if (error.response.status === 401) {
                        Message.warning({
                            message: '上岸超時,請從新登錄'
                        })
                        store.commit('LOGIN_OUT')
                        window.location.reload()
                    } else {
                        Message.error({
                            message: '體系非常'
                        })
                    }
                    reject(error)
                }
            )
            .catch(e => {
                console.log(e)
            })
    })
}

export default instance
    原文作者:潤林君
    原文地址: https://segmentfault.com/a/1190000014694749
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞