Skip to content

路由配置

TIP

一条路由记录描述要匹配的 URL 与要渲染的组件。会在代码中反复使用的目的地建议命名;一次性的直接跳转可使用路径。

一份实用的路由表

ts
import { createRouter, createWebHistory } from "@elfui/router";

export const router = createRouter({
  history: createWebHistory("/console"),
  linkActiveClass: "nav-active",
  linkExactActiveClass: "nav-exact",
  routes: [
    { path: "/", name: "home", component: () => import("../pages/home-page") },
    {
      path: "/users/:id(\\d+)",
      name: "user",
      component: () => import("../pages/user-page"),
      props: (route) => ({ id: Number(route.params.id) }),
      meta: { requiresAuth: true, title: "用户" },
    },
    { path: "/member/:id", redirect: (to) => ({ name: "user", params: to.params }) },
    { path: "/me", alias: ["/profile", "/account"], component: () => import("../pages/profile-page") },
    { path: "/:pathMatch(.*)*", name: "not-found", component: () => import("../pages/not-found-page") },
  ],
});

WARNING

配置的 base 会包含在面向浏览器的 href 中:router.resolve({ name: "user", params: { id: 7 } }).href 会得到 /console/users/7。路由记录的 path 不需要、也不应写入 base。

参数与匹配优先级

参数会解析到 route.params。支持可选参数(?)、零到多个(*)、一个到多个(+)和自定义正则片段。

ts
const routes = [
  { path: "/products/new", component: "product-editor" }, // 静态路由,优先匹配
  { path: "/products/:id(\\d+)", component: "product-page" },
  { path: "/docs/:sections+", component: "docs-page" }, // params.sections 是 string[]
  { path: "/search/:term?", component: "search-page" },
];

TIP

静态片段的优先级高于动态片段,因此 /products/new 不会被当作 id。兜底路由应放在最后。默认匹配不区分大小写且忽略末尾斜杠;需要区分时可以开启:

ts
createRouter({
  history: createWebHistory(),
  sensitive: true, // /Users 与 /users 不同
  strict: true, // /users 与 /users/ 不同
  routes,
});

嵌套路由与元信息

子路径会与父路径拼接;匹配子路由时,route.matched 是从父到子的记录链。父级 meta 会合并到子路由,同名字段以子级为准。

ts
{
  path: "/settings",
  component: () => import("../pages/settings-layout"),
  meta: { requiresAuth: true },
  children: [
    { path: "", name: "settings", component: () => import("../pages/settings-general") },
    { path: "security", name: "settings-security", component: () => import("../pages/settings-security") }
  ]
}

布局页面通过 depth="1" 渲染子路由出口:

html
<h1>设置</h1>
<elf-router-view depth="1"></elf-router-view>

Props 与命名视图

使用 props: true 直接传递路由参数。固定值可用对象;需要转换参数或 query 时可用函数。

ts
{
  path: "/reports/:year",
  components: {
    default: () => import("../pages/report-page"),
    aside: () => import("../pages/report-filter")
  },
  props: {
    default: route => ({ year: Number(route.params.year), mode: route.query.mode ?? "summary" }),
    aside: { heading: "报表筛选" }
  }
}

在默认出口旁边渲染命名出口:

html
<section><elf-router-view></elf-router-view></section>
<aside><elf-router-view name="aside"></elf-router-view></aside>

启动后添加路由

动态路由适合功能开关、插件或权限。addRoute() 会返回移除函数;同名的新路由会替换旧记录。

ts
const removeBilling = router.addRoute({
  path: "/billing",
  name: "billing",
  component: () => import("../pages/billing-page"),
  meta: { requiresAuth: true },
});

if (router.hasRoute("billing")) {
  await router.push({ name: "billing" });
}

// 后续关闭功能。
removeBilling();
// 等价的显式 API:router.removeRoute("billing")

addRoute 传入父级名称即可添加子记录:router.addRoute("settings", { path: "billing", name: "settings-billing", component: "billing-page" })getRoutes() 返回当前顶层记录;clearRoutes() 会移除全部记录。

恢复滚动位置

成功导航后会调用 scrollBehavior(to, from, savedPosition)。前进/后退时返回 savedPosition 以恢复位置;普通导航可返回 { top, left }{ el: "#id" } 可滚动到锚点;返回 null 则不改变滚动位置,也可以返回 Promise。

ts
createRouter({
  history: createWebHistory(),
  routes,
  scrollBehavior(to, _from, savedPosition) {
    if (savedPosition) return savedPosition;
    if (to.hash) return { el: to.hash, behavior: "smooth" };
    return { top: 0, left: 0 };
  },
});