computed,
ref,
ComponentPublicInstance,
+ unref,
+ SetupContext,
} from 'vue'
-import { RouteLocationMatched } from '../types'
+import {
+ RouteLocationMatched,
+ VueUseOptions,
+ RouteLocationNormalizedResolved,
+ Immutable,
+} from '../types'
import {
matchedRouteKey,
viewDepthKey,
routeLocationKey,
} from '../utils/injectionSymbols'
+interface ViewProps {
+ route: Immutable<RouteLocationNormalizedResolved>
+ name: string
+}
+
+type UseViewOptions = VueUseOptions<ViewProps>
+
+export function useView(options: UseViewOptions) {
+ const depth: number = inject(viewDepthKey, 0)
+ provide(viewDepthKey, depth + 1)
+
+ const matchedRoute = computed(
+ () =>
+ unref(options.route).matched[depth] as RouteLocationMatched | undefined
+ )
+ const ViewComponent = computed(
+ () =>
+ matchedRoute.value && matchedRoute.value.components[unref(options.name)]
+ )
+
+ const propsData = computed(() => {
+ // propsData only gets called if ViewComponent.value exists and it depends on matchedRoute.value
+ const { props } = matchedRoute.value!
+ if (!props) return {}
+ const route = unref(options.route)
+ if (props === true) return route.params
+
+ return typeof props === 'object' ? props : props(route)
+ })
+
+ provide(matchedRouteKey, matchedRoute)
+
+ const viewRef = ref<ComponentPublicInstance>()
+
+ function onVnodeMounted() {
+ // if we mount, there is a matched record
+ matchedRoute.value!.instances[unref(options.name)] = viewRef.value
+ // TODO: trigger beforeRouteEnter hooks
+ }
+
+ return (attrs: SetupContext['attrs']) => {
+ return ViewComponent.value
+ ? h(ViewComponent.value as any, {
+ ...propsData.value,
+ ...attrs,
+ onVnodeMounted,
+ ref: viewRef,
+ })
+ : null
+ }
+}
+
export const View = defineComponent({
name: 'RouterView',
props: {
setup(props, { attrs }) {
const route = inject(routeLocationKey)!
- const depth: number = inject(viewDepthKey, 0)
- provide(viewDepthKey, depth + 1)
-
- const matchedRoute = computed(
- () => route.value.matched[depth] as RouteLocationMatched | undefined
- )
- const ViewComponent = computed(
- () => matchedRoute.value && matchedRoute.value.components[props.name]
- )
-
- const propsData = computed(() => {
- // propsData only gets called if ViewComponent.value exists and it depends on matchedRoute.value
- const { props } = matchedRoute.value!
- if (!props) return {}
- if (props === true) return route.value.params
-
- return typeof props === 'object' ? props : props(route.value)
- })
-
- provide(matchedRouteKey, matchedRoute)
-
- const viewRef = ref<ComponentPublicInstance>()
-
- function onVnodeMounted() {
- // if we mount, there is a matched record
- matchedRoute.value!.instances[props.name] = viewRef.value
- // TODO: trigger beforeRouteEnter hooks
- }
-
- return () => {
- return ViewComponent.value
- ? h(ViewComponent.value as any, {
- ...propsData.value,
- ...attrs,
- onVnodeMounted,
- ref: viewRef,
- })
- : null
- }
+ const renderView = useView({ route, name: props.name })
+ return () => renderView(attrs)
},
})
import { LocationQuery, LocationQueryRaw } from '../utils/query'
import { PathParserOptions } from '../matcher/path-parser-ranker'
-import { markNonReactive, ComponentOptions, ComponentPublicInstance } from 'vue'
+import {
+ markNonReactive,
+ ComponentOptions,
+ ComponentPublicInstance,
+ Ref,
+ ComputedRef,
+} from 'vue'
import { RouteRecordNormalized } from '../matcher/types'
export type Lazy<T> = () => Promise<T>
readonly [P in keyof T]: Immutable<T[P]>
}
+export type VueUseOptions<T> = {
+ [k in keyof T]: Ref<T[k]> | T[k] | ComputedRef<T[k]>
+}
+
export type TODO = any
export type RouteParamValue = string