前言
前面的文章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
*/