Vue路由
用 Vue.js + Vue Router 创建单页应用
-
下载vue-router.js
vue-router3.x是针对Vue2.x版本
vue-router4.x是针对Vue3.x版本
-
引入在vue.js的后面
1. 基础路由配置
- 配置路由
// 定义路由组件
const index = { template: `<div>首页</div>` }
const list = { template: `<div>列表页</div>` }
const cart = { template: `<div>购物车</div>` }
// 定义路由表
const routes = [
{ path: '/' , redirect: '/index'},
{ path: '/index' , component: index },
{ path: '/list' , component: list },
{ path: '/cart' , component: cart },
]
//创建路由对象
const router = new VueRouter({
routes
})
// 注册路由
var vm = new Vue({
el: '#app',
data: {
},
methods: {},
router
});
-
路由出口
<router-view></router-view>
-
路由跳转(即路由导航)
<router-link to="/index"></router-link>
2. 嵌套路由
比如: 小米首页的推荐,智能、电视
{
path: '/index',
component: index,
redirect: '/index/recommend',
children: [
{
path: 'recommend',
component: recommend
},
{
name: 'ai',
path: 'ai',
component: ai
},
{
path: 'tv',
component: tv
}
]
},
路由出口在父组件index中
<router-view></router-view>
路由导航
<router-link to="/index/recommend">推荐</router-link>
3. 动态路由
能够接收参数的路由称为动态路由
-
配置动态路由
const routes = [ { path: '/detail/:id/:type' , component: detail }, ]
-
路由跳转时传参(5为id值,car为type的值)
<router-link to="/detail/5/car"></router-link>
-
在detail组件中接收参数
let id = this.$route.params.id let type = this.$route.params.type
4. vue两种实现导航(跳转)的方式
-
声明式导航 ,用router-link实现
<router-link to="/list">列表页</router-link>
-
编程式导航,用this.$router的API实现
this.$router.push("/list") this.$router.go(-1) : 返回上一步 this.$router.replace("/list") : 跳转至下个页面,并替换当前页的跳转记录(go(-1)或在浏览器中单击后退按钮时会跳过上一级页面)
5. 命名路由
有时候,通过一个名称来标识一个路由显得更方便一些,特别是在链接一个路由,或者是执行一些跳转的时候。你可以在创建 Router 实例的时候,在 routes
配置中给某个路由设置名称。
const routes = [
{
path: '/user/:userId',
name: 'user', //命名路由,该名称在路由跳转及传参中使用
component: User
}
]
6. 路由传参方式汇总
- 声明式导航
<!-- 下面传参的方式,在跳转到的路由组件中,都是通过this.$route.query来接收, 路由不需要在router/index.js中配置参数 -->
<router-link :to="{path:'/list',query:{ id: 9} }">进入list页面</router-link>
<router-link :to="{path:'/list',query:{ id: 9,person:this.person} }">进入list页面</router-link>
<router-link to="/list?id=8">进入list页面</router-link>
<router-link to="/list?id=8&page=2">进入list页面</router-link>
<!-- name的值是在路由配置中用name属性定义的路由名称 -->
<router-link :to="{name:'list',query:{ id: 9} }">进入list页面</router-link>
<!-- 没有提前在路由中配置动态参数,直接用params传值,接收 this.$route.params.id -->
<router-link :to="{name: 'list',params: {id: 8}}">进入list页面</router-link>
<!-- 下方传参无效,path不能和params结合使用 -->
<router-link :to="{path: '/list',params: {id: 8}}">进入list页面</router-link>
-
编程式导航
// 编程式导航的传参方式和接收方式都和声明式导航相同 this.$router.push("/list") this.$router.push({ path: '/list' }) this.$router.push("/list/7") //无效,因为list没有配置动态参数 this.$router.push("/list?id=9") this.$router.push({ path: '/list', query: { id: 7, age: 100 } }) this.$router.push({ name: 'list', params: { id: 7, age: 100 } })
7. 了解命名视图
有时候想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar
(侧导航) 和 main
(主内容) 两个视图,这个时候命名视图就派上用场了。你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view
没有设置名字,那么默认为 default
。
<!--App.vue -->
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
一个视图使用一个组件渲染,因此对于同一个路由,多个视图就需要多个组件。确保正确使用 components
配置 (带上 s):
const routes = [
{
path: '/index',
components: {
default: Foo, //组件Foo渲染在默认视图
a: Bar, // 组伯Bar渲染在name='a'的视图中
b: Baz
}
},
{
// 没有配置多个components的情况下,只渲染在默认视图中
path: '/category',
component: Category
}
]
嵌套命名视图
我们也有可能使用命名视图创建嵌套视图的复杂布局。这时你也需要命名用到的嵌套 router-view
组件
const Index = {
template: `
<div class="index">
11111
<hr/>
<router-view></router-view>
<hr/>
222222
<router-view name="a"></router-view>
</div>
`
}
路由配置
const routes = [
{ path: '/', redirect: '/index' },
{
path: '/index',
component: Index,
children: [
{
path: 'child1',
components: {
default: IndexChild1, //嵌套命名视图组件
a: IndexChild3 // //嵌套命名视图组件
}
},
{
path: 'child2',
component: IndexChild2
},
]
},
{ path: '/category', component: Category },
{ path: '/discover', component: Discover },
{ path: '/cart', component: Cart },
{ path: '/user', component: User },
]
8. r o u t e 和 route和 route和router的区别(重要)
在一个vue组件中,都可以访问到这两个对象,它俩的区别 是:
-
this.$route是路由记录(信息)对象,只读,存储了与路由相关的信息
fullPath: "/index/recommend" matched: (2) [{…}, {…}] //存储了所有级别路由的信息 (如果有两个,则matched[0]是一级路由信息,matched[1]是嵌套路由信息) meta: {title: '首页-推荐'} name: "recommend" //在路由配置中给路由命的名字 params: {} path: "/index/recommend" query: {}
-
this.$router是路由对象,可以用来进行路由跳转
9. 路由的模式
-
路由模式常见的有hash和history
// 改变模式,添加mode选项,默认是hash const router = new VueRouter({ routes, mode: 'history' })
-
vue-router的hash和history的区别
hash: 地址栏中有#
history: 地址栏没有#,更好看一些
-
原理
hash: 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。
history: 如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。
********************但这种模式以后上线,需要后端在服务器上配置才能使用
10. 路由激活时类名的处理
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL, //路由基本站点根目录
routes,
// 定义链接激活时的类名
linkActiveClass: 'active'
})
效果: 当前激活状态的会自动添加active这个类名
11. 路由元信息
携带数据
-
定义路由元信息
const routes = [ { path: '/circle', component: circle }, { path: '/cart', component: cart, meta: { // Authorization 授权 auth: true } }, { name: 'my', path: '/my', component: my, // 路由元信息,可以在组件中通过this.$route.meta访问到这些信息 meta: { title: '个人信息', icon: 'icon-center', auth: true } }, { path: '/login', component: login } ]
12. 导航守卫
又称为路由守卫或路由拦截
重点学习全局前置守卫,其它了解即可
vue-router
提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。
全局路由守卫在路由配置文件中定义,并且必须在new VueRouter后定义
单个路由独享的守卫,也是在路由配置文件中定义,但是,是在某一个具体的路由定义中添加
组件级的路由守卫: 添加在某个组件内部
-
全局前置守卫
router.beforeEach((to,from,next)=>{ })
-
全局解析守卫
和 router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
router.beforeResolve((to,from,next)=>{
})
-
全局后置钩子
和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:
router.afterEach((to,from)=>{
})
-
路由独享的守卫(只有一个)
在路由配置上直接定义 beforeEnter 守卫
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
-
组件内的守卫
const Foo = { template: `...`, beforeRouteEnter(to, from, next) { // 在渲染该组件的对应路由被 confirm 前调用 // 不!能!获取组件实例 `this` // 因为当守卫执行前,组件实例还没被创建 // next的回调会在路由全部解析完毕并且dom更新后执行 // 创建好的组件实例会作为回调函数的参数传入。 next(vm=>{ console.log('beforeRouterEnter中next的回调',vm.msg) }) }, beforeRouteUpdate(to, from, next) { // 在当前路由改变,但是该组件被复用时调用 // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候, // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。 // 可以访问组件实例 `this` }, beforeRouteLeave(to, from, next) { // 导航离开该组件的对应路由时调用 // 可以访问组件实例 `this` } }
13. 完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用
beforeRouteLeave
守卫。 - 调用全局的
beforeEach
守卫。 - 在重用的组件里调用
beforeRouteUpdate
守卫 (2.2+)。 - 在路由配置里调用
beforeEnter
。 - 解析异步路由组件。
- 在被激活的组件里调用
beforeRouteEnter
。 - 调用全局的
beforeResolve
守卫 (2.5+)。 - 导航被确认。
- 调用全局的
afterEach
钩子。 - 触发 DOM 更新。
- 调用
beforeRouteEnter
守卫中传给next
的回调函数,创建好的组件实例会作为回调函数的参数传入。
14. 路由懒加载和项目打包
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
-
实现方式
不需要使用import提前引入路由组件
在路由配置中实现懒加载
{ path: '/index', // 路由懒加载 component: ()=> import ('@/views/index.vue'), //下面的方式可以给对应的代码块命名 component: ()=> import (/* webpackChunkName: "index-module" */ '@/views/index.vue'), }
-
项目打包
npm run build
如果没有路由懒加载,会打包成两个js
app.js: 存储自定义的业务逻辑代码
chunk-vendor.js: 存储第三方依赖包的代码 如: vue.js vue-router.js
路由懒加载之后, 懒加载的组件会生成独立的css和js, 并且在访问页面时,会按需加载
15. 404页面的配置
如果用户访问的路由在路由表中没有匹配路径,则应该显示404页面
实现方式: 在所有路由表的最后,添加以下面的配置
{
// *表示所有路由,如果在上面没有匹配到,就会执行该路由,显示404页面
// 这个配置必须放在所有路由的最下面
path: '*',
component: ()=> import ('@/views/NotFound.vue'),
}
16. 滚动行为
应用场景: 首页和分类页都有滚动效果,如果在首页中页面滚动200px,切换到分类页时,看不到顶部信息,因为页面还在200px偏移的位置
解决方案:
const router = new VueRouter({
routes,
//滚动行为,切换路由时页面滚动到顶部
scrollBehavior(){
return {
x: 0, y: 0
}
}
})