feat: 新增 useRouteListener,系统统一采用该 hook 监听路由变化
This commit is contained in:
parent
a267429e5b
commit
a3dce0e0f2
48
src/hooks/useRouteListener.ts
Normal file
48
src/hooks/useRouteListener.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { onBeforeUnmount } from "vue"
|
||||||
|
import mitt, { type Handler } from "mitt"
|
||||||
|
import { type RouteLocationNormalized } from "vue-router"
|
||||||
|
|
||||||
|
/** 回调函数的类型 */
|
||||||
|
type Callback = (route: RouteLocationNormalized) => void
|
||||||
|
|
||||||
|
const emitter = mitt()
|
||||||
|
const key = Symbol("ROUTE_CHANGE")
|
||||||
|
let latestRoute: RouteLocationNormalized
|
||||||
|
|
||||||
|
/** 设置最新的路由信息,触发路由变化事件 */
|
||||||
|
const setRouteChange = (to: RouteLocationNormalized) => {
|
||||||
|
// 触发事件
|
||||||
|
emitter.emit(key, to)
|
||||||
|
// 缓存最新的路由信息
|
||||||
|
latestRoute = to
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 单独监听路由会浪费渲染性能,使用发布订阅模式去进行分发管理 */
|
||||||
|
export function useRouteListener() {
|
||||||
|
/** 回调函数集合 */
|
||||||
|
const callbackList: Callback[] = []
|
||||||
|
|
||||||
|
/** 监听路由变化(可以选择立即执行) */
|
||||||
|
const listenerRouteChange = (callback: Callback, immediate = false) => {
|
||||||
|
// 缓存回调函数
|
||||||
|
callbackList.push(callback)
|
||||||
|
// 监听事件
|
||||||
|
emitter.on(key, callback as Handler)
|
||||||
|
// 可以选择立即执行一次回调函数
|
||||||
|
immediate && latestRoute && callback(latestRoute)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 移除路由变化事件监听器 */
|
||||||
|
const removeRouteListener = (callback: Callback) => {
|
||||||
|
emitter.off(key, callback as Handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 组件销毁前移除监听器 */
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
for (let i = 0; i < callbackList.length; i++) {
|
||||||
|
removeRouteListener(callbackList[i])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return { setRouteChange, listenerRouteChange, removeRouteListener }
|
||||||
|
}
|
@ -1,10 +1,12 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, watch } from "vue"
|
import { ref } from "vue"
|
||||||
import { type RouteLocationMatched, useRoute, useRouter } from "vue-router"
|
import { type RouteLocationMatched, useRoute, useRouter } from "vue-router"
|
||||||
|
import { useRouteListener } from "@/hooks/useRouteListener"
|
||||||
import { compile } from "path-to-regexp"
|
import { compile } from "path-to-regexp"
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const { listenerRouteChange } = useRouteListener()
|
||||||
|
|
||||||
/** 定义响应式数据 breadcrumbs,用于存储面包屑导航信息 */
|
/** 定义响应式数据 breadcrumbs,用于存储面包屑导航信息 */
|
||||||
const breadcrumbs = ref<RouteLocationMatched[]>([])
|
const breadcrumbs = ref<RouteLocationMatched[]>([])
|
||||||
@ -31,13 +33,10 @@ const handleLink = (item: RouteLocationMatched) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 监听路由变化,更新面包屑导航信息 */
|
/** 监听路由变化,更新面包屑导航信息 */
|
||||||
watch(
|
listenerRouteChange((route) => {
|
||||||
() => route.path,
|
if (route.path.startsWith("/redirect/")) return
|
||||||
(path) => {
|
getBreadcrumb()
|
||||||
if (path.startsWith("/redirect/")) return
|
})
|
||||||
getBreadcrumb()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/** 初始化面包屑导航信息 */
|
/** 初始化面包屑导航信息 */
|
||||||
getBreadcrumb()
|
getBreadcrumb()
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, watch, nextTick } from "vue"
|
import { ref, nextTick } from "vue"
|
||||||
import { RouterLink, useRoute } from "vue-router"
|
import { RouterLink, useRoute } from "vue-router"
|
||||||
|
import { useSettingsStore } from "@/store/modules/settings"
|
||||||
|
import { useRouteListener } from "@/hooks/useRouteListener"
|
||||||
|
import Screenfull from "@/components/Screenfull/index.vue"
|
||||||
import { ElScrollbar } from "element-plus"
|
import { ElScrollbar } from "element-plus"
|
||||||
import { ArrowLeft, ArrowRight } from "@element-plus/icons-vue"
|
import { ArrowLeft, ArrowRight } from "@element-plus/icons-vue"
|
||||||
import { useSettingsStore } from "@/store/modules/settings"
|
|
||||||
import Screenfull from "@/components/Screenfull/index.vue"
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
tagRefs: InstanceType<typeof RouterLink>[]
|
tagRefs: InstanceType<typeof RouterLink>[]
|
||||||
@ -14,6 +15,7 @@ const props = defineProps<Props>()
|
|||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const settingsStore = useSettingsStore()
|
const settingsStore = useSettingsStore()
|
||||||
|
const { listenerRouteChange } = useRouteListener()
|
||||||
|
|
||||||
/** 滚动条组件元素的引用 */
|
/** 滚动条组件元素的引用 */
|
||||||
const scrollbarRef = ref<InstanceType<typeof ElScrollbar>>()
|
const scrollbarRef = ref<InstanceType<typeof ElScrollbar>>()
|
||||||
@ -94,15 +96,9 @@ const moveTo = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 监听路由变化,移动到目标位置 */
|
/** 监听路由变化,移动到目标位置 */
|
||||||
watch(
|
listenerRouteChange(() => {
|
||||||
route,
|
nextTick(moveTo)
|
||||||
() => {
|
})
|
||||||
nextTick(moveTo)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
deep: true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { getCurrentInstance, onBeforeUnmount, onMounted, ref, watch } from "vue"
|
import { getCurrentInstance, onMounted, ref, watch } from "vue"
|
||||||
import { type RouteLocationNormalizedLoaded, type RouteRecordRaw, RouterLink, useRoute, useRouter } from "vue-router"
|
import { type RouteLocationNormalizedLoaded, type RouteRecordRaw, RouterLink, useRoute, useRouter } from "vue-router"
|
||||||
import { type TagView, useTagsViewStore } from "@/store/modules/tags-view"
|
import { type TagView, useTagsViewStore } from "@/store/modules/tags-view"
|
||||||
import { usePermissionStore } from "@/store/modules/permission"
|
import { usePermissionStore } from "@/store/modules/permission"
|
||||||
import { listenerRouteChange, removeRouteListener } from "@/utils/route-listener"
|
import { useRouteListener } from "@/hooks/useRouteListener"
|
||||||
import path from "path-browserify"
|
import path from "path-browserify"
|
||||||
import ScrollPane from "./ScrollPane.vue"
|
import ScrollPane from "./ScrollPane.vue"
|
||||||
import { Close } from "@element-plus/icons-vue"
|
import { Close } from "@element-plus/icons-vue"
|
||||||
@ -13,6 +13,7 @@ const router = useRouter()
|
|||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const tagsViewStore = useTagsViewStore()
|
const tagsViewStore = useTagsViewStore()
|
||||||
const permissionStore = usePermissionStore()
|
const permissionStore = usePermissionStore()
|
||||||
|
const { listenerRouteChange } = useRouteListener()
|
||||||
|
|
||||||
/** 标签页组件元素的引用数组 */
|
/** 标签页组件元素的引用数组 */
|
||||||
const tagRefs = ref<InstanceType<typeof RouterLink>[]>([])
|
const tagRefs = ref<InstanceType<typeof RouterLink>[]>([])
|
||||||
@ -148,6 +149,11 @@ const closeMenu = () => {
|
|||||||
visible.value = false
|
visible.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 监听路由变化 */
|
||||||
|
listenerRouteChange((route) => {
|
||||||
|
addTags(route)
|
||||||
|
})
|
||||||
|
|
||||||
watch(visible, (value) => {
|
watch(visible, (value) => {
|
||||||
value ? document.body.addEventListener("click", closeMenu) : document.body.removeEventListener("click", closeMenu)
|
value ? document.body.addEventListener("click", closeMenu) : document.body.removeEventListener("click", closeMenu)
|
||||||
})
|
})
|
||||||
@ -156,18 +162,6 @@ onMounted(() => {
|
|||||||
initTags()
|
initTags()
|
||||||
addTags(route)
|
addTags(route)
|
||||||
})
|
})
|
||||||
|
|
||||||
//#region 监听路由
|
|
||||||
const callback = (route: RouteLocationNormalizedLoaded) => {
|
|
||||||
addTags(route)
|
|
||||||
}
|
|
||||||
|
|
||||||
listenerRouteChange(callback)
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
removeRouteListener(callback)
|
|
||||||
})
|
|
||||||
//#endregion
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { watch, onBeforeMount, onMounted, onBeforeUnmount } from "vue"
|
import { onBeforeMount, onMounted, onBeforeUnmount } from "vue"
|
||||||
import { useRoute } from "vue-router"
|
|
||||||
import { useAppStore } from "@/store/modules/app"
|
import { useAppStore } from "@/store/modules/app"
|
||||||
|
import { useRouteListener } from "@/hooks/useRouteListener"
|
||||||
import { DeviceEnum } from "@/constants/app-key"
|
import { DeviceEnum } from "@/constants/app-key"
|
||||||
|
|
||||||
/** 参考 Bootstrap 的响应式设计将最大移动端宽度设置为 992 */
|
/** 参考 Bootstrap 的响应式设计将最大移动端宽度设置为 992 */
|
||||||
@ -8,8 +8,8 @@ const MAX_MOBILE_WIDTH = 992
|
|||||||
|
|
||||||
/** 根据浏览器宽度变化,变换 Layout 布局 */
|
/** 根据浏览器宽度变化,变换 Layout 布局 */
|
||||||
export default () => {
|
export default () => {
|
||||||
const route = useRoute()
|
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
|
const { listenerRouteChange } = useRouteListener()
|
||||||
|
|
||||||
/** 用于判断当前设备是否为移动端 */
|
/** 用于判断当前设备是否为移动端 */
|
||||||
const _isMobile = () => {
|
const _isMobile = () => {
|
||||||
@ -25,15 +25,12 @@ export default () => {
|
|||||||
isMobile && appStore.closeSidebar(true)
|
isMobile && appStore.closeSidebar(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/** 监听路由名称变化,根据设备类型调整布局 */
|
/** 监听路由变化,根据设备类型调整布局 */
|
||||||
watch(
|
listenerRouteChange(() => {
|
||||||
() => route.name,
|
if (appStore.device === DeviceEnum.Mobile && appStore.sidebar.opened) {
|
||||||
() => {
|
appStore.closeSidebar(false)
|
||||||
if (appStore.device === DeviceEnum.Mobile && appStore.sidebar.opened) {
|
|
||||||
appStore.closeSidebar(false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
|
|
||||||
/** 在组件挂载前添加窗口大小变化事件监听器 */
|
/** 在组件挂载前添加窗口大小变化事件监听器 */
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
|
@ -2,19 +2,19 @@ import router from "@/router"
|
|||||||
import { useUserStoreHook } from "@/store/modules/user"
|
import { useUserStoreHook } from "@/store/modules/user"
|
||||||
import { usePermissionStoreHook } from "@/store/modules/permission"
|
import { usePermissionStoreHook } from "@/store/modules/permission"
|
||||||
import { ElMessage } from "element-plus"
|
import { ElMessage } from "element-plus"
|
||||||
|
import { useRouteListener } from "@/hooks/useRouteListener"
|
||||||
import { getToken } from "@/utils/cache/cookies"
|
import { getToken } from "@/utils/cache/cookies"
|
||||||
import { fixBlankPage } from "@/utils/fix-blank-page"
|
import { fixBlankPage } from "@/utils/fix-blank-page"
|
||||||
import { setRouteEmitter } from "@/utils/route-listener"
|
|
||||||
import routeSettings from "@/config/route"
|
import routeSettings from "@/config/route"
|
||||||
import isWhiteList from "@/config/white-list"
|
import isWhiteList from "@/config/white-list"
|
||||||
import NProgress from "nprogress"
|
import NProgress from "nprogress"
|
||||||
import "nprogress/nprogress.css"
|
import "nprogress/nprogress.css"
|
||||||
|
|
||||||
NProgress.configure({ showSpinner: false })
|
NProgress.configure({ showSpinner: false })
|
||||||
|
const { setRouteChange } = useRouteListener()
|
||||||
|
|
||||||
router.beforeEach(async (to, _from, next) => {
|
router.beforeEach(async (to, _from, next) => {
|
||||||
fixBlankPage()
|
fixBlankPage()
|
||||||
setRouteEmitter(to)
|
|
||||||
NProgress.start()
|
NProgress.start()
|
||||||
const userStore = useUserStoreHook()
|
const userStore = useUserStoreHook()
|
||||||
const permissionStore = usePermissionStoreHook()
|
const permissionStore = usePermissionStoreHook()
|
||||||
@ -70,6 +70,10 @@ router.beforeEach(async (to, _from, next) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
router.afterEach((to) => {
|
||||||
|
setRouteChange(to)
|
||||||
|
})
|
||||||
|
|
||||||
router.afterEach(() => {
|
router.afterEach(() => {
|
||||||
NProgress.done()
|
NProgress.done()
|
||||||
})
|
})
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
/** 单独监听路由会浪费渲染性能,使用发布订阅模式去进行分发管理 */
|
|
||||||
|
|
||||||
import mitt, { type Handler } from "mitt"
|
|
||||||
import { type RouteLocationNormalized } from "vue-router"
|
|
||||||
|
|
||||||
/** 回调函数的类型 */
|
|
||||||
type Callback = (route: RouteLocationNormalized) => void
|
|
||||||
|
|
||||||
const emitter = mitt()
|
|
||||||
const key = Symbol("ROUTE_CHANGE")
|
|
||||||
let latestRoute: RouteLocationNormalized
|
|
||||||
|
|
||||||
/** 设置最新的路由信息 */
|
|
||||||
export const setRouteEmitter = (to: RouteLocationNormalized) => {
|
|
||||||
emitter.emit(key, to)
|
|
||||||
latestRoute = to
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 设置路由变化时的回调函数(可以选择立即执行一次回调函数) */
|
|
||||||
export const listenerRouteChange = (callback: Callback, immediate = false) => {
|
|
||||||
emitter.on(key, callback as Handler)
|
|
||||||
immediate && latestRoute && callback(latestRoute)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 移除路由变化事件监听器 */
|
|
||||||
export const removeRouteListener = (callback: Callback) => {
|
|
||||||
emitter.off(key, callback as Handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 移除所有路由变化事件监听器 */
|
|
||||||
export const removeAllRouteListener = () => {
|
|
||||||
emitter.off(key)
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user