]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
feat(view): useView to customize router-view
authorEduardo San Martin Morote <posva13@gmail.com>
Tue, 24 Mar 2020 15:09:14 +0000 (16:09 +0100)
committerEduardo San Martin Morote <posva13@gmail.com>
Tue, 24 Mar 2020 15:09:14 +0000 (16:09 +0100)
playground/views/NotFound.vue
src/components/Link.ts
src/components/View.ts
src/history/html5.ts
src/types/index.ts

index f999958e6f900b54e247fb6bf61e29dc5e00ac5b..31e3bb02d1279ce89f43efd280e2873f9dd087dc 100644 (file)
@@ -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 }
   },
 })
index 460d6d8ee49882eb89d2b0a2fd0233b06c68fa83..0f2f916fd183da8e7e55aca2a127331db7a6f2dc 100644 (file)
@@ -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
index 07faee9e4001b3bf2be4820407cb5283e53c5561..dcb8180dcabffd8316eeccb2a5c67f723b9b3dee 100644 (file)
@@ -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: {
@@ -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)
   },
 })
index c523c99878c8ed2da8936177a80db83cd411f238..a5249a078cb64f0774884c4f3b502348f668cb02 100644 (file)
@@ -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
index 2e49b347673d2021222a29f5ab2db8d1bc955d32..6d4ca2f384f035ff63e47aa34ac7c6f754f9f9d6 100644 (file)
@@ -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>
@@ -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