Skip to content

Commit

Permalink
feat(view): useView to customize router-view
Browse files Browse the repository at this point in the history
  • Loading branch information
posva committed Mar 24, 2020
1 parent 5d80209 commit 06b0c34
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 49 deletions.
3 changes: 2 additions & 1 deletion playground/views/NotFound.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@

<script>
import { defineComponent, inject } from 'vue'
import { useRoute } from '../../src'
export default defineComponent({
name: 'NotFound',
setup() {
const route = inject('route')
const route = useRoute()
return { route }
},
})
Expand Down
12 changes: 6 additions & 6 deletions src/components/Link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ import {
inject,
computed,
reactive,
Ref,
unref,
} from 'vue'
import { RouteLocation, RouteLocationNormalized, Immutable } from '../types'
import {
RouteLocation,
RouteLocationNormalized,
Immutable,
VueUseOptions,
} from '../types'
import { isSameLocationObject, isSameRouteRecord } from '../utils'
import { routerKey } from '../utils/injectionSymbols'
import { RouteRecordNormalized } from '../matcher/types'

type VueUseOptions<T> = {
[k in keyof T]: Ref<T[k]> | T[k]
}

interface LinkProps {
to: RouteLocation
// TODO: refactor using extra options allowed in router.push
Expand Down
102 changes: 62 additions & 40 deletions src/components/View.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,73 @@ import {
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: {
Expand All @@ -26,44 +85,7 @@ export const View = defineComponent({

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)
},
})
2 changes: 1 addition & 1 deletion src/history/html5.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,8 @@ function useHistoryStateNavigation(base: string) {
),
...history.state,
...data,
position: historyState.value.position,
}
if (historyState) state.position = historyState.value.position

changeLocation(normalized, state, true)
location.value = normalized
Expand Down
12 changes: 11 additions & 1 deletion src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
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>
Expand All @@ -10,6 +16,10 @@ export type Immutable<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
Expand Down

0 comments on commit 06b0c34

Please sign in to comment.