Vue Router基础(一小时搞定)

330 阅读4分钟

零、上手

  • <router-link to:"?">:(路由入口)指定链接进行导航
  • <router-view>:(路由入口)路由匹配到的组件将渲染在这里
  • 上述的映射需要定义对应的路由、路由组件,创建路由示例:
//定义路由组件(可以从其他文件导入)
const Home = { template: '<div>Home</div>' }
const About = { template: '<div>About</div>' }

//定义对应的路由(是一个数组对象)
const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About },
]

//创建路由实例并挂载
const router = VueRouter.createRouter({
  history: VueRouter.createWebHashHistory(),
  routes, // `routes: routes` 的缩写
})

const app = Vue.createApp({})
app.use(router)

app.mount("#app")

通过app.use(router)之后,就可以在任意子组件中以this.$router的形式访问当前路由了,也可以操作当前路由来实现编程式导航。

一、路由匹配规则

1. 动态路由与参数

在路径中使用一个动态字段可以实现给定匹配模式的路由映射到同一个组件,例如:

//$route.params 表示所获取的所有动态字段,可以用在User的模板中
const User = {
  template: '<div>User{{ $route.params.id }}</div>',
}

const routes = [
  // 动态字段以冒号开始,这里‘id’就是一个动态字段参数
  //$route.params 就是 { id: 'xxx' }
  { path: '/users/:id', component: User },
]

注意,当用户从从 /users/johnny 导航到 /users/jolyne 时,相同的组件实例将被重复使用,这样做是为了复用组件,但是这也意味着组件的生命周期钩子不会被调用。如果需要对参数的变化做出响应,可以使用watch$router对象上的任意属性。例如:

const User = {
  template: '...',
  created() {
    this.$watch(
      () => this.$route.params,
      (toParams, previousParams) => {
        // 对路由变化做出响应...
      }
    )
  },
}

侦听器见 侦听器 | Vue.js (vuejs.org)

另外一种常见的需求是捕获所有路由或者404 NOT FOUND路由,前者可以使用自定义的 路径参数 正则表达式,只需要在 路径参数 后面的括号加入 正则表达式 即可。

2. 高级匹配语法

例子:

const routes = [
  // /:orderId -> 仅匹配数字
  { path: '/:orderId(\\d+)' }, //注意转义反斜杠
  // /:productName -> 匹配其他任何内容
  { path: '/:productName' },
]

如果需要匹配具有多个部分的路由,比如/one/two/three,这个时候需要用到*+

const routes = [
  // /:chapters ->  匹配 /one, /one/two, /one/two/three, 等
  { path: '/:chapters+' },
  // /:chapters -> 匹配 /, /one, /one/two, /one/two/three, 等
  { path: '/:chapters*' },
]

如果一个参数是可选的(0个或1个),这个时候要用到?将一个参数来标记为可选的。

更加细致的定制化的匹配规则都围绕正则表达式展开。

二、编程式导航

1. 导航到不同位置

一句话:通过$router访问路由实例,调用this.$router.push方法。

调用this.$router.push方法相当于点击对应的<router-link :to="...">。这个方法的参数可以是一个路径字符串,也可以是一个描述目标地址的对象:

// 字符串路径
router.push('/users/eduardo')

// 带有路径的对象
router.push({ path: '/users/eduardo' })

// 命名的路由,并加上参数,让路由建立 url。
router.push({ name: 'user', params: { username: 'eduardo' } })

// 带查询参数,结果是 /register?plan=private
router.push({ path: '/register', query: { plan: 'private' } })

// 带 hash,结果是 /about#team
router.push({ path: '/about', hash: '#team' })

注意:如果对象中提供的path,则params会被忽略。提供给params的参数都会除了 字符串 和 数字类型 都会被自动字符串化。

该方法会返回一个Promise,可以等到导航完成后才知道是失败还是成功。

2. 替换当前位置

  • 声明式:<router-link :to="..." replace>
  • 编程式:router.replace(...)或者router.push({ path: '/xxxx', replace: true })

3. 历史操作

