Я уже писал оvue权限路由实现方式总结В статье, после периода наступания на яму и подведения итогов, поговорим о решении, которое я считаю более «идеальным» на данный момент:Меню и маршрутизация полностью предоставляются серверной частью.
Меню и маршруты полностью возвращаются бэкендом
О таком плане уже упоминалось ранее, и сейчас я расскажу о нем более конкретно.
Многие люди любят перерабатывать маршруты в меню или перерабатывать меню в маршруты (я делал это раньше), и в конце концов обнаружили, что ямы копаются все глубже и глубже.
Меню приложения может быть двухуровневым, трехуровневым или даже четырех-пятиуровневым, а маршрутизация обычно не превышает трех уровней. Если меню приложения достигает пятого уровня, и его можно решить с помощью двухуровневой маршрутизации, чтобы сгенерировать соответствующее меню в соответствии с маршрутизацией, некоторые люди придумают пятиуровневую маршрутизацию. . .
Поэтому предлагается, чтобы данные меню и данные маршрутизации были разделены независимо, если вы можете перейти к соответствующему маршруту в соответствии с меню.
И меню, и маршруты предоставляются серверной частью, и для меню и маршрутов требуются соответствующие функции обслуживания. Также требуются некоторые свойства в меню, такие как заголовок и путь перехода (вы также можете использовать имя перехода, соответствующее имени маршрута, потому что маршрутизация vue может переходить в соответствии с именем). Данные маршрутизации могут поддерживать поля, необходимые для маршрутизации vue.
Конечно, для управления разрешениями необходимо поддерживать соответствующие коды разрешений как в меню, так и в маршрутах, а серверная часть отфильтровывает меню и маршруты, к которым пользователи могут получить доступ, в соответствии с разрешениями пользователя.
Ниже приведен пример меню и маршрутизации, возвращаемых серверной частью.
let permissionMenu = [
{
title: "系统",
path: "/system",
icon: "folder-o",
children: [
{
title: "系统设置",
icon: "folder-o",
children: [
{
title: "菜单管理",
path: "/system/menu",
icon: "folder-o"
},
{
title: "路由管理",
path: "/system/route",
icon: "folder-o"
}
]
},
{
title: "权限管理",
icon: "folder-o",
children: [
{
title: "功能管理",
path: "/system/function",
icon: "folder-o"
},
{
title: "角色管理",
path: "/system/role",
icon: "folder-o"
},
{
title: "角色权限管理",
path: "/system/rolepermission",
icon: "folder-o"
},
{
title: "角色用户管理",
path: "/system/roleuser",
icon: "folder-o"
},
{
title: "用户角色管理",
path: "/system/userrole",
icon: "folder-o"
}
]
},
{
title: "组织架构",
icon: "folder-o",
children: [
{
title: "部门管理",
path: "",
icon: "folder-o"
},
{
title: "职位管理",
path: "",
icon: "folder-o"
}
]
},
{
title: "用户管理",
icon: "folder-o",
children: [
{
title: "用户管理",
path: "/system/user",
icon: "folder-o"
}
]
}
]
}
]
let permissionRouter = [
{
name: "系统设置",
path: "/system",
component: "layoutHeaderAside",
componentPath:'layout/header-aside/layout',
meta: {
title: '系统设置'
},
children: [
{
name: "菜单管理",
path: "/system/menu",
meta: {
title: '菜单管理'
},
component: "menu",
componentPath:'pages/sys/menu/index',
},
{
name: "路由管理",
path: "/system/route",
meta: {
title: '路由管理'
},
component: "route",
componentPath:'pages/sys/menu/index',
}
]
},
{
name: "权限管理",
path: "/system",
component: "layoutHeaderAside",
componentPath:'layout/header-aside/layout',
meta: {
title: '权限管理'
},
children: [
{
name: "功能管理",
path: "/system/function",
meta: {
title: '功能管理'
},
component: "function",
componentPath:'pages/sys/menu/index',
},
{
name: "角色管理",
path: "/system/role",
meta: {
title: '角色管理'
},
component: "role",
componentPath:'pages/sys/menu/index',
},
{
name: "角色权限管理",
path: "/system/rolepermission",
meta: {
title: '角色权限管理'
},
component: "rolePermission",
componentPath:'pages/sys/menu/index',
},
{
name: "角色用户权限管理",
path: "/system/roleuser",
meta: {
title: '角色用户管理'
},
component: "roleUser",
componentPath:'pages/sys/menu/index',
},
{
name: "用户角色权限管理",
path: "/system/userrole",
meta: {
title: '用户角色管理'
},
component: "userRole",
componentPath:'pages/sys/menu/index',
}
]
},
{
name: "用户管理",
path: "/system",
component: "layoutHeaderAside",
componentPath:'layout/header-aside/layout',
meta: {
title: '用户管理'
},
children: [
{
name: "用户管理",
path: "/system/user",
meta: {
title: '用户管理'
},
component: "user",
componentPath:'pages/sys/menu/index',
}
]
}
]
Видно, что меню может доходить до трех уровней, а маршрутизация имеет только два уровня.pathс маршрутизациейpathСоответственно при клике по меню оно может корректно прыгать.
Есть небольшая хитрость: в роутинге
metaподдерживатьtitleАтрибут, при переключении страницы, если вам нужно динамически изменить заголовок вкладки браузера, вы можете получить его прямо из текущего маршрута, не заходя в меню.
Данные меню можно использовать в качестве источника данных для левого меню или источника данных для верхнего меню. В некоторых системах много контента.Сверху может быть системный модуль, а слева меню под модулем.При переключении между разными модулями вверху левое меню должно динамически переключаться. При выполнении подобных функций, поскольку данные меню отделены от маршрутизации, вам нужно сосредоточиться только на меню, например, на добавлении атрибутов модуля в меню.
Текущие данные маршрутизации полностью соответствуют правилам объявления маршрутизации vue, но метод добавления маршрутов используется напрямуюaddRoutesДинамическое добавление маршрутов недопустимо. Поскольку атрибут компонента маршрута vue должен быть компонентом, например
{
name: "login",
path: "/login",
component: () => import("@/pages/Login.vue")
}
В настоящее время атрибут компонента в данных маршрутизации, которые мы получаем, представляет собой строку. Свойство компонента необходимо преобразовать в реальный компонент в соответствии с этой строкой. В маршрутных данных помимо атрибута component, не соответствующего требованиям vue routing, есть дополнительный атрибут componentPath. Далее описываются два метода обработки маршрутизации на основе этих двух атрибутов соответственно.
обрабатывать маршрутизацию
Использование компонентов routerMap
Это имя взято мной, по сути, оно предназначено для поддержки js-файла и экспорта компонентов в соответствии с правилами ключ-значение, например:
import layoutHeaderAside from '@/layout/header-aside'
export default {
"layoutHeaderAside": layoutHeaderAside,
"menu": () => import(/* webpackChunkName: "menu" */'@/pages/sys/menu'),
"route": () => import(/* webpackChunkName: "route" */'@/pages/sys/route'),
"function": () => import(/* webpackChunkName: "function" */'@/pages/permission/function'),
"role": () => import(/* webpackChunkName: "role" */'@/pages/permission/role'),
"rolePermission": () => import(/* webpackChunkName: "rolepermission" */'@/pages/permission/rolePermission'),
"roleUser": () => import(/* webpackChunkName: "roleuser" */'@/pages/permission/roleUser'),
"userRole": () => import(/* webpackChunkName: "userrole" */'@/pages/permission/userRole'),
"user": () => import(/* webpackChunkName: "user" */'@/pages/permission/user')
}
Ключ здесь соответствует атрибуту компонента данных маршрутизации, возвращаемых серверной частью. Итак, после получения данных маршрутизации, возвращаемых серверной частью, используйте это правило для обработки данных маршрутизации:
const formatRoutes = function (routes) {
routes.forEach(route => {
route.component = routerMapComponents[route.component]
if (route.children) {
formatRoutes(route.children)
}
})
}
formatRoutes(permissionRouter)
router.addRoutes(permissionRouter);
Более того, компоненты, поддерживаемые в списке правил, будут упакованы вебпаком в отдельные файлы js, даже если они не используются при обработке данных маршрутизации (не используютсяrouterMapComponents[route.component]соответствовать). Когда нам нужно сделать несколько макетов для страницы, нам нужно только изменить компонент на соответствующий ключ в routerMapComponents в интерфейсе обслуживания меню.
Стандартные асинхронные компоненты
Согласно официальной документации vueАсинхронные компонентыМетод записи, получить два метода обработки маршрутизации и использовать componentPath в данных маршрутизации:
Первый способ записи:
const formatRoutesByComponentPath = function (routes) {
routes.forEach(route => {
route.component = function (resolve) {
require([`../${route.componentPath}.vue`], resolve)
}
if (route.children) {
formatRoutesByComponentPath(route.children)
}
})
}
formatRoutesByComponentPath(permissionRouter);
router.addRoutes(permissionRouter);
Второй способ написания:
const formatRoutesByComponentPath = function (routes) {
routes.forEach(route => {
route.component = () => import(`../${route.componentPath}.vue`)
if (route.children) {
formatRoutesByComponentPath(route.children)
}
})
}
formatRoutesByComponentPath(permissionRouter);
router.addRoutes(permissionRouter);
На самом деле, по мнению большинства людей (включая меня), веб-пакет не должен уметь обрабатывать такой код, ведь componentPath определяется во время выполнения, а веб-пакет обрабатывается статически при «компиляции».
Чтобы проверить, может ли такой код работать нормально, я написал простуюdemo, вы можете загрузить и запустить его локально, если вам это интересно.
Результатом теста является то, что две вышеупомянутые программы записи могут работать нормально.
Просмотрите упакованный код и убедитесь, что все компоненты упакованы, независимо от того, используются они или нет (в предыдущем методе routerMapComponents будут упакованы только компоненты, сохраненные в списке).
Все компоненты упакованы, но упакованный код двух методов сильно отличается.
использовать
route.component = function (resolve) {
require([`../${route.componentPath}.vue`], resolve)
}
Технологическая маршрутизация после упаковки
Файлы, начинающиеся с 0,page404.vueУпакованный код, начинающийся с 1,home.vueиз. Эти два компонента могут быть упакованы отдельно, потому чтоmain.jsЭти два компонента явно используются в:
...
let routers = [
{
name: "home",
path: "/",
component: () => import(/* webpackChunkName: "home" */"@/pages/home.vue")
},
{
name: "404",
path: "*",
component: () => import(/* webpackChunkName: "page404" */"@/pages/page404.vue")
}
];
let router = new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
routes: routers
});
...
Файлы, начинающиеся с 4, упакованы со всеми остальными компонентами, и в них есть кое-что лишнее:
webpackJsonp([4, 0], {
"/EbY": function(e, t, n) {
var r = {
"./App.vue": "M93x",
"./pages/dynamic.vue": "fJxZ",
"./pages/home.vue": "vkyI",
"./pages/nouse.vue": "HYpT",
"./pages/page404.vue": "GVrJ"
};
function i(e) {
return n(a(e))
}
function a(e) {
var t = r[e];
if (! (t + 1)) throw new Error("Cannot find module '" + e + "'.");
return t
}
i.keys = function() {
return Object.keys(r)
},
i.resolve = a,
e.exports = i,
i.id = "/EbY"
},
GVrJ: function(e, t, n) {
"use strict";
Object.defineProperty(t, "__esModule", {
value: !0
});
var r = {
render: function() {
var e = this.$createElement,
t = this._self._c || e;
return t("div", [this._v("\n 404\n "), t("div", [t("router-link", {
attrs: {
to: "/"
}
},
[this._v("返回首页")])], 1)])
},
staticRenderFns: []
};
var i = n("VU/8")({
name: "page404"
},
r, !1,
function(e) {
n("tqPO")
},
"data-v-5b14313a", null);
t.
default = i.exports
},
HYpT: function(e, t, n) {
"use strict";
Object.defineProperty(t, "__esModule", {
value: !0
});
var r = {
render: function() {
var e = this.$createElement;
return (this._self._c || e)("div", [this._v("\n 从未使用的组件\n")])
},
staticRenderFns: []
};
var i = n("VU/8")({
name: "nouse"
},
r, !1,
function(e) {
n("v4yi")
},
"data-v-d4fde316", null);
t.
default = i.exports
},
WMa5: function(e, t) {},
fJxZ: function(e, t, n) {
"use strict";
Object.defineProperty(t, "__esModule", {
value: !0
});
var r = {
render: function() {
var e = this.$createElement,
t = this._self._c || e;
return t("div", [t("div", [this._v("动态路由页")]), this._v(" "), t("router-link", {
attrs: {
to: "/"
}
},
[this._v("首页")])], 1)
},
staticRenderFns: []
};
var i = n("VU/8")({
name: "dynamic"
},
r, !1,
function(e) {
n("WMa5")
},
"data-v-71726d06", null);
t.
default = i.exports
},
tqPO: function(e, t) {},
v4yi: function(e, t) {}
});
dynamic.vue,nouse.vueОни упакованы иpage404.vueБыл снова упакован (???).
И немного:
var r = {
"./App.vue": "M93x",
"./pages/dynamic.vue": "fJxZ",
"./pages/home.vue": "vkyI",
"./pages/nouse.vue": "HYpT",
"./pages/page404.vue": "GVrJ"
};
Это должно использоваться во время выполненияcomponentPathОбработка маршрутизации, ключевой момент для нормальной работы программы.
чтобы уточнить
page404.vueПочему он снова был упакован, я добавилsimple.vue, И вmain.jsОн также явно импортируется и обнаруживается после упаковки.simple.vueТакже упакованы отдельно, толькоpage404.vueУпакован был дважды. Пока нет решения. . .
использовать
route.component = () => import(`../${route.componentPath}.vue`)
Технологическая маршрутизация после упаковки
Файлы, начинающиеся с 0,page404.vueУпакованный код, начинающийся с 1,home.vueДа, 4 начинается сnouse.vueДа, 5 начинается сdynamic.vueиз.
Все компоненты упакованы индивидуально, иhome.vueВ упакованном коде есть что написать:
webpackJsonp([1], {
"rF/f": function(e, t) {},
sTBc: function(e, t, n) {
var r = {
"./App.vue": ["M93x"],
"./pages/dynamic.vue": ["fJxZ", 5],
"./pages/home.vue": ["vkyI"],
"./pages/nouse.vue": ["HYpT", 4],
"./pages/page404.vue": ["GVrJ", 0]
};
function i(e) {
var t = r[e];
return t ? Promise.all(t.slice(1).map(n.e)).then(function() {
return n(t[0])
}) : Promise.reject(new Error("Cannot find module '" + e + "'."))
}
i.keys = function() {
return Object.keys(r)
},
i.id = "sTBc",
e.exports = i
},
vkyI: function(e, t, n) {
"use strict";
Object.defineProperty(t, "__esModule", {
value: !0
});
var r = {
name: "home",
methods: {
addRoutes: function() {
this.$router.addRoutes([{
name: "dynamic",
path: "/dynamic",
component: function() {
return n("sTBc")("./" +
function() {
return "pages/dynamic"
} + ".vue")
}
}]),
alert("路由添加成功!")
}
}
},
i = {
render: function() {
var e = this.$createElement,
t = this._self._c || e;
return t("div", [t("div", [this._v("这是首页")]), this._v(" "), t("a", {
attrs: {
href: "javascript:void(0)"
},
on: {
click: this.addRoutes
}
},
[this._v("动态添加路由")]), this._v(" \n "), t("router-link", {
attrs: {
to: "/dynamic"
}
},
[this._v("前往动态路由")])], 1)
},
staticRenderFns: []
};
var s = n("VU/8")(r, i, !1,
function(e) {
n("rF/f")
},
"data-v-25e45483", null);
t.
default = s.exports
}
});
можно увидеть
var r = {
"./App.vue": ["M93x"],
"./pages/dynamic.vue": ["fJxZ", 5],
"./pages/home.vue": ["vkyI"],
"./pages/nouse.vue": ["HYpT", 4],
"./pages/page404.vue": ["GVrJ", 0]
};
забежал внутрь, наверное, потому что был вhome.vueиспользуется вroute.component = () => import(../${route.componentPath}.vue)
Для проектов, созданных более низкой версией vue-cli, запакованный код такой же, как и в предыдущем способе, не все компоненты запакованы отдельно, не знаю, то ли это вебпак (такое бывает с webpack2), то ли проблема с vue- погрузчик.
резюме
- использовать
routerMapComponentsМаршруты обрабатываются способом маршрутизации. Данные о маршруте, возвращаемые серверной частью, должны идентифицировать поле компонента. Использование этого поля может соответствовать списку компонентов маршрута, поддерживаемому внешним интерфейсом (routerMapComponents.js) компоненты. При использовании этого метода поддерживается только список компонентов маршрута (routerMapComponents.js) будет упакован. - использовать
route.component = function (resolve) {
require([`../${route.componentPath}.vue`], resolve)
}
Данные маршрутизации, возвращаемые серверной частью, должны идентифицировать конкретное местоположение компонента в каталоге внешнего интерфейса (тот, который использовался выше).componentPathполе). Используя этот метод, он уже отображается во время компиляции.importкомпоненты будут упакованы отдельно, а все остальные компоненты будут упакованы вместе (независимо от того, используются ли соответствующие компоненты во время выполнения),404Компонент, соответствующий маршруту, будет упакован дважды.
- использовать
route.component = () => import(`../${route.componentPath}.vue`)
Данные маршрутизации, возвращаемые серверной частью, также должны указывать конкретное расположение компонента в каталоге внешнего интерфейса. Таким образом, все компоненты будут упакованы индивидуально, независимо от того, использовались они или нет.
Поэтому рекомендуется использовать первый и третий методы для обработки маршрутов, возвращаемых бэкендом.
В первом случае интерфейс должен поддерживать список компонентов маршрута (routerMapComponents.js), когда соответствующий персонал поддерживает маршрут, интерфейсная разработка должна предоставить соответствующий ключ.Конечно, человек, который поддерживает маршрут, также может определить ключ и передать его интерфейсной разработке.
В третьем способе фронтенду не нужно ничего поддерживать, достаточно указать человеку, который поддерживает маршрут, путь соответствующего компонента во фронтенд-проекте, что может привести к утечке структуры фронтенд-проекта, т.к. упакованный код всегда можно увидеть прибывшим.
Суммировать
Меню и маршрутизация полностью предоставляются серверной частью. Меню и данные маршрутизации разделены. Меню и маршрутизация отмечены метками разрешений. Серверная часть отфильтровывает меню и маршруты, к которым пользователь может получить доступ в соответствии с Обработка для корректного сопоставления маршрута с соответствующим компонентом. Это должна быть относительно «идеальная» реализация маршрутизации разрешений vue.
Некоторые люди могут сказать, что, поскольку передняя и задняя части уже разделены, почему они так зависят от задней части?
Меню и маршруты не предоставляются серверной частью. Когда разрешения отфильтрованы, список разрешений, возвращаемый серверной частью, не требуется, а идентификаторы разрешений также жестко закодированы в меню и маршрутах.
Меню и маршрутизация полностью обеспечиваются бэкендом, что не означает, что фронтенд-разработка нуждается в большем общении (спорах) с бэкенд-разработкой. Меню и маршруты могут выполнять соответствующие функции обслуживания, такие как поддержка пакетного экспорта и импорта.При добавлении новых меню или маршрутов вы можете работать с функцией страницы. Единственная стоимость связи заключается в том, что при обслуживании маршрута вам необходимо знать ключ списка компонентов внешнего обслуживания или путь, соответствующий компоненту, но маршрут также может поддерживаться интерфейсной разработкой, и разрешение идентификатор может поддерживаться после подтверждения внешнего интерфейса и внутреннего интерфейса (конечно, элементы на странице Идентификация разрешения уровня контроля разрешений должны быть подтверждены заранее). И если меню и маршрутизация жестко запрограммированы на внешнем интерфейсе, внешний и внутренний интерфейсы должны подтвердить соответствующий идентификатор разрешения в начале.