Skip to content

Commit 521c13e

Browse files
committed
feat(useExactView): 提取 BetterRouterView 的精准渲染功能到独立 hooks 中
- 新增 useExactView 钩子函数,用于自定义视图匹配逻辑 - 扩展 exact 属性支持 number 类型,用于指定匹配的路由层级 - 重构 BetterRouterView 组件,使用新的钩子函数实现视图匹配
1 parent 7474fcb commit 521c13e

File tree

4 files changed

+69
-27
lines changed

4 files changed

+69
-27
lines changed

src/BetterRouterView.ts

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,14 @@ import type {
77
} from 'vue'
88
import type {
99
RouteComponent,
10+
RouteLocationMatched,
1011
RouteLocationNormalizedLoaded,
1112
RouteLocationNormalizedLoadedGeneric,
1213
RouterViewProps,
1314
} from 'vue-router'
14-
import {
15-
computed,
16-
defineComponent,
17-
getCurrentInstance,
18-
h,
19-
inject,
20-
provide,
21-
shallowRef,
22-
toValue,
23-
} from 'vue'
24-
import { RouterView, useRoute, viewDepthKey } from 'vue-router'
15+
import { computed, defineComponent, getCurrentInstance, h, shallowRef } from 'vue'
16+
import { RouterView } from 'vue-router'
17+
import { useExactView } from './hooks/use-exact-view'
2518
import { getWrappers } from './wrappers'
2619

2720
export interface SlotData {
@@ -33,28 +26,26 @@ export type ResolveViewKey = (
3326
route: RouteLocationNormalizedLoaded,
3427
) => string | void | undefined | null
3528

29+
export type ExactFn = (matchedRoute: RouteLocationMatched) => boolean
30+
3631
export interface BetterRouterViewProps extends RouterViewProps {
3732
resolveViewKey?: ResolveViewKey
38-
exact?: boolean
33+
exact?: boolean | number | null
3934
}
4035

41-
export const BetterRouterView: new () => {
42-
$props: AllowedComponentProps & ComponentCustomProps & VNodeProps & BetterRouterViewProps
43-
$slots: {
44-
default?: (data: SlotData) => VNode[]
45-
}
46-
} = /* #__PURE__ */ defineComponent({
36+
const BetterRouterViewImpl = /* #__PURE__ */ defineComponent({
4737
name: 'BetterRouterView',
4838
inheritAttrs: false,
4939
props: {
5040
resolveViewKey: {
5141
type: Function,
5242
},
5343
exact: {
54-
type: Boolean,
44+
type: [Boolean, Number, null],
45+
default: null,
5546
},
5647
},
57-
setup(props: BetterRouterViewProps, { attrs, slots }) {
48+
setup(props, { attrs, slots }) {
5849
const app = getCurrentInstance()!.appContext.app
5950
const wrappers = getWrappers(app)
6051

@@ -89,13 +80,9 @@ export const BetterRouterView: new () => {
8980
return wrappers.get(name)!
9081
}
9182

92-
const route = useRoute()
93-
const viewDepth = inject(viewDepthKey, 0)
94-
provide(
95-
viewDepthKey,
96-
// route.matched 最后一个往往就是精确匹配的,这里更改 viewDepth 后可以让其直接渲染对应的视图组件
97-
computed(() => (props.exact ? route.matched.length - 1 : toValue(viewDepth))),
98-
)
83+
useExactView({
84+
exact: computed(() => props.exact),
85+
})
9986

10087
return () =>
10188
h(RouterView, attrs, {
@@ -112,3 +99,10 @@ export const BetterRouterView: new () => {
11299
})
113100
},
114101
})
102+
103+
export const BetterRouterView = BetterRouterViewImpl as new () => {
104+
$props: AllowedComponentProps & ComponentCustomProps & VNodeProps & BetterRouterViewProps
105+
$slots: {
106+
default?: (data: SlotData) => VNode[]
107+
}
108+
}

src/hooks/use-exact-view.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import type { MaybeRefOrGetter, Reactive } from 'vue'
2+
import { computed, inject, provide, toValue } from 'vue'
3+
import { useRoute, viewDepthKey } from 'vue-router'
4+
import { isBoolean } from '../utils'
5+
6+
export interface UseExactViewOptions {
7+
/**
8+
* 是否精准匹配当前路由的视图,必须在包含 `<RouterView>` 的所属组件 `setup()` 内调用
9+
* - boolean: 设为 `true` 时将精准匹配当前路由的视图,且在调用该函数的组件下所有视图组件将**无法再嵌套** `<RouterView>`!
10+
* - number: 设置为数字时,将精准匹配对应路由的视图,且在调用该函数的组件下所有视图组件**支持再嵌套** `<RouterView>`!
11+
* - null: 默认值,将还原 `<RouterView>` 渲染逻辑
12+
*/
13+
exact: MaybeRefOrGetter<boolean | number | null>
14+
}
15+
16+
export function useExactView(options: UseExactViewOptions | Reactive<UseExactViewOptions>): void {
17+
const route = useRoute()
18+
const viewDepth = inject(viewDepthKey, 0)
19+
provide(
20+
viewDepthKey,
21+
computed(() => {
22+
const exactValue = toValue(options.exact)
23+
const viewDepthValue = toValue(viewDepth)
24+
25+
if (exactValue == null) {
26+
return viewDepthValue
27+
}
28+
29+
if (isBoolean(exactValue)) {
30+
return exactValue ? route.matched.length - 1 : viewDepthValue
31+
}
32+
33+
return route.matched[exactValue] ? exactValue : viewDepthValue
34+
}),
35+
)
36+
}

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { App, ObjectPlugin } from 'vue'
22
import { BetterRouterView as Component } from './BetterRouterView'
33

44
export * from './BetterRouterView'
5+
export * from './hooks/use-exact-view'
56

67
const BetterRouterView: typeof Component & ObjectPlugin = /* #__PURE__ */ Object.assign(Component, {
78
install(app: App) {

src/utils.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export function isFunction(value: any): value is (...args: any[]) => any {
2+
return typeof value === 'function'
3+
}
4+
5+
export function isNumber(value: any): value is number {
6+
return typeof value === 'number'
7+
}
8+
9+
export function isBoolean(value: any): value is boolean {
10+
return typeof value === 'boolean'
11+
}

0 commit comments

Comments
 (0)