• 关于动态注册组件的问题


    关于动态注册组件的问题

    遗留问题

    上节课菜单,你们放在PugMenu.vue的生命周期进行异步调用合理吗?

    • 答案是:不合理

    • 原因:每次刷新都会去服务器查询和同步一次菜单,着实没必要。

    • 解决方案:只查询,放入状态管理,让menuList存入sessionStorage中。

    动态注册组件

    • 对路由的添加通常是通过 routes 选项来完成的,

    • 但是在某些情况下,你可能想在应用程序已经运行的时候添加或删除路由。

    背后含义是什么?

    • 动态注册路由:就是在执行的过程中,我才确定根路由,父子路由的关系。从而提升性能。和网页加载速度。
    • 默认情况下:如果路由全部通过routes和父子路由children指定以后,都是静态加载,这种静态加载会在启动项目时候,全部把路由中所有的js,css, template全部编译一边,然后合并成一个js。这个js非常的大。影响加载的速度和性能。
    • 动态路由:在访问的时候才确定,把需要的路由相关js/css/tempalte进行合并js,在进行渲染。懒加载。

    非动态绑定

    import { createRouter, createWebHistory } from 'vue-router'
    import store from '@/store'
    import { showFullLoading, hideFullLoading, toastError } from '@/utils'
    
    
    import Index from '@/views/PugAdmin.vue'
    import Dashboard from '@/views/dashboard/Index.vue'
    
    import ProductList from '@/views/product/List.vue'
    import CategoryList from '@/views/category/List.vue'
    import CouponList from '@/views/coupon/List.vue'
    import UserList from '@/views/user/List.vue'
    import OrderList from '@/views/order/List.vue'
    import ImageList from '@/views/image/List.vue'
    import NoticeList from '@/views/notice/List.vue'
    import LevelList from '@/views/level/List.vue'
    
    import ManagerList from '@/views/manager/List.vue'
    import RoleList from '@/views/role/List.vue'
    import PermissionList from '@/views/permission/List.vue'
    
    
    // 动态路由,用于匹配菜单动态添加路由
    const asyncRoutes = [{
        path: '/',
        name: "dashboard",
        meta: { title: '后台首页' },
        component: Dashboard
    }, {
        path: '/user/list',
        name: "/user/list",
        meta: { title: '用户管理' },
        component: UserList
    }, {
        path: '/manager/list',
        name: "/manager/list",
        meta: { title: '后台管理员' },
        component: ManagerList
    }, {
        path: '/role/list',
        name: "/role/list",
        meta: { title: '角色管理' },
        component: RoleList
    }, {
        path: '/permission/list',
        name: "/permission/list",
        meta: { title: '权限管理' },
        component: PermissionList
    }, {
        path: '/user/list',
        name: "/user/list",
        meta: { title: '用户管理' },
        component: UserList
    }, {
        path: '/level/list',
        name: "/level/list",
        meta: { title: '会员等级' },
        component: LevelList
    }, {
        path: "/category/list",
        name: "/category/list",
        component: CategoryList,
        meta: { title: "分类列表" }
    }, {
        path: "/coupon/list",
        name: "/coupon/list",
        component: CouponList,
        meta: { title: "优惠券管理" }
    }, {
        path: "/order/list",
        name: "/order/list",
        component: OrderList,
        meta: {
            title: "订单列表"
        }
    }, {
        path: "/image/list",
        name: "/image/list",
        component: ImageList,
        meta: {
            title: "图库列表"
        }
    }, {
        path: "/notice/list",
        name: "/notice/list",
        component: NoticeList,
        meta: {
            title: "公告列表"
        }
    }, {
        path: '/product/list',
        name: "/product/list",
        meta: { title: '产品列表' },
        component: ProductList
    }];
    
    
    //4 :定义路由配置规则
    const routes = [{
        path: "/",
        meta: { title: "首页" },
        name: "admin",
        component: Index,
        children: asyncRoutes
    }, {
        path: "/login",
        name: "login",
        meta: { title: "登录" },
        component: () =>
            import ('../views/Login.vue')
    }, {
        path: "/toLogin",
        redirect: "/login"
    }, { //----------------新增代码,建议把注释删掉
        path: '/:pathMatch(.*)*',
        name: '404',
        meta: { title: "404" },
        component: () =>
            import ('../views/error/404.vue')
    }]
    
    
    //2 :创建路由对象
    const router = createRouter({
        // 引入访问模式
        history: createWebHistory(),
        routes
    })
    
    
    // 动态注册路由方法
    export function registerRoutes(menuList) {
        // 是否有新的路由
        let hasNewRoutes = false
        const findAndAddRoutesByMenus = (arr) => {
            arr.forEach(e => {
                // 查看每个
                let item = asyncRoutes.find(o => o.path == e.path)
                if (item && !router.hasRoute(item.path)) {
                    router.addRoute("admin", item)
                    hasNewRoutes = true
                }
                if (e.children && e.children.length > 0) {
                    findAndAddRoutesByMenus(e.children)
                }
            })
        }
    
        findAndAddRoutesByMenus(menuList)
    
        return hasNewRoutes
    }
    
    
    let loadNewRoute = false;
    // 定义后置守卫--拦截器思想
    router.beforeEach(async(to, from, next) => {
        // 全屏动画开启
        showFullLoading()
    
        // 判断是否已经登录
        var isLogin = store.getters["user/isLogin"];
    
        // 没有登录,强制跳转回登录页
        if (!isLogin && to.path != "/login") {
            toastError("请先登录", "error")
            next("/toLogin")
        }
    
        // 防止重复登录
        if (isLogin && to.path == "/login") {
            toastError("请勿重复登录", "error")
            next({ path: from.path ? from.path : "/" })
        }
    
    
        next();
    })
    
    
    
    /* 后置守卫 */
    router.afterEach((to, from) => {
        // 结束全屏动画
        hideFullLoading();
        // 标题切换
        document.title = to.meta.title + "-PugAdmin-后台管理系统" || "PugAdmin-后台管理系统";
    })
    
    
    
    
    
    // 3: 导出即可生效
    export default router
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195

    这种绑定,对于后面的打包的时候,js体积会非常的大。不利于网页加载速度。

    ## 动态路由化
    
    • 1

    把asyncRoutes子路由的集合,动态的绑定 在 routes数据模型的 name: “admin”, 形成父子路由。

    动态添加路由

    router.addRoute({ path: '/about', component: About })
    
    • 1

    添加嵌套路由

    router.addRoute('admin', { path: 'settings', component: AdminSettings })
    
    • 1

    原理

    原理其实就是:把数据库查询出来的路由和router.js定义路由,进行匹配和注册。把存在和合法的动态注册到admin父路由上。

    代码如下:

    // 动态注册路由方法- 考虑到
    export function registerRoutes(menuList) {
        // 是否有新的路由
        let hasNewRoutes = false
        const findAndAddRoutesByMenus = (arr) => {
            menuList.forEach(e => {
                // 一定要查询我的router.js中asyncRoutes存在的路由,才去绑定,否则不绑定
                let item = asyncRoutes.find(o => o.path == e.path)
    
                // item存在,并且没有绑定过router.hasRoute,
                if (item && !router.hasRoute(item.path)) {
                    // 如果没有绑定,就开始绑定。
                    router.addRoute("admin", item)
                    hasNewRoutes = true
                }
    
                // 递归,如果当前子菜单还有子元素,
                if (e.children && e.children.length > 0) {
                    findAndAddRoutesByMenus(e.children)
                }
            })
        }
    
        // 开始递归调用动态绑定路由关系
        findAndAddRoutesByMenus(menuList)
    
        // 第一次绑定:就是 true, 
        // 如果已经全部绑定过就是: false
        // 返回这个作用:就是为了让后续的路由访问只绑定一次,没必要每次访问都绑定
        return hasNewRoutes
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    注册位置

    beforeEach 前置守卫

    • 但是我们指定beforeEach前置守卫,每个路由请求都进入,所以必须找到开关,让注册只注册一次。所以就定义了 let loadNewRoute = false; ,然后注册以后,立马修改let loadNewRoute = true。就可以防止重复注册。

    代码如下:

    import { createRouter, createWebHistory } from 'vue-router'
    import store from '@/store'
    import { showFullLoading, hideFullLoading, toastError } from '@/utils'
    
    
    import PugAdmin from '@/views/PugAdmin.vue'
    import Dashboard from '@/views/dashboard/Index.vue'
    
    import ProductList from '@/views/product/List.vue'
    import CategoryList from '@/views/category/List.vue'
    import CouponList from '@/views/coupon/List.vue'
    import UserList from '@/views/user/List.vue'
    import OrderList from '@/views/order/List.vue'
    import ImageList from '@/views/image/List.vue'
    import NoticeList from '@/views/notice/List.vue'
    import LevelList from '@/views/level/List.vue'
    
    import ManagerList from '@/views/manager/List.vue'
    import RoleList from '@/views/role/List.vue'
    import PermissionList from '@/views/permission/List.vue'
    
    
    // 动态路由,用于匹配菜单动态添加路由
    const asyncRoutes = [{
        path: '/',
        name: "dashboard",
        meta: { title: '后台首页' },
        component: Dashboard,
    }, {
        path: '/user/list',
        name: "/user/list",
        meta: { title: '用户管理' },
        component: UserList
    }, {
        path: '/manager/list',
        name: "/manager/list",
        meta: { title: '后台管理员' },
        component: ManagerList
    }, {
        path: '/role/list',
        name: "/role/list",
        meta: { title: '角色管理' },
        component: RoleList
    }, {
        path: '/permission/list',
        name: "/permission/list",
        meta: { title: '权限管理' },
        component: PermissionList
    }, {
        path: '/user/list',
        name: "/user/list",
        meta: { title: '用户管理' },
        component: UserList
    }, {
        path: '/level/list',
        name: "/level/list",
        meta: { title: '会员等级' },
        component: LevelList
    }, {
        path: "/category/list",
        name: "/category/list",
        component: CategoryList,
        meta: { title: "分类列表" }
    }, {
        path: "/coupon/list",
        name: "/coupon/list",
        component: CouponList,
        meta: { title: "优惠券管理" }
    }, {
        path: "/order/list",
        name: "/order/list",
        component: OrderList,
        meta: {
            title: "订单列表"
        }
    }, {
        path: "/image/list",
        name: "/image/list",
        component: ImageList,
        meta: {
            title: "图库列表"
        }
    }, {
        path: "/notice/list",
        name: "/notice/list",
        component: NoticeList,
        meta: {
            title: "公告列表"
        }
    }, {
        path: '/product/list',
        name: "/product/list",
        meta: { title: '产品列表' },
        component: ProductList
    }];
    
    
    //4 :定义路由配置规则
    const routes = [{
        path: "/",
        meta: { title: "首页" },
        name: "PugAdmin",
        component: PugAdmin
    }, {
        path: "/login",
        name: "login",
        meta: { title: "登录" },
        component: () =>
            import ('../views/Login.vue')
    }, {
        path: "/toLogin",
        redirect: "/login"
    }, { //----------------新增代码,建议把注释删掉
        path: '/:pathMatch(.*)*',
        name: '404',
        meta: { title: "404" },
        component: () =>
            import ('../views/error/404.vue')
    }]
    
    
    //2 :创建路由对象
    const router = createRouter({
        // 引入访问模式
        history: createWebHistory(),
        routes
    })
    
    
    
    // 动态注册路由方法- 考虑到
    export function registerRoutes(menuList) {
        // 是否有新的路由
        let hasNewRoutes = false
        const findAndAddRoutesByMenus = (arr) => {
            arr.forEach(e => {
                // 一定要查询我router.js中asyncRoutes存在的路由,才去绑定,否则不绑定
                let item = asyncRoutes.find(o => o.path == e.path)
                    // item存在,并且没有绑定过router.hasRoute,
                if (item && !router.hasRoute(item.path)) {
                    // 如果没有绑定,就开始绑定。这里就是admin,代表不论多个子元素,最中绑定全部挂载admin这集
                    // 也就意味着:后续所有的子元素,孙子元素等的路由访问,都跳转到PuaAdmin.vue的router-view的位置
                    router.addRoute("PugAdmin", item)
                    hasNewRoutes = true
                }
    
                // 递归,如果当前子菜单还有子元素,
                if (e.children && e.children.length > 0) {
                    findAndAddRoutesByMenus(e.children)
                }
            })
        }
    
        // 开始递归调用动态绑定路由关系
        findAndAddRoutesByMenus(menuList)
    
        // 第一次绑定:就是 true, 
        // 如果已经全部绑定过就是: false
        // 返回这个作用:就是为了让后续的路由访问只绑定一次,没必要每次访问都绑定
        return hasNewRoutes
    }
    
    
    /* 
       这个开关,是用来控制动态路由注册只绑定一次 
       只要不刷新、F5,第一次就是false, 后面永远都是 true
    */
    let loadNewRoute = false;
    // 定义后置守卫--拦截器思想
    router.beforeEach(async(to, from, next) => {
        // 全屏动画开启
        showFullLoading()
    
        // 判断是否已经登录
        var isLogin = store.getters["user/isLogin"];
    
        // 没有登录,强制跳转回登录页
        if (!isLogin && to.path != "/login") {
            toastError("请先登录", "error")
            next("/toLogin")
        }
    
        // 防止重复登录
        if (isLogin && to.path == "/login") {
            toastError("请勿重复登录", "error")
            next({ path: from.path ? from.path : "/" })
        }
    
    
    
        let hasNewRoute = false; //F5的问题
    
        if (isLogin && !loadNewRoute) {
            // 锁住
            loadNewRoute = true;
    
            // 从数据查询菜单信息,开始进行动态注册
            let menusList = await store.dispatch("menu/asyncGetMenuList")
    
            // 动态注册路由
            hasNewRoute = registerRoutes(menusList);
        }
    
        // 这里为啥要判断,是因为当前路由刷新的时候,要给一个具体的执行。直接执行next肯定不行。因为不知道下个页面是多少
        next();
    })
    
    
    
    /* 后置守卫 */
    router.afterEach((to, from) => {
        // 结束全屏动画
        hideFullLoading();
        // 标题切换
        document.title = to.meta.title + "-PugAdmin-后台管理系统" || "PugAdmin-后台管理系统";
    })
    
    
    
    
    
    // 3: 导出即可生效
    export default router
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223

    动态注册路由的刷新问题

    如果直接刷新路由,造成404页面。原因是?因为刷新的路由没有from只有to. 所以出现了404。next就不知道访问到哪里去。

    ··
    在这里插入图片描述

  • 相关阅读:
    python爬虫实战之异步爬取数据
    RLChina2022-强化学习暑期课程-博弈搜索算法
    Matlab:设置命令历史记录预设项
    【MyBatis】Mybatis的输入和输出映射
    用 NativeScript 开发 iOS 应用,如何调试?
    一个老测试/开发程序员的独白......
    21JVM内存模型(JMM)
    pdf文件属性的删除
    star 最多的 Go 语言本地化库|GitHub 2.8K
    Java常见类型数据及其包装类—byte类型,String类型,boolean类型
  • 原文地址:https://blog.csdn.net/Jiaodaqiaobiluo/article/details/126320974