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>
|
||||
import { ref, watch } from "vue"
|
||||
import { ref } from "vue"
|
||||
import { type RouteLocationMatched, useRoute, useRouter } from "vue-router"
|
||||
import { useRouteListener } from "@/hooks/useRouteListener"
|
||||
import { compile } from "path-to-regexp"
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { listenerRouteChange } = useRouteListener()
|
||||
|
||||
/** 定义响应式数据 breadcrumbs,用于存储面包屑导航信息 */
|
||||
const breadcrumbs = ref<RouteLocationMatched[]>([])
|
||||
@ -31,13 +33,10 @@ const handleLink = (item: RouteLocationMatched) => {
|
||||
}
|
||||
|
||||
/** 监听路由变化,更新面包屑导航信息 */
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
if (path.startsWith("/redirect/")) return
|
||||
getBreadcrumb()
|
||||
}
|
||||
)
|
||||
listenerRouteChange((route) => {
|
||||
if (route.path.startsWith("/redirect/")) return
|
||||
getBreadcrumb()
|
||||
})
|
||||
|
||||
/** 初始化面包屑导航信息 */
|
||||
getBreadcrumb()
|
||||
|
@ -1,10 +1,11 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, nextTick } from "vue"
|
||||
import { ref, nextTick } from "vue"
|
||||
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 { ArrowLeft, ArrowRight } from "@element-plus/icons-vue"
|
||||
import { useSettingsStore } from "@/store/modules/settings"
|
||||
import Screenfull from "@/components/Screenfull/index.vue"
|
||||
|
||||
interface Props {
|
||||
tagRefs: InstanceType<typeof RouterLink>[]
|
||||
@ -14,6 +15,7 @@ const props = defineProps<Props>()
|
||||
|
||||
const route = useRoute()
|
||||
const settingsStore = useSettingsStore()
|
||||
const { listenerRouteChange } = useRouteListener()
|
||||
|
||||
/** 滚动条组件元素的引用 */
|
||||
const scrollbarRef = ref<InstanceType<typeof ElScrollbar>>()
|
||||
@ -94,15 +96,9 @@ const moveTo = () => {
|
||||
}
|
||||
|
||||
/** 监听路由变化,移动到目标位置 */
|
||||
watch(
|
||||
route,
|
||||
() => {
|
||||
nextTick(moveTo)
|
||||
},
|
||||
{
|
||||
deep: true
|
||||
}
|
||||
)
|
||||
listenerRouteChange(() => {
|
||||
nextTick(moveTo)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -1,9 +1,9 @@
|
||||
<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 TagView, useTagsViewStore } from "@/store/modules/tags-view"
|
||||
import { usePermissionStore } from "@/store/modules/permission"
|
||||
import { listenerRouteChange, removeRouteListener } from "@/utils/route-listener"
|
||||
import { useRouteListener } from "@/hooks/useRouteListener"
|
||||
import path from "path-browserify"
|
||||
import ScrollPane from "./ScrollPane.vue"
|
||||
import { Close } from "@element-plus/icons-vue"
|
||||
@ -13,6 +13,7 @@ const router = useRouter()
|
||||
const route = useRoute()
|
||||
const tagsViewStore = useTagsViewStore()
|
||||
const permissionStore = usePermissionStore()
|
||||
const { listenerRouteChange } = useRouteListener()
|
||||
|
||||
/** 标签页组件元素的引用数组 */
|
||||
const tagRefs = ref<InstanceType<typeof RouterLink>[]>([])
|
||||
@ -148,6 +149,11 @@ const closeMenu = () => {
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
/** 监听路由变化 */
|
||||
listenerRouteChange((route) => {
|
||||
addTags(route)
|
||||
})
|
||||
|
||||
watch(visible, (value) => {
|
||||
value ? document.body.addEventListener("click", closeMenu) : document.body.removeEventListener("click", closeMenu)
|
||||
})
|
||||
@ -156,18 +162,6 @@ onMounted(() => {
|
||||
initTags()
|
||||
addTags(route)
|
||||
})
|
||||
|
||||
//#region 监听路由
|
||||
const callback = (route: RouteLocationNormalizedLoaded) => {
|
||||
addTags(route)
|
||||
}
|
||||
|
||||
listenerRouteChange(callback)
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
removeRouteListener(callback)
|
||||
})
|
||||
//#endregion
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { watch, onBeforeMount, onMounted, onBeforeUnmount } from "vue"
|
||||
import { useRoute } from "vue-router"
|
||||
import { onBeforeMount, onMounted, onBeforeUnmount } from "vue"
|
||||
import { useAppStore } from "@/store/modules/app"
|
||||
import { useRouteListener } from "@/hooks/useRouteListener"
|
||||
import { DeviceEnum } from "@/constants/app-key"
|
||||
|
||||
/** 参考 Bootstrap 的响应式设计将最大移动端宽度设置为 992 */
|
||||
@ -8,8 +8,8 @@ const MAX_MOBILE_WIDTH = 992
|
||||
|
||||
/** 根据浏览器宽度变化,变换 Layout 布局 */
|
||||
export default () => {
|
||||
const route = useRoute()
|
||||
const appStore = useAppStore()
|
||||
const { listenerRouteChange } = useRouteListener()
|
||||
|
||||
/** 用于判断当前设备是否为移动端 */
|
||||
const _isMobile = () => {
|
||||
@ -25,15 +25,12 @@ export default () => {
|
||||
isMobile && appStore.closeSidebar(true)
|
||||
}
|
||||
}
|
||||
/** 监听路由名称变化,根据设备类型调整布局 */
|
||||
watch(
|
||||
() => route.name,
|
||||
() => {
|
||||
if (appStore.device === DeviceEnum.Mobile && appStore.sidebar.opened) {
|
||||
appStore.closeSidebar(false)
|
||||
}
|
||||
/** 监听路由变化,根据设备类型调整布局 */
|
||||
listenerRouteChange(() => {
|
||||
if (appStore.device === DeviceEnum.Mobile && appStore.sidebar.opened) {
|
||||
appStore.closeSidebar(false)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
/** 在组件挂载前添加窗口大小变化事件监听器 */
|
||||
onBeforeMount(() => {
|
||||
|
@ -2,19 +2,19 @@ import router from "@/router"
|
||||
import { useUserStoreHook } from "@/store/modules/user"
|
||||
import { usePermissionStoreHook } from "@/store/modules/permission"
|
||||
import { ElMessage } from "element-plus"
|
||||
import { useRouteListener } from "@/hooks/useRouteListener"
|
||||
import { getToken } from "@/utils/cache/cookies"
|
||||
import { fixBlankPage } from "@/utils/fix-blank-page"
|
||||
import { setRouteEmitter } from "@/utils/route-listener"
|
||||
import routeSettings from "@/config/route"
|
||||
import isWhiteList from "@/config/white-list"
|
||||
import NProgress from "nprogress"
|
||||
import "nprogress/nprogress.css"
|
||||
|
||||
NProgress.configure({ showSpinner: false })
|
||||
const { setRouteChange } = useRouteListener()
|
||||
|
||||
router.beforeEach(async (to, _from, next) => {
|
||||
fixBlankPage()
|
||||
setRouteEmitter(to)
|
||||
NProgress.start()
|
||||
const userStore = useUserStoreHook()
|
||||
const permissionStore = usePermissionStoreHook()
|
||||
@ -70,6 +70,10 @@ router.beforeEach(async (to, _from, next) => {
|
||||
}
|
||||
})
|
||||
|
||||
router.afterEach((to) => {
|
||||
setRouteChange(to)
|
||||
})
|
||||
|
||||
router.afterEach(() => {
|
||||
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