类似于原生js中的window.history.go(n),可以使用router.go(n)来表示在历史栈中前进或后退多少步。例如:

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

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

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

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

同样的,其他原生js中window.history的API与Vue router中也极为类似。

三、嵌套路由和命名视图

1. 嵌套与命名路由

一般而言,一个应用程序的UI是由多层嵌套的组件构成的,URL的片段也通常对应于特定的组件结构,可以通过嵌套路由配置来表达这种关系:顶层的<router-view>渲染顶层路由匹配到的组件,被渲染的组件也可以包含自己嵌套的<router-view>。例如:

//组件模板:
const User = {
  template: `
    <div class="user">
      <h2>User {{ $route.params.id }}</h2>
      <router-view></router-view>
    </div>
  `,
}
//路由配置
const routes = [
  {
    path: '/user/:id',
    component: User,
    children: [
      {
        // 当 /user/:id/profile 匹配成功
        // UserProfile 将被渲染到 User 的 <router-view> 内部
        path: 'profile',
        component: UserProfile,
      },
      {
        // 当 /user/:id/posts 匹配成功
        // UserPosts 将被渲染到 User 的 <router-view> 内部
        path: 'posts',
        component: UserPosts,
      },
    ],
  },
]

注意一个细节,一级path中开头使用了/,二级path没有,因为以/开头的(嵌套)路径将被视为根路径。

除了path之外,还可以为任何路由提供name,可以实现参数的自动编码解码,并绕过路径排序。路由配置中提供了name后,就可以用于<router-link>router.push中了。

2. 同级命名视图

上述的嵌套路由用于嵌套展示,但是常存在同级展示多个视图的需求,比如一个有 测导航 和 主内容 两个视图的页面。如何应对这个问题呢?你可以在界面中设置多个<router-view>出口,每个出口设置一个names属性(如果没有,默认为default)。

<router-view class="view left-sidebar" name="LeftSidebar"></router-view>
<router-view class="view main-content"></router-view>
<router-view class="view right-sidebar" name="RightSidebar"></router-view>

对应的,路由配置也要做出改动,改动的地方在components键值:

const router = createRouter({
  history: createWebHashHistory(),
  routes: [
    {
      path: '/',
      components: {
        default: Home,
        // LeftSidebar: LeftSidebar 的缩写
        LeftSidebar,
        // 它们与 `<router-view>` 上的 `name` 属性匹配
        RightSidebar,
      },
    },
  ],
})

3. 嵌套命名视图

当然我们可能会使用命名视图创建嵌套视图的复杂布局,此时要结合上面所述,用到嵌套的router-view组件。借用官方的例子,设置如下的面板:

/settings/emails                                      
/settings/profile
+-----------------------------------+                  +------------------------------+
| UserSettings                      |                  | UserSettings                 |
| +-----+-------------------------+ |                  | +-----+--------------------+ |
| | Nav | UserEmailsSubscriptions | |  +------------>  | | Nav | UserProfile        | |
| |     +-------------------------+ |                  | |     +--------------------+ |
| |     |                         | |                  | |     | UserProfilePreview | |
| +-----+-------------------------+ |                  | +-----+--------------------+ |
+-----------------------------------+                  +------------------------------+

对应的路由配置为:

{
  path: '/settings',
  // 你也可以在顶级路由就配置命名视图
  component: UserSettings,
  children: [
  {
    path: 'emails',
    component: UserEmailsSubscriptions
  }, 
  {
    path: 'profile',
    components: {
          default: UserProfile,
          helper: UserProfilePreview
        }
  }
  ]
}

四、重定向和别名

重定向的实现只需要在路由配置中增加一个redirect键:

const routes = [{ path: '/home', redirect: '/' }]

当然,重定向的目标也可以是一个命名路由:

const routes = [{ path: '/home', redirect: { name: 'homepage' } }]

或者动态返回重定向目标。

相对重定向时,相对位置不以/开头。

别名的设置只需要在路由配置中增加一个alias键:

const routes = [{ path: '/', component: Homepage, alias: '/home' }]

当然也可以用一个数组提供多个别名;

{ path: '', component: UserList, alias: ['/people', 'list'] }