likes
comments
collection
share

Vue-Router入门(三) :嵌套路由和编程式导航

作者站长头像
站长
· 阅读数 4

前言

嵌套路由

因为组件是有嵌套关系的存在,路由与组件是对应的,如果我们做过一些比较复杂的项目,它的页面大多由多层嵌套的组件组成,所有我们就一定会接触到嵌套路由。

Vue-Router入门(三) :嵌套路由和编程式导航

嵌套路由的语法很简单,就是在创建路由时将子路由放到childrenchildren的ts接口依旧为RouteRecordRaw数组,这也就意味着嵌套可以无限下去。

写法

嵌套路由写法虽然很简单,但还是有好几个注意的点,下面我们就在Root路由添加一个子路由渲染下视图。

// 定义路由
const routes: Array<RouteRecordRaw> = [
  {
    path: "/",
    name: "Root",
    component: () => import("../views/TheRoot.vue"),
    //定义子路由
    children: [
      {
        path: "/home",
        name: "home",
        component: HomeView,
      },
      {
        path: "/about",
        name: "about",
        //也可以通过懒加载的方式
        component: () => import("../views/AboutView.vue"),
      },
    ],
  },
];

我们添加了一个about子路由,加载路由的写法就是父路由后面拼接path名称,我们打开页面请求/aboutVue-Router入门(三) :嵌套路由和编程式导航

还是Root界面,这是为什么呢?原因很简单,TheRoot文件没有添加router-view路由想要渲染出对应的视图,必须要用router-view,嵌套路由就需要在父路由添加。我们在Root界面添加一个router-view,然后刷新页面。

Vue-Router入门(三) :嵌套路由和编程式导航

子路由对应的组件就渲染出来了。嵌套路由的表现出来的就是嵌套组件,这也是我们一直强调的路由对于组件,

编程式导航

前面章节我们都是通过router-link进行路由之间的跳转,这种是声明式导航。但日常开发中我们需要从代码层面进行路由跳转,也就是编程式导航,本节我们就来学习一下。

本部分所涉及的所有源码都在这里源码位置

编程式导航写法

编程式导航在代码层面操作路由,这些操作都是router实例方法,要想使用这些方法就得先访问router实例。vue版本不同访问router实例的方法也不同:

  • vue2.x(2.7可以引入组合式API)使用编程式API可以通过this.$router进行访问。
  • vue3.x组合式API,在setup中需要使用useRouter函数。 router中有很多实例方法,下面我们了解一下导航相关的。

push方法

router.push方法用于跳转路由,可以导航到任意页面,具体的语法为:

   router.push(to)
    push(to: RouteLocationRaw): Promise<NavigationFailure | void | undefined>;
    /**
     * Programmatically navigate to a new URL by replacing the current entry in
     * the history stack.
     *
     * @param to - Route location to navigate to
     */

RouteLactionRaw是一个联合类型,核心是两种形式:

  • RouteQueryAndHash&LocationAsPath&RouteLocationOptions:path(路径)、query(传参)、hash形式组合。
  • RouteQueryAndHash & LocationAsRelativeRaw & RouteLocationOptions:name(命令视图)、params(传参)形式组合。
   interface RouteQueryAndHash {
        query?: LocationQueryRaw;
        hash?: string;
   }
   interface LocationAsPath {
        path: string;
   }
   interface LocationAsRelativeRaw {
        name?: RouteRecordName;
        params?: RouteParamsRaw;
   }

push的参数对象不能混用,比如name与query不能一块使用,这是由具体的ts类型决定的。

router.push({ name: 'home', params: { id: '123' } })

router.push({ path: '/home', query: { id: '123' } })

原理

push的原理就是向history添加新记录,如果我们在A页面用push导航到B页面,点击浏览器退回页面就会回到A页面。虽说原理很简单,但源码还是复杂的,毕竟要考虑各种情况。首先会判断是否有重定向地址,如果有跳转重定向地址。

function pushWithRedirect(
    to: RouteLocationRaw | RouteLocation,
    redirectedFrom?: RouteLocation
  ): Promise<NavigationFailure | void | undefined> {
    const targetLocation: RouteLocation = (pendingLocation = resolve(to))
    const from = currentRoute.value
    const data: HistoryState | undefined = (to as RouteLocationOptions).state
    const force: boolean | undefined = (to as RouteLocationOptions).force
    // to could be a string where `replace` is a function
    const replace = (to as RouteLocationOptions).replace === true

    const shouldRedirect = handleRedirectRecord(targetLocation)

    if (shouldRedirect)
      return pushWithRedirect(
        assign(locationAsObject(shouldRedirect), {
          state:
            typeof shouldRedirect === 'object'
              ? assign({}, data, shouldRedirect.state)
              : data,
          force,
          replace,
        }),
        // keep original redirectedFrom if it exists
        redirectedFrom || targetLocation
      )

pushWithRedirect还考虑了replace导航,通过一系列判断最终调用routerHistory的push方法。

 if (isPush) {
      // on the initial navigation, we want to reuse the scroll position from
      // history state if it exists
      if (replace || isFirstNavigation)
        routerHistory.replace(
          toLocation.fullPath,
          assign(
            {
              scroll: isFirstNavigation && state && state.scroll,
            },
            data
          )
        )
      else routerHistory.push(toLocation.fullPath, data)
    }

replace方法

router.replace替换当前路由,replace的用法跟push方法基本一样,不过该方法不会往浏览器的历史记录添加记录

   router.push({ name: 'login', replace:true })
   router.replace({ path: '/login' })

go方法

例子:

// 向前移动一条记录,与 router.forward() 相同
router.go(1)

// 返回一条记录,与 router.back() 相同
router.go(-1)

// 前进 3 条记录
router.go(3)

// 如果没有那么多记录,静默失败
router.go(-100)
router.go(100)

源码:

  back(): ReturnType<Router['go']>
  /**
   * Go forward in history if possible by calling `history.forward()`.
   * Equivalent to `router.go(1)`.
   */
  forward(): ReturnType<Router['go']>
  /**
   * Allows you to move forward or backward through the history. Calls
   * `history.go()`.
   *
   * @param delta - The position in the history to which you want to move,
   * relative to the current page
   */
  go(delta: number): void

  /**
   * Add a navigation guard that executes before any navigation. Returns a
   * function that removes the registered guard.
   *
   * @param guard - navigation guard to add
   */