vue-router文档
vue-router API
vue-router原码
定义
- 前端路由:对于单页面应用程序来说,主要通过URL中的hash(#号)来实现不同页面之间的切换,同时,hash有一个特点:HTTP请求中不会包含hash相关的内容;所以,单页面程序中的页面跳转主要用hash实现;
- 后端路由:对于普通的网站,所有的超链接都是URL地址,所有的URL地址都对应服务器上对应的资源;
- 在单页面应用程序中,这种通过hash改变来切换页面的方式,称作前端路由(区别于后端路由);
在 vue 中使用 vue-router
步骤:
- 导入 vue-router 组件类库,当导入vue-router包之后,在 window 全局对象中,就有了一个 路由的构造函数,叫做 VueRouter,在 new 路由对象的时候,可以为 构造函数,传递一个配置对象:
- 使用 router-link 组件来导航,默认渲染为一个a 标签;
- 使用 router-view 组件来显示匹配到的组件,专门用来当作占位符的,由路由规则匹配到的组件,就会展示到这个 router-view 中去;
- 创建组件的模板对象;
- 创建一个路由 router 实例,通过 routers 属性来定义路由匹配规则,这个规则对象身上有两个必须的属性:
(1)属性1 : path, 表示监听哪个路由链接地址;
(2) 属性2 :component, 表示如果路由是前面匹配到的 path ,则展示 component 属性对应的那个组件,omponent 的属性值,必须是一个 组件的模板对象, 不能是组件的引用名称;
属性2也可以是:redirect,设置默认的路由地址 - 使用 router 将路由规则对象,注册到 vm 实例上,用来监听 URL 地址的变化,然后展示对应的组件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
<script src="./lib/vue-router-3.0.1.js"></script>
<style>
</style>
</head>
<body>
<div id="app">
<!-- router-link 默认渲染为一个a 标签 -->
<router-link to="/login" tag="span">登录</router-link>
<router-link to="/register">注册</router-link>
<!-- 这是 vue-router 提供的元素,用来当作占位符的,路由规则匹配到的组件,就会展示到这个 router-view 中去 -->
<router-view></router-view>
</div>
<script>
var login = {
template: '<h1>登录组件</h1>'
}
var register = {
template: '<h1>注册组件</h1>'
}
var routerObj = new VueRouter({
routes: [
// { path: '/', component: login },
{ path: '/', redirect: '/login' },
{ path: '/login', component: login },
{ path: '/register', component: register }
],
linkActiveClass: 'myactive'
})
var vm = new Vue({
el: '#app',
router: routerObj
});
</script>
</body>
</html>
获取路由参数
- $route:获取当前路由的对象合属性
$route.path
字符串,等于当前路由对象的路径,会被解析为绝对路径,如 “/login/id=12&name=ls” 。$route.params
对象,包含路由中的动态片段和全匹配片段的键值对{id: “12”, name: “ls”}$route.query
对象包含路由中查询参数的键值对。例如,对于/login/id=12&name=ls,会得到$route.query.name == ‘ls’ 。$route.matched
数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象。$route.name
当前路径的名字,如果没有使用具名路径,则名字为空。
- 传参的方式
- 使用查询字符串,给路由传递参数,直接在链接?后面传参
<router-link to="/login?id=10&name=zs">登录</router-link>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
<script src="./lib/vue-router-3.0.1.js"></script>
</head>
<body>
<div id="app">
<router-link to="/login?id=10&name=zs">登录</router-link>
<router-link to="/register">注册</router-link>
<router-view></router-view>
</div>
<script>
var login = {
template: '<h1>登录 --- {{ $route.query.id }} --- {{ $route.query.name }}</h1>',
data(){
return {
msg: '123'
}
},
created(){
console.log(this.$route)
// console.log(this.$route.query.id)
}
}
var register = {
template: '<h1>注册</h1>'
}
var router = new VueRouter({
routes: [
{ path: '/login', component: login },
{ path: '/register', component: register }
]
})
var vm = new Vue({
el: '#app',
// router: router
router
});
</script>
</body>
</html>
配置路由
- 在路由中定义参数
- path:定义路由的地址,后可接
/:+参数名
来定义路由的参数 - component:路由的页面
- name:定义路由的名字
var router = new VueRouter({
routes: [
{ path: '/login/:id/:name', component: Login, name: 'login' },
{ path: '/register', component: register }
{ path: '/home/goodscomment/:id', component: GoodsComment, name: 'goodscomment' }
]
})
- 跳转路由的方法
-
1.html界面中定义:按规定在链接/后面传参
<router-link to="/login/12/ls">登录</router-link>
-
2.在js中使用路由方法调准路由
方法1:this.$router.push('/login/12/ls');
方法2:
this.$router.push({ name: "login", params: { id } });
注意:此处的id为变量,如果是第二个参数,用逗号隔开
- 案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="./lib/vue-2.4.0.js"></script>
<script src="./lib/vue-router-3.0.1.js"></script>
</head>
<body>
<div id="app">
<router-link to="/login/12/ls">登录</router-link>
<router-link to="/register">注册</router-link>
<router-view></router-view>
</div>
<script>
var login = {
template: '<h1>登录 --- {{ $route.params.id }} --- {{ $route.params.name }}</h1>',
created(){ // 组件的生命周期钩子函数
console.log(this.$route.params.id)
}
}
var register = {
template: '<h1>注册</h1>'
}
var router = new VueRouter({
routes: [
{ path: '/login/:id/:name', component: login },
{ path: '/register', component: register }
]
})
var vm = new Vue({
el: '#app',
data: {},
methods: {},
router
});
</script>
</body>
</html>
使用 children 属性实现路由嵌套
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
<script src="./lib/vue-router-3.0.1.js"></script>
</head>
<body>
<div id="app">
<router-link to="/account">Account</router-link>
<router-view></router-view>
</div>
<template id="tmpl">
<div>
<h1>这是 Account 组件</h1>
<router-link to="/account/login">登录</router-link>
<router-link to="/account/register">注册</router-link>
<router-view></router-view>
</div>
</template>
<script>
var account = {
template: '#tmpl'
}
var login = {
template: '<h3>登录</h3>'
}
var register = {
template: '<h3>注册</h3>'
}
var router = new VueRouter({
routes: [
{
path: '/account',
component: account,
// 使用 children 属性,实现子路由,同时,子路由的 path 前面,不要带 / ,否则永远以根路径开始请求,这样不方便我们用户去理解URL地址
children: [
{ path: 'login', component: login },
{ path: 'register', component: register }
]
}
]
})
var vm = new Vue({
el: '#app',
router
});
</script>
</body>
</html>
一个路由地址放置多个组件
案例:命名视图实现经典布局
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
<script src="./lib/vue-router-3.0.1.js"></script>
<style>
html,
body {
margin: 0;
padding: 0;
}
.header {
background-color: orange;
height: 80px;
}
h1 {
margin: 0;
padding: 0;
font-size: 16px;
}
.container {
display: flex;
height: 600px;
}
.left {
background-color: lightgreen;
flex: 2;
}
.main {
background-color: lightpink;
flex: 8;
}
</style>
</head>
<body>
<div id="app">
<router-view></router-view>
<div class="container">
<router-view name="left"></router-view>
<router-view name="main"></router-view>
</div>
</div>
<script>
var header = {
template: '<h1 class="header">Header头部区域</h1>'
}
var leftBox = {
template: '<h1 class="left">Left侧边栏区域</h1>'
}
var mainBox = {
template: '<h1 class="main">mainBox主体区域</h1>'
}
var router = new VueRouter({
routes: [
/* { path: '/', component: header },
{ path: '/left', component: leftBox },
{ path: '/main', component: mainBox } */
{
// components,带s,后面跟对象,跟着多个组件
path: '/', components: {
'default': header,
'left': leftBox,
'main': mainBox
}
}
]
})
var vm = new Vue({
el: '#app',
router
});
</script>
</body>
</html>
linkActiveClass
router-link有一个被选中的默认类样式,为linkActiveClass,若要修改被选中的链接路由样式,则修改此类样式即可,也可以给路由自定义一个linkActiveClass。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
<script src="./lib/vue-router-3.0.1.js"></script>
<style>
/*2.设置选中样式*/
.router-link-active,
.myactive {
color: red;
font-size: 80px;
}
</style>
</head>
<body>
<div id="app">
<router-link to="/login" tag="span">登录</router-link>
<router-link to="/register">注册</router-link>
<router-view></router-view>
</div>
<script>
var login = {
template: '<h1>登录组件</h1>'
}
var register = {
template: '<h1>注册组件</h1>'
}
var routerObj = new VueRouter({
routes: [
{ path: '/', redirect: '/login' },
{ path: '/login', component: login },
{ path: '/register', component: register }
],
linkActiveClass: 'myactive'//1.设置路由被被选中的类样式名
})
var vm = new Vue({
el: '#app',
router: routerObj
});
</script>
</body>
</html>
动态路由匹配
我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果:
router/index.js
{
path: '/course/:name',
component: () => import('../views/Detail.vue')
}
About.vue
<router-link :to="`/course/${c.name}`">
{{ c.name }} - {{ c.price | currency('¥') }}
</router-link>
通配符
适合做404页面路由
{
// 会匹配所有路径
path: '*',
component: () => import('../views/404.vue')
}
路由守卫
-
全局守卫
vue-router
提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。router.beforeEach((to, from, next) => { // ... // to: Route: 即将要进入的目标 路由对象 // from: Route: 当前导航正要离开的路由 // next: Function: 一定要调用该方法来 resolve 这个钩子。 })
登录例子:
//全局守卫定义 router.beforeEach((to, from, next) => { if (to.meta.auth) { if (window.isLogin) { next() } else { next('/login?redirect='+to.fullPath) //回到登录页,且保存登录后要跳转的页面 //fullPath:完成解析后的 URL,包含查询参数和 hash 的完整路径。 } } else { next() } })
//路由配置 { path: '/about', meta: { auth: true //meta.auth = true 表示该路由需要登录 } }, { path: '/login', component: () => import('../views/Login.vue') },
<template> <div> <button @click="login" v-if="!isLogin">登录</button> <button @click="logout" v-else>登出</button> </div> </template> <script> export default { methods: { login() { window.isLogin = true this.$router.push(this.$route.query.redirect) //登录成功跳转redirect的地址 }, logout() { window.isLogin = false } }, computed: { isLogin() { return window.isLogin } }, } </script>
注:
$route.query
表示 URL 查询参数。例如,对于路径 /foo?user=1,则有 $route.query.user == 1,如果没有查询参数,则是个空对象。 -
局部守卫
可以路由配置上直接定义 beforeEnter 守卫:{ path: '/about', name: 'about', // ... beforeEnter(to, from, next) { if (to.meta.auth) { if (window.isLogin) { next() } else { next('/login?redirect=' + to.fullPath) } } else { next() } } },
-
组件内守卫
(1)可以在路由组件内直接定义以下路由导航守卫:
beforeRouteEnter
:进入路由之前,组件还没渲染
beforeRouteUpdate
:参数发生变化的时候
beforeRouteLeave
:离开路由// About.vue beforeRouteEnter(to, from, next) { if (window.isLogin) { next(); } else { next("/login?redirect=" + to.fullPath); }
(2)路由导航前的数据获取时机
// 组件未渲染,通过给next传递回调访问组件实例 beforeRouteEnter (to, from, next) { getPost(to.params.id, post => { //getPost 获取数据的方法 next(vm => vm.setData(post)) //next可以传回调函数,回调函数的形参是当前组件的实例,调取实例setData的方法去存储获取的异步数据 }) }, // 组件已渲染,参数切换的情况,可以访问this直接赋值 beforeRouteUpdate (to, from, next) { this.post = null //数据置空 getPost(to.params.id, post => { //重新获取数据 this.setData(post) next() }) },
(3)路由导航后的数据获取时机
created () { this.fetchData() //正常在created中获取 }, watch: { '$route': 'fetchData' }
路由的动态配置
通过router.addRoutes(routes)
方式动态添加路由
用户登录后根据用户的登录信息为他动态的添加路由,没有添加的路由就跳转到404页面
// 全局守卫修改为:要求用户必须登录,否则只能去登录页
router.beforeEach((to, from, next) => {
if (window.isLogin) {
if (to.path === '/login') {
next('/')
} else {
next()
}
} else {
if (to.path === '/login') {
next()
} else {
next('/login?redirect=' + to.fullPath)
}
}
})
// Login.vue用户登录成功后动态添加/about
login() {
window.isLogin = true;
this.$router.addRoutes([
{
path: "/about", //...
}
]);
const redirect = this.$route.query.redirect || "/";
this.$router.push(redirect);
}
keepalive
路由组件缓存
利用keepalive做组件缓存,保留组件状态,提高执行效率
参数:
include
- 字符串或正则表达式。只有名称匹配的组件会被缓存。
exclude
- 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
max
- 数字。最多可以缓存多少组件实例。最多缓存的组件实例,最老的缓存出去,最新的缓存进来。而不是缓存10个后不能缓存,保证资源的合理利用
把切换需要缓存的router-view
用keep-alive
包裹起来,其中可以用include
指定切换到哪些路由时才需要缓存,也可以exculde
排除除这些组件外要缓存的对象,如果没有设置include
或exculde
则全部缓存。
范例:缓存about组件
<template>
<div >
<keep-alive include="about" max="10">
<router-view></router-view>
</keep-alive>
</div>
</template>
<script>
export default {
name:about,
data() {
return {
}
},
activated(){
},
deactivated(){
}
}
</script>
注意:
1、使用include
或exclude
时要给组件设置name
,这里的name不是路由配置的name
,而是组件的name
。
<script>
export default{
name:"app",
data(){
return{
}
}
}
</script>
2、缓存后的路由不再重新执行created
、mounted
等生命周期,这时候要用到两个特别的生命周期:激活状态activated
、取消激活状态deactivated
<script>
export default{
activated(){
},
deactivated(){
}
}
</script>
在webpack中使用vue-router
-
运行
npm i vue-router -D
安装包; -
导入模块
import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter)
-
创建.vue组件
app.vue<template> <div> <h1>这是 App 组件</h1> <router-link to="/account">Account</router-link> <router-link to="/goodslist">Goodslist</router-link> <router-view></router-view> </div> </template> <script> </script> <style> </style>
account.vue
<template> <div> <h1>这是 Account 组件</h1> </div> </template> <script> </script> <style> </style>
-
创建路由对象
在入口文件main.js中导入.vue组件,然后创建路由对象import App from './App.vue' import account from './main/Account.vue' var router = new VueRouter({ routes:[ {path:'/account',compoent:account} ] })
-
在main.js中将路由对象挂在对vue实例中去
var vm = new Vue({ el: '#app', render: c => c(login), router })
抽离路由模块
main.js
import Vue from 'vue'
// 1. 导入 vue-router 包
import VueRouter from 'vue-router'
// 2. 手动安装 VueRouter
Vue.use(VueRouter)
// 导入 app 组件
import app from './App.vue'
// 导入 自定义路由模块
import router from './router.js'
var vm = new Vue({
el: '#app',
render: c => c(app), // render 会把 el 指定的容器中,所有的内容都清空覆盖,所以 不要 把 路由的 router-view 和 router-link 直接写到 el 所控制的元素中
router // 4. 将路由对象挂载到 vm 上
})
// 注意: App 这个组件,是通过 VM 实例的 render 函数,渲染出来的, render 函数如果要渲染 组件, 渲染出来的组件,只能放到 el: '#app' 所指定的 元素中;
// Account 和 GoodsList 组件, 是通过 路由匹配监听到的,所以, 这两个组件,只能展示到 属于 路由的 <router-view></router-view> 中去;
router.js
import VueRouter from 'vue-router'
// 导入 Account 组件
import account from './main/Account.vue'
import goodslist from './main/GoodsList.vue'
// 导入Account的两个子组件
import login from './subcom/login.vue'
import register from './subcom/register.vue'
// 3. 创建路由对象
var router = new VueRouter({
routes: [
// account goodslist
{
path: '/account',
component: account,
name:'account',
children: [
{ path: 'login', component: login },
{ path: 'register', component: register }
]
},
{
path: '/goodslist',
name:'goodslist',
component: goodslist
},
{
path: '/about',
name:'about',
component: () => import('./main/about.vue') //路由懒加载
},
]
})
// 把路由对象暴露出去
export default router
路由懒加载
路由组件的懒加载能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
() => import("../views/About.vue")