美文网首页【vue-rouer源码】
【vue-router源码】十二、useRoute、useRou

【vue-router源码】十二、useRoute、useRou

作者: MAXLZ | 来源:发表于2022-06-13 17:36 被阅读0次

    前言

    【vue-router源码】系列文章将带你从0开始了解vue-router的具体实现。该系列文章源码参考vue-router v4.0.15
    源码地址:https://github.com/vuejs/router
    阅读该文章的前提是你最好了解vue-router的基本使用,如果你没有使用过的话,可通过vue-router官网学习下。

    该篇文章将分析useRouteuseRouteruseLink的实现。

    使用

    <script lant="ts" setup>
    import { useRouter, useRoute } from 'vue-router'
    
    // router为创建的router实例
    const router = useRouter()
    // currentRoute当前路由
    const currentRoute = useRoute()
    </script>
    

    使用useLink可以自定义我们自己的RouterLink,如下面自定的MyRouterLink,如果是外部链接,我们需要让它新打开一个页面。

    <template>
      <a
        v-if="isExternalLink"
        v-bind="$attrs"
        :class="classes"
        :href="to"
        target="_blank"
      >
        <slot />
      </a>
      <a
        v-else
        v-bind="$attrs"
        :class="classes"
        :href="href"
        @click="navigate"
      >
        <slot />
      </a>
    </template>
    
    <script lang="ts">
    export default {
      name: 'MyRouterLink',
    }
    </script>
    
    <script lang="ts" setup>
    import { useLink, useRoute, RouterLink } from 'vue-router'
    import { computed } from 'vue'
    
    const props = defineProps({
      // @ts-ignore
      ...RouterLink.props
    })
    
    const { route, href, navigate, isActive, isExactActive  } = useLink(props)
    
    const isExternalLink= computed(() => typeof props.to === 'string' && props.to.startsWith('http'))
    
    const currentRoute = useRoute()
    
    const classes = computed(() => ({
      'router-link-active':
        isActive.value || currentRoute.path.startsWith(route.value.path),
      'router-link-exact-active':
        isExactActive.value || currentRoute.path === route.value.path,
    }))
    </script>
    

    MyRouterLink使用:

    <my-router-link to="https://www.xxx.com">MyRouterLink External Link</my-router-link>
    <my-router-link to="/home">MyRouterLink /home</my-router-link>
    

    useRouter、useRoute

    export function useRouter(): Router {
      return inject(routerKey)!
    }
    
    export function useRoute(): RouteLocationNormalizedLoaded {
      return inject(routeLocationKey)!
    }
    

    useRouteruseRoute都是使用inject来进行获取对应值。对应值都是在install过程中注入的。

    install(app) {
      // ...
      app.provide(routerKey, router)
      app.provide(routeLocationKey, reactive(reactiveRoute))
      // ...
    }
    

    useLink

    export function useLink(props: UseLinkOptions) {
      // router实例
      const router = inject(routerKey)!
      // 当前路由地址
      const currentRoute = inject(routeLocationKey)!
    
      // 目标路由相关信息
      const route = computed(() => router.resolve(unref(props.to)))
    
      // 被激活记录的索引
      const activeRecordIndex = computed<number>(() => {
        const { matched } = route.value
        const { length } = matched
        // 目标路由所匹配到的完整路由
        const routeMatched: RouteRecord | undefined = matched[length - 1]
        const currentMatched = currentRoute.matched
        // 如果没有匹配到的目标路由或当前路由也没有匹配到的路由返回-1
        if (!routeMatched || !currentMatched.length) return -1
        // 在当前路由所匹配到的路由中寻找目标路由
        const index = currentMatched.findIndex(
          isSameRouteRecord.bind(null, routeMatched)
        )
        if (index > -1) return index
        // 目标路由匹配到的路由的父路由的path(如果父路由是由别名产生,取源路由的path)
        const parentRecordPath = getOriginalPath(
          matched[length - 2] as RouteRecord | undefined
        )
        return (
          length > 1 &&
            // 如果目标路由的父路由与
            getOriginalPath(routeMatched) === parentRecordPath &&
            // 避免将孩子与父路由比较
            currentMatched[currentMatched.length - 1].path !== parentRecordPath
            ? currentMatched.findIndex(
                isSameRouteRecord.bind(null, matched[length - 2])
              )
            : index
        )
      })
    
      // 当前router-link是否处于激活状态,activeRecordIndex大于-1并且,当前路由的params与目标路由的params相同
      const isActive = computed<boolean>(
        () =>
          activeRecordIndex.value > -1 &&
          includesParams(currentRoute.params, route.value.params)
      )
      // 是否完全匹配,目标路由必须和当前路由所匹配到的路由最后一个相同
      const isExactActive = computed<boolean>(
        () =>
          activeRecordIndex.value > -1 &&
          activeRecordIndex.value === currentRoute.matched.length - 1 &&
          isSameRouteLocationParams(currentRoute.params, route.value.params)
      )
    
      // 利用push或replace进行路由跳转
      function navigate(
        e: MouseEvent = {} as MouseEvent
      ): Promise<void | NavigationFailure> {
        // 对于一些特殊情况,不能进行跳转
        if (guardEvent(e)) {
          return router[unref(props.replace) ? 'replace' : 'push'](
            unref(props.to)
          ).catch(noop)
        }
        return Promise.resolve()
      }
    
      // devtools only
      if ((__DEV__ || __FEATURE_PROD_DEVTOOLS__) && isBrowser) {
        // ...
      }
    
      return {
        route,
        href: computed(() => route.value.href),
        isActive,
        isExactActive,
        navigate,
      }
    }
    

    在进行路由跳转时,一些特殊情况下是不能跳转的,这些情况包括:

    1. 按住了window(MAC的commond)键、alt键、ctrl键、shift键中的任一键
    2. 调用过e.preventDefault()
    3. 右键
    4. target='_blank'
    function guardEvent(e: MouseEvent) {
      // don't redirect with control keys
      if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return
      // don't redirect when preventDefault called
      if (e.defaultPrevented) return
      // don't redirect on right click
      if (e.button !== undefined && e.button !== 0) return
      // don't redirect if `target="_blank"`
      // @ts-expect-error getAttribute does exist
      if (e.currentTarget && e.currentTarget.getAttribute) {
        // @ts-expect-error getAttribute exists
        const target = e.currentTarget.getAttribute('target')
        if (/\b_blank\b/i.test(target)) return
      }
      // this may be a Weex event which doesn't have this method
      if (e.preventDefault) e.preventDefault()
    
      return true
    }
    

    相关文章

      网友评论

        本文标题:【vue-router源码】十二、useRoute、useRou

        本文链接:https://www.haomeiwen.com/subject/wagomrtx.html