入门
用 Vue + Vue Router 创建单页应用非常简单:通过 Vue.js,已经用组件组成了应用。当加入 Vue Router 时,将组件映射到路由上,让 Vue Router 知道在哪里渲染它们。
HTML
Hello App!
Go to Home
Go to About
router-link
在这里没有使用常规的 a 标签,而是使用一个自定义组件 router-link 来创建链接。这使得 Vue Router 可以在不重新加载页面的情况下更改 URL,处理 URL 的生成以及编码。
router-view
router-view 显示与 url 对应的组件。可以把它放在任何地方,以适应你的布局。
JavaScript
const Home = { template: 'Home' }
const About = { template: 'About' }
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 的形式访问它,并且以 this.$route 的形式访问当前路由:
带参数的动态路由匹配
很多时候,需要将给定匹配模式的路由映射到同一个组件。在 Vue Router 中,可以在路径中使用一个动态字段来实现,称之为 路径参数 :
const User = {
template: 'User',
}
// 这些都会传递给 `createRouter`
const routes = [
// 动态字段以冒号开始
{ path: '/users/:id', component: User },
]
现在像 /users/johnny 和 /users/jolyne 这样的 URL 都会映射到同一个路由。路径参数 用冒号 : 表示。
响应路由参数的变化
使用带有参数的路由时需要注意,当你从 /users/johnny 导航到/users/jolyne 时,相同的组件实例将被重复使用。因为两个路由都渲染同个组件,比起销毁再建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会被调用。
要对同一个组件中参数的变化做出响应的话,可以简单地 watch $route 对象上的任意属性,在这个场景中,就是 $route.params :
const User = {
template: '...',
created() {
this.$watch(
() => this.$route.params,
(toParams, previousParams) => {
// 对路由变化做出响应...
}
)
},
}
路由的匹配语法
大多数应用都会使用 /about 这样的静态路由和 /users/:userId 这样的动态路由,就像刚才在动态路由匹配中看到的那样,但是 Vue Router 可以提供更多的方式!
在参数中自定义正则
当定义像 :userId 这样的参数时,内部使用以下的正则 (如[ ^ / ]+) (至少有一个字符不是 / )来从 URL 中提取参数。当你需要根据参数的内容来区分两个路由,最简单的方法就是在路径中添加一个静态部分来区分它们:
const routes = [
// 匹配 /o/3549
{ path: '/o/:orderId' },
// 匹配 /p/books
{ path: '/p/:productName' },
]
但在某些情况下,不想添加静态的 /o /p 部分。由于,orderId 总是一个数字,而 productName 可以是任何东西,所以可以在括号中为参数指定一个自定义的正则:
const routes = [
// /:orderId -> 仅匹配数字
{ path: '/:orderId(\\d+)' },
// /:productName -> 匹配其他任何内容
{ path: '/:productName' },
]
现在,转到 /25 将匹配 /:orderId,其他情况将会匹配 /:productName。routes 数组的顺序并不重要!
可重复的参数
如果需要匹配具有多个部分的路由,如 /first/second/third,应该用 *(0 个或多个)和 +(1 个或多个)将参数标记为可重复:
const routes = [
// /:chapters -> 匹配 /one, /one/two, /one/two/three, 等
{ path: '/:chapters+' },
// /:chapters -> 匹配 /, /one, /one/two, /one/two/three, 等
{ path: '/:chapters*' },
]
这将会提供一个参数数组,而不是一个字符串,并且在使用命名路由时也需要传递一个数组:
可选参数
可以通过使用 ? 修饰符(0 个或 1 个)将一个参数标记为可选
const routes = [
// 匹配 /users 和 /users/posva
{ path: '/users/:userId?' },
// 匹配 /users 和 /users/42
{ path: '/users/:userId(\\d+)?' },
]
这里需要注意,* 在技术上也标志着一个参数是可选的,但 ? 参数不能重复。
调试
如果要探究路由是如何转化为正则的,以了解为什么一个路由没有被匹配,或者,报告一个 bug,可以使用路径排名工具。它支持通过 URL 分享路由。
嵌套路由
一些应用程序的 UI 由多层嵌套的组件组成。在这种情况下,URL 的片段通常对应于特定的嵌套组件结构,例如:
/user/johnny/profile /user/johnny/posts
+------------------+ +-----------------+
| User | | User |
| +--------------+ | | +-------------+ |
| | Profile | | +------------> | | Posts | |
| | | | | | | |
| +--------------+ | | +-------------+ |
+------------------+ +-----------------+
通过 Vue Router,可以使用嵌套路由配置来表达这种关系。
const User = {
template: 'User {{ $route.params.id }}',
}
// 这些都会传递给 `createRouter`
const routes = [{ path: '/user/:id', component: User }]
这里的
const User = {
template: `
User {{ $route.params.id }}
`,
}
要将组件渲染到这个嵌套的 router-view 中,需要在路由中配置 children:
const routes = [
{
path: '/user/:id',
component: User,
children: [
{
// 当 /user/:id/profile 匹配成功
// UserProfile 将被渲染到 User 的 内部
path: 'profile',
component: UserProfile,
},
{
// 当 /user/:id/posts 匹配成功
// UserPosts 将被渲染到 User 的 内部
path: 'posts',
component: UserPosts,
},
],
},
]
需要注意,以 / 开头的嵌套路径将被视为根路径。这允许利用组件嵌套,而不必使用嵌套的 URL。
如你所见,children 配置只是另一个路由数组,就像 routes 本身一样。因此,你可以根据自己的需要,不断地嵌套视图。
编程式导航
除了使用
导航到不同的位置
想要导航到不同的 URL,可以使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,会回到之前的 URL。
当点击
该方法的参数可以是一个字符串路径,或者一个描述地址的对象。
// 字符串路径
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 会被忽略,上述的 query 并不属于这种情况。取而代之的是下面的做法,需要提供路由的 name 或手写完整的带有参数的 path :
const username = 'eduardo'
// 可以手动建立 url,但必须自己处理编码
router.push(`/user/${username}`) // -> /user/eduardo
// 同样
router.push({ path: `/user/${username}` }) // -> /user/eduardo
// 如果可能的话,使用 `name` 和 `params` 从自动 URL 编码中获益
router.push({ name: 'user', params: { username } }) // -> /user/eduardo
// `params` 不能与 `path` 一起使用
router.push({ path: '/user', params: { username } }) // -> /user
由于属性 to 与 router.push 接受的对象种类相同,所以两者的规则完全相同。
router.push 和所有其他导航方法都会返回一个 Promise,让我们可以等到导航完成后才知道是成功还是失败。
替换当前位置
它的作用类似于 router.push,唯一不同的是,它在导航时不会向 history 添加新记录,正如它的名字所暗示的那样——它取代了当前的条目。
也可以直接在传递给 router.push 的 routeLocation 中增加一个属性 replace: true :
router.push({ path: '/home', replace: true })
// 相当于
router.replace({ path: '/home' })