vue-router基本使用
Vue Router功能
- 嵌套的路由/视图表
- 模块化的、基于组件的路由配置
- 路由参数、查询、通配符
- 基于 Vue.js 过渡系统的视图过渡效果
- 细粒度的导航控制
- 带有自动激活的 CSS class 的链接
- HTML5 历史模式或 hash 模式,在 IE9 中自动降级
- 自定义的滚动条行为
安装
- CDN
<script src="https://unpkg.com/vue/dist/vue.js"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
- npm
npm install --save-dev vue-router
import Vue from 'vue'; import VueRouter from 'vue-router' Vue.use(VueRouter)
基本用法
在html中应用
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- 使用 router-link 组件来导航. -->
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<!-- 路由出口 路由匹配到的组件将渲染在这里-->
<router-view></router-view>
</div>
js部分
// 1. 定义组件
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
// 2. 定义路由。每个路由应该映射一个组件
const router = new VueRouter({
routes:[
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
})
// 3. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
const app = new Vue({
router
}).$mount('#app')
模块化应用
- vue create一个实例
- 创建组件(components文件夹)
- router.js
//1.加载vue与vue-router
//2.导入相应组件
//3.编写并导出路由
import VueRouter from 'vue-router'
Vue.use(VueRouter)
import Vue from 'vue'
import PageHome from './components/pages/Home';
import PageAbout from './components/pages/About';
const router=new VueRouter({
routes:[
{
path:'/',
component:PageHome
},
{
path:'/about',
component:PageAbout
},
]
})
export default router
- main.js导入路由
- App.vue呈现路由视图
<router-view/>
嵌套路由
嵌套路由允许指定子路由,并且用另一个<router-view/>
来显示其内容。假设这里有两个单页面,一个为address一个为email,他们共用header和nav:
-
在common中创建两个共用组件:
NavHeader
和NavLeft
-
在pages创建主页面
Settings.vue
和两个子组件SettingsAddress
和SettingsEmail
-
将公共组件放在主页面
Settings.vue
中,其中router-view
展现的是其子路由。可以适当加样式<template> <div> <NavHeader/> <NavLeft/> <router-view/> </div> </template> <script> import NavHeader from '../common/NavHeader' import NavLeft from '../common/NavLeft' export default{ name:'Settings', components:{ NavHeader, NavLeft } } </script>
-
router.js
//... import PageSettings from './components/pages/Settings'; import PageSettingsEmail from './components/pages/SettingsEmail'; import PageSettingsAddress from './components/pages/SettingsAddress'; const router=new VueRouter({ routes:[ { path:'/settings', component:PageSettings, children:[ { path:'email', component:PageSettingsEmail }, { path:'address', component:PageSettingsAddress }, ] }, ] }) export default router
最后呈现:
重定向和别名
有些时候,你不希望前往/a
访问的用户看到一个错误页面,也不希望搜索引擎链接到一堆不存在的页面上。那么可以用上redirect
,这个时候任何对/a
的访问都会被重定向到/b
const router=new VueRouter({
routes:[
{
path:'/a',
redirect:'/b',
},
]
})
那么“别名”又是什么呢?
如下面的例子。/a
的别名是 /b
,意味着,当用户访问 /b
时,URL 会保持为 /b
,但是路由其实指向 /a
,就像用户访问 /a
一样。
const router = new VueRouter({
routes: [
{
path: '/a',
alias: '/b' ,
component: A,
}
]
})
命名路由
//有时候,通过一个名称来标识一个路由显得更方便一些,特别是在链接跳转路由的时候,下面的编程式导航会有例子
const router = new VueRouter({
routes: [
{
path: '/user,
name: 'user',//命名路由
component: User
}
]
})
命名视图
有时候想同时 (同级) 展示多个视图,而不是嵌套展示。例如一个布局有 sidebar
和 main
两个视图,而你不想嵌套,那么你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view
没有设置名字,那么默认为 default
。
<router-view></router-view>
<router-view name="a"></router-view>
<router-view name="b"></router-view>
一个视图使用一个组件渲染。因此对于同个路由,多个视图就需要多个组件。注意此时要写成components
:
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}
]
})
HTML5 History模式
vue-router
默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。我们可以让vue-router使用这种HTML5 history API,只需要改变路由的模式
const router=new VueRouter({
mode:'history',
routes:[...]
})
当你使用 history 模式时,URL 就像正常的 url,例如
http://yoursite.com/user/id
。不过还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 不存在的url 就会返回 404,这就不好看了。所以需要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个index.html
页面,这个页面就是你 app 依赖的页面。
动态路由
基础应用
动态路由的使用场景很多。比如你想匹配用户的ID作为路径的一部分,就可以用到动态路由,动态路由用 :
标记:
const router = new VueRouter({
routes: [
{
path: '/user/:id',
component: PageUser
}
]
})
路由参数
在组件实例中,可以通过使用属性this.$route
来获取当前的路由对象。其中 this.$route.params
可以访问到动态路由参数。
//访问:`user/1234`:
{
"id":'1234'
}
//如果有多个动态参数如/user/:Id/posts/:pageNumber
//访问:/user/1234/posts/2
{
"id":"1234",
"pageNumber":"2"
}
响应路由变化
当从 /user/foo 和 /user/bar相互切换时,原来的组件实例会被复用。意味着组件的生命周期钩子不会再被调用。如果想对路由参数的变化作出响应的话:
-
使用
watch
监测$route 对象:const PageUser = { template: '...', watch: { $route(to, from) { // 对路由变化作出响应... } } }
-
使用
beforeRouteUpdate
导航守卫const PageUser = { template: '...', beforeRouteUpdate (to, from, next) { // 对路由变化作出响应... next() } }
路由传参
在组件中使用 $route
会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。
原本:
const User = {
template: '<p>user:{{this.$route.params.id}}</p>',
}
const router = new VueRouter({
routes: [
{
path: '/user/:id',
component: User
}
]
})
现在:
const User = {
props:['id']
template: '<p>user:{{id}}</p>',
}
//在路由中指定props为true
const router = new VueRouter({
routes: [
{
path: '/user/:id',
component: User,
props:true
},
//对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:
{
path: '/user/:id',
components: { default: User, sidebar: Sidebar },
props: { default: true, sidebar: false }
}
]
})
导航
锚点导航
<!--一个动态跳转路由的例子-->
<li v-for="(item,index) in phoneList" :key="index">
<a target="_blank" :href="'/#/product/'+item.id"></a>
</li>
链接导航
在起步中已经用到过:<router-link>
-
通过传入
to
属性指定链接 -
默认会被渲染成一个
<a>
标签 -
通过
tag
属性改变默认标签<router-link to="/foo" tag="li">Go to Foo</router-link>
-
优化:可以在
<router-link>
里面加上锚点标签<router-link to="/foo"><a>Go to Foo</a></router-link> //这样就可以使用原生浏览器行为
-
原生事件
<router-link to="/foo" @click.native="handleclick">Go to Foo</router-link>
编程式导航
我们还可以借助 router 的实例方法实现路由跳转:
-
router.push()
router.push({ path: 'home' })// ===> /home const userId = '123' router.push({ path: `/user/${userId}` }) // ===> /user/123 //当路由有名字时 router.push({ name: 'user'}) // ===> /user
这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。
-
router.replace()
不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。
-
router.go(n)
//类似 window.history.go(n) // 在浏览器记录中前进一步,等同于 history.forward() router.go(1) // 后退一步记录,等同于 history.back() router.go(-1) // 前进 3 步记录 router.go(3)
导航守卫
导航守卫主要用来通过跳转或取消的方式来控制导航操作
全局前置守卫
router.beforeEach()
假设你想要限制未登录的用户访问你应用的某些部分,同时有一个userAuthenticated()
方法用于当用户登录时返回true。这个时候就可以用到导航守卫
该守卫有3个参数:to
、from
和next
。其中from和to分别表示导航从哪里来和到哪里去,next则是一个回调,在里面你可以让vue-router去处理导航、取消导航、重定向到其他地方或者注册一个错误。
const router = new VueRouter({ ... })
router.beforeEach((to,from,next)=>{
if(to.path.startsWidth('/account')&&!userAuthenticated()) next('/login');
else next()
})
//解析:如果被导向的路由的路径以/account开头,而用户还未登录,则该用户会被重定向到/login;
//否则,就调用next(),用户就能看到他们所请求的account页面
全局后置钩子
router.afterEach()
这个方法运行在导航之后。只被传入两个参数,to和from,因此不会影响导航
const router = new VueRouter({
routes: [
{
path: '/blog',
component:Blog,
meta:{
title:'welcome'
}
}
]
})
router.afterEach((to)=>{
document.title=to.meta.title
})
路由独享守卫
beforeEnter
const router = new VueRouter({
routes: [
{
path: '/blog',
component:Blog,
beforeEnter(to,from,next){
//...
}
}
]
})
组件内部守卫
你可以在路由组件内直接定义以下路由导航守卫:
beforeRouteEnter
等效于(beforeEach)beforeRouteUpdate
beforeRouteLeave
const Foo = {
template: `...`,
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 组件实例还没被创建,不能获取组件实例 `this`
next(vm=>{..})//可用过vm访问组件实例
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
//这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。
const answer = window.confirm('Do you really want to leave? ')
if (answer) {
next()
} else {
next(false)
}
}
}
完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用
beforeRouteLeave
守卫。 - 调用全局的
beforeEach
守卫。 - 在重用的组件里调用
beforeRouteUpdate
守卫 (2.2+)。 - 在路由配置里调用
beforeEnter
。 - 解析异步路由组件。
- 在被激活的组件里调用
beforeRouteEnter
。 - 调用全局的
beforeResolve
守卫 (2.5+)。 - 导航被确认。
- 调用全局的
afterEach
钩子。 - 触发 DOM 更新。
- 调用
beforeRouteEnter
守卫中传给next
的回调函数,创建好的组件实例会作为回调函数的参数传入
路由元信息
定义路由的时候可以配置 meta
字段。
如果你的网站拥有大量路由,那么一个个检查会很麻烦。这个时候路由元信息(route meta fields)就发挥作用了。现在你只需在需要检查的路由上添加一个meta,里面写一个requiresAuth:true
,这时守卫就能在那里获取它了。
const router = new VueRouter({
routes: [
{
path: '/account',
component:Account,
meta:{
requiresAuth:true
}
}
]
})
router.beforeEach((to,from,next)=>{
if(to.meta.requiresAuth&&!userAuthenticated()){
next('/login');
}else{
next()
}
})
当使用嵌套路由时,to.meta
指向的是子路由的元信息,而非其父路由。这个时候可以通过遍历to.matched
,它会包含父路由的元信息:
router.beforeEach((to,from,next)=>{
const requiresAuth=to.matched.some((record)=>{
return record.meta.requiresAuth
})
if(requiresAuth&&!userAuthenticated()){
next('/login');
}else{
next()
}
})
404页面
路由顺序:vue-router在内部通过遍历路由数组的方式来挑选被显示的路由,并选取其中匹配到当前URL的第一个。这意味着安排好路由的顺序很重要。
现在,可以利用vue-router会按顺序搜索路由直到与通配符(*)匹配的特点,来渲染一个显示错误页面:
const router = new VueRouter({
routes: [
//...你的其他路由
{
path: '*',
component:NotFound,//其他路由都匹配不到时,就会显示NotFound组件
}
]
})
在使用嵌套路由时,如果没有匹配到子路由,则路由器会继续往下对其父路由之外的路由列表进行搜寻。如果想让子路由的错误页面也能在父组件中显示,则需要在子路由数组中添加该通配符路由:
const router = new VueRouter({
routes: [
{
path:'settings',
component:Settings,
children:[
{
path:'address',
component:Address,
},
{
path: '*',
component:NotFound,//在子路由中添加
}
]
}
{
path: '*',
component:NotFound,
}
]
})