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

前言

前面的文章Vue-Router入门(一) :初识路由,我们了解到了一个children属性,代表着路由下面的子路由,也称为嵌套路由 。也提到组件 router-link 来进行跳转。本文就来详细介绍下嵌套路由以及编程式导航。

嵌套路由

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

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

写法

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

js 复制代码
// 定义路由
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名称,我们打开页面请求/about。

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

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

编程式导航

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

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

编程式导航写法

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

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

push方法

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

js 复制代码
   router.push(to)

to参数为具体的路由,ts类型为RouteLocationRaw ,返回一个promise。

js 复制代码
    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(传参)形式组合。
js 复制代码
   interface RouteQueryAndHash {
        query?: LocationQueryRaw;
        hash?: string;
   }
   interface LocationAsPath {
        path: string;
   }
   interface LocationAsRelativeRaw {
        name?: RouteRecordName;
        params?: RouteParamsRaw;
   }

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

js 复制代码
router.push({ name: 'home', params: { id: '123' } })

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

原理

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

js 复制代码
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方法。

js 复制代码
 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方法基本一样,不过该方法不会往浏览器的历史记录添加记录。

查看push源码时我们就提到replace方法也包含其中,而replace的底层API就是History.replaceState()。如果我们在用push导航时将replace属性设置为true,结果跟直接调用replace方法一样。

js 复制代码
   router.push({ name: 'login', replace:true })
   router.replace({ path: '/login' })

go方法

router.go(n):将路由前进或后退到第n条记录,n为整数,正数表示前进;负数表示后退。浏览器中前进按钮就相当于router.go(1),后退就相当于router.go(-1)。通过push与replace方法我们可以猜到router.go用到的API就是history.go()。

例子:

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

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

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

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

源码:

js 复制代码
  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
   */
相关推荐
卸任2 小时前
Vibe coding工具使用心得
前端·ai编程
brien93 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance3 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M3 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写3 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李3 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易4 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马4 小时前
前沿视界
前端
kyriewen4 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易4 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员