feat: 新增 useRouteListener,系统统一采用该 hook 监听路由变化

This commit is contained in:
pany 2023-08-28 14:07:34 +08:00
parent a267429e5b
commit a3dce0e0f2
7 changed files with 85 additions and 80 deletions

View 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 }
}

View File

@ -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()

View File

@ -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>

View File

@ -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>

View File

@ -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(() => {

View File

@ -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()
})

View File

@ -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)
}