# 路由
## 介绍
Vue路由是vue.js的专业路由工具。它与Vue核心深度集成可以轻而易举的使用vue.js创建单页面应用(SPA)。功能包括:
* 路由嵌套映射
* 动态路由
* 模块化,基于组件的路由配置
* 路由参数,查询,通配符支持
* 基于vue.js过渡系统的视图过渡效果
* 精细导航控制
* 自动激活链接CSS样式
* html5历史模式或哈稀模式
* 自定义滚动行为
* URL编码
## 安装
### **直接下载或CDN**
[https://unpkg.com/vue-router@4](https://unpkg.com/vue-router@4)
[Unpkg.com](https://unpkg.com/)提供基于CDM的链接。上面的链接指向一个最新版本的NPM。你也可以通过`https://unpkg.com/vue-router@4.0.5/dist/vue-router.global.js`查询特定的版本/标签。
### **NPM**
~~~
npm install vue-router@4
~~~
### **yarn**
~~~
yarn add vue-router@4
~~~
## 必备基础(Essentials)
### **开始**
使用Vue + Vue路由创建一个单页面应用(SPA)是一个很自然的事情:使用vue.js我们已经准备好了使用组件构成应用。当把Vue路由融合进来以后,所有需要我们做的就是将组件映射到路由和让Vue路由知道在何处渲染他们。下面是一个基础示例:
* **HTML**
~~~html
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>router-basic</title>
<script src="https://unpkg.com/vue@next"></script>
<script src="https://unpkg.com/vue-router@4"></script>
</head>
<body>
<div id="app">
<h1>你好,路由!</h1>
<p>
<!--使用router-link组件进行导航-->
<!--通过to属性来指定链接地址-->
<!--最终`router-link`会被渲染为有正确`href`的`a`标签-->
<router-link to="/">首页</router-link>
<router-link to="/about">关于我们</router-link>
</p>
<!--路由出路-->
<!--根据链接导航的地址会渲染到这里-->
<router-view></router-view>
</div>
<script type="text/javascript">
//1. 先定义组件,这些可以从独立文件中import进来
const Home = {template: '<div>这是首页</div>'}
const About = {template: '<div>这是关于我们</div>'}
//2. 定义路由变量
// 每一条路由需要映射到一个组件
// 后面会说到路由嵌套
const routes = [
{path: '/',component: Home},
{path: '/about', component: About}
]
//3. 传入 routes创建路由实例
// 先尽量简洁,后面还可以传入其他的选项
const router = VueRouter.createRouter({
//4. use需要提供历史实现。这里使用简单路由模式。
history:VueRouter.createWebHashHistory(),
routes // routes:routes的简写
})
// app实例
const app = Vue.createApp({})
// 确保_use_路由实例,引入全局路由中间件
app.use(router)
app.mount("#app")
</script>
</body>
</html>
~~~
<iframe height="265" style="width: 100%;" scrolling="no" title="vue-router-basic" src="https://codepen.io/zhouyu629/embed/vYgOXNV?height=265&theme-id=dark&default-tab=js,result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
See the Pen <a href='https://codepen.io/zhouyu629/pen/vYgOXNV'>vue-router-basic</a> by zhou
(<a href='https://codepen.io/zhouyu629'>@zhouyu629</a>) on <a href='https://codepen.io'>CodePen</a>.
</iframe>
调用 `app.use(router)`,我们可以像任何组件中通过`this.$route`访问当前路由一样通过`this.$router`访问它。
* **JavaScript**
~~~
// Home.vue
export default {
computed: {
username() {
// We will see what `params` is shortly
return this.$route.params.username
},
},
methods: {
goToDashboard() {
if (isAuthenticated) {
this.$router.push('/dashboard')
} else {
this.$router.push('/login')
}
},
},
}
~~~
在`setup`函数中访问路由,调用`useRouter`或`useRoute`函数。学习[组合API](https://next.router.vuejs.org/guide/advanced/composition-api.html#accessing-the-router-and-current-route-inside-setup)了解更多
<br />
整个文档中,我们将会经常` use router`实例。记住`this.$router`实际上同通过`createRouter`直接创建的`router`实例一样。我们使用`this.$router`是因为我们不需要在每个独立组件中都管理路由。
### **动态路由匹配**
我们会经常将路由映射到给定样式的同一组件。例如我们有一个`user`的组件,所有用户都会使用这个组件做展示,但根据userId不同展示的内容可能不同。Vue的路由可以动态的传递参数来实现:
~~~
const User = {
template: '<div>User</div>',
}
// 下面变量要传递给 `createRouter`
const routes = [
// 动态参数以冒号开始
{ path: '/users/:id', component: User },
]
~~~
现在URL像`/users/johnny`和`/users/jolyne`都映射到同一组件。
<br />
一个有冒号`:`标志的参数,一旦路由匹配到,在每个组件中都期望参数值传给`this.$route.params`。因此,我们可以更新用户的id来渲染模板:
~~~
const User = {
template: '<div>User {{ $route.params.id }}</div>',
}
~~~
同一路由上可以有多个参数,它们会映射到`this.$route.params.`相应的字段上。例如:
| pattern | matched path | $route.params |
| --- | --- | --- |
| /users/:username | /users/eduardo | `{ username: 'eduardo' }` |
| /users/:username/posts/:postId | /users/eduardo/posts/123 | `{ username: 'eduardo', postId: '123' }` |
`$route.params`另外,`$route`对象也期望其它有用信息如`$route.query`(如果url中有query的话),`$route.query`等。你可以查看全部细节`[API参考](https://next.router.vuejs.org/api/#routelocationnormalized)`。
这有一个可运行的示例:[demo](https://codesandbox.io/s/route-params-vue-router-examples-mlb14?from-embed&initialpath=%2Fusers%2Feduardo%2Fposts%2F1)
>[译者按]
我觉得多个参数第一个参数不带key有点不规范。试了下使用kv方式也是可行的。
/users/username/:username/posts/:postId
### **响应参数变化**
使用带参数的路由有一件事需要注意,当用户从`/users/johnny`导航到`/users/jolyne`时,**相同的组件实例将会被复用**。复用同一组件,总比销毁旧组件再实例化新组件效率要高。**但是,这意味着组件的生命周期勾子将不会被调用 !**
响应同一组件的参数变化,你只需简单的监听`$route`对象,就是`$route.params`:
~~~
const User = {
template: '...',
created() {
this.$watch(
() => this.$route.params,
(toParams, previousParams) => {
// react to route changes...
}
)
},
}
~~~
或者,使用`beforeRouteUpdate`[导航向导](https://next.router.vuejs.org/guide/advanced/navigation-guards.html),它也可以让你取消导航:
~~~
const User = {
template: '...',
async beforeRouteUpdate(to, from) {
// react to route changes...
this.userData = await fetchUser(to.params.id)
},
}
~~~
### **拦截所有404路由**
常规参数只会匹配URL间`/`分隔的不同片断。如果我们想匹配任何内容,我们可以通过在参数后括号内加入正则表达式来定制一个参数规则:
~~~
const routes = [
// 会匹配 `$route.params.pathMatch`中的所有内容
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
// 匹配所有以 `/user-`开始和`$route.params.afterUser`下面的所有内容
{ path: '/user-:afterUser(.*)', component: UserGeneric },
]
~~~
特定方案我们在括号内使用[定制正则表达式](https://next.router.vuejs.org/guide/essentials/route-matching-syntax.html#custom-regexp-in-params)和将`pathMatch`参数作为可[重复选择项](https://next.router.vuejs.org/guide/essentials/route-matching-syntax.html#optional-parameters)。如果我们需要将`path`分割成一个数组,这可以让我们直接导航到路由。
~~~
this.$router.push({
name: 'NotFound',
//保存当前路径和移除第一个字符避免目标URL以`//`开始
params: { pathMatch: this.$route.path.substring(1).split('/') },
// 如果有query和has,保存之
query: this.$route.query,
hash: this.$route.hash,
})
~~~
更多请查阅[重复参数](https://next.router.vuejs.org/guide/essentials/route-matching-syntax.html#repeatable-params)
如果你使用[hsitory模式](https://next.router.vuejs.org/guide/essentials/history-mode.html),也要确认跟随介绍正确配置你的服务。
### **高级匹配模式**
Vue路径使用自带的路径匹配语法,使用`express`,所以它支持一些高级参数模式如选项参数,0或更多/一个或更多([译者按]应该是对应正则里的*和+)需求,甚至是自定义正则表达式模式。参考[高级匹配(https://next.router.vuejs.org/guide/essentials/route-matching-syntax.html)学习更多知识。
## 高级