前言
路由(routing)就是通过互联的网络把信息从源地址传输到目的地址的活动
路由器提供了两种机制:路由和转送
- 路由是决定数据包从来源到目的地的路径
- 转送是将输入端的数据转移到合适的输出端
—前端渲染与后端渲染
后端路由阶段:由服务器处理 URL 和页面之间的映射关系
前后端分离阶段:后端只提供 API 来返回数据,前端通过 Ajax 获取数据,并且通过 JavaScript 将数据渲染到页面中
**
- 单页面应用(SPA)阶段:在前后端分离的基础上加了一层前端路由,由前端来维护一套路由规则
—前端路由方式
当 URL 发生改变时,不会向服务器重新请求资源(不刷新)
URL 的 hash
可以通过直接赋值 location.hash
来改变 href,但页面不会发生刷新
此时页面的 href 变为
HTML5 的 history
history 接口是 HTML5 新增的,它有五种模式改变 URL 而不刷新页面
- pushState
使用 history.pushState()
也可以改变 href
此时页面的 href 变为
- back
加载 history 列表中的前一个 URL(返回上一页)
- forward
加载 history 列表中的下一个 URL(前进下一页)
- replaceState
和 pushState 用法一样,区别在于 replaceState 不能返回页面
- go
加载 history 列表中的某个具体页面
history.go(-2)
后退2页
history.go(-1)
等同于 history.back()
history.go(1)
等同于 history.forward()
安装和使用
安装
npm install vue-router —save
配置
// src/router/index.js
import Vue from 'vue'
// 导入 vue-router
import Router from 'vue-router'
// 导入组件
import HelloWorld from '@/components/HelloWorld'
// 1.通过 vue.use() 传入路由插件
Vue.use(Router)
// 2.创建并导出路由对象
export default new Router({
// 配置路由和组件之间的映射关系
routes: [
{
path: '/home',
name: 'HelloWorld',
component: HelloWorld
}
]
})
// 3.将 router 对象挂载到 vue 实例
// src/main.js
import Vue from 'vue'
import App from './App'
// 导入 router
// 当不指定文件名时,会自动查询目录下的 index 文件
import router from './router'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
// 挂载到 vue 实例中
router,
render: h => h(App)
})
使用路由
- 标签方式
// src/App.vue
<template>
<div id="app">
<!-- 最终会渲染为 <a> 标签 to 属性用于指定跳转的路径 -->
<!-- 默认是使用 hash 改变 href -->
<router-link to="/home">首页</router-link>
<!-- 相当于一个占位符,决定渲染之后的位置 -->
<router-view></router-view>
</div>
</template>
改变 router-link 的模式
// src/router/index.js
export default new Router({
mode: 'history'
})
- 代码方式
export default { name: "App", methods: { homeClick() { // 不要使用 history,因为这样就绕过了 vue-roter // 同样也有 replace 方法 this.$router.push('/home') } };
route-link
- to 属性
**
用于指定跳转的路径
<router-link to="/home">首页</router-link>
- tag 属性
tag 属性的值用于指定渲染后的标签,默认为 标签
<!-- 默认为渲染为 a 标签 -->
<router-link to="/home">首页</router-link>
<router-link to="/about" tag="button">关于</router-link>
- replace 属性
replace 属性没有值,当指定了 replace 后,路由跳转将会使用 history.``replaceState()
的方式
<router-link to="/home" tag="button" replace>首页</router-link>
- 默认 class
**
当使用 router-link 渲染后的标签处于 active 状态时,Vue 会自动为标签添加 router-link-exact-active
和 router-link-active
class
要想改变默认 class 可以使用 active-class 属性
<router-link to="/home" active-class="active">首页</router-link>
但是给第一个标签都添加属性很麻烦,可以在 router 的配置文件中更改默认 class
// src/router/index.js
export default new Router({
linkActiveClass: 'active'
})
路由方法
router.push()
等同于<router-link :to="...">
router.replace()
跟router.push
很像,唯一的不同就是,它不会向 history 添加新记录router.go()
这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似window.history.go(n)
动态路由
我们有一个 User
组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router
的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果:**
const router = new Router({
routes: [
// 动态路径参数以冒号开头
{ path: '/user/:id', component: User }
]
})
使用 v-bink 在跳转链接中动态拼接用户 id
<template>
<div id="app">
<router-link v-bind:to="'/user/' + id" replace>用户</router-link>
<!-- 相当于一个占位符,决定渲染之后的位置 -->
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
userId: "lisi",
};
},
};
</script>
以上使用的是声名式,还可以使用编程式
const userId = '123'
router.push({ name: 'user', params: { userId }}) // -> /user/123
router.push({ path: `/user/${userId}` }) // -> /user/123
// 这里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user
当匹配到一个路由时,参数值会被设置到this.$route.param
中
<template>
<div>
<h2>我是用户界面</h2>
<p>我是用户的相关信息</p>
<h2>用户ID:{{ Id }}</h2>
</div>
</template>
<script>
export default {
name: "User",
computed: {
Id() {
// $route 拿到当前活跃的 route 对象
return this.$route.params.userId;
},
},
};
</script>
路由懒加载
路由懒加载的主要作用就是将路由对应的组件打包成一个个的 js 代码块,只有在这个路由被访问到的时候才加载对应的组件
使用
// src/router/index.js
// 使用箭头函数
const HelloWorld = () => import('../components/HelloWorld')
export default new Router({
routes: [
{
path: '/home',
name: 'HelloWorld',
component: HelloWorld
}
]
})
一个组件打包成一个 js 代码
路由的嵌套
创建对应的子组件,并在路由映射中配置对应的子路由
// src/router/index.js
const Home = () => import('../components/Home')
const HomeNews = () => import('../components/HomeNews')
export default new Router({
routes: [
{
path: '/home',
name: 'HelloWorld',
component: HelloWorld,
children: [
{
path: '/news',
component: HomeNews
}
]
}
]
})
在组件内部使用
<template>
<div>
<h2>我是首页</h2>
<p>我是首页内容</p>
<router-link to="/home/news">新闻</router-link>
<router-view></router-view>
</div>
</template>
参数传递
在“动态路由”中使用
$route.params
传递参数使用 query 传递参数
通过标签传递参数
<template>
<div id="app">
<h2>我是App组件</h2>
<router-link :to="{ path: '/home', query: { name: 'why', age: '18' } }">首页</router-link>
<router-view></router-view>
</div>
</template>
通过代码传递参数
<script>
export default {
name: "App",
methods: {
homeClick: {
this.$router.push({
path: '/home',
query: {
name: 'why',
age: 18
}
})
}
}
};
</script>
通过 $route.query
获取参数
<template>
<div>
<h2>我是用户界面</h2>
<p>我是用户的相关信息</p>
<h2>用户名:{{ $route.query.name }}</h2>
</div>
</template>
$router 和 $route
任何组件都继承自 Vue 类的原型,而 $router
是一个 Vue 中的一个属性(全局属性),不管在哪获取的都是路由文件里 new 出来的 router 对象
从构造函数的源码可以看到“前端路由方式”中的各种方法
在使用了 vue-router 的应用中,路由对象会被注入每个组件中,赋值为 this.$route
,并且当路由切换时,路由对象会被更新
可以看到 this.$route
对象中有 params 对象和 query 对象
导航守卫
监听路由之间的跳转,并实现一些功能
全局守卫
- 前置守卫
在路由跳转前做的一些操作
使用
// src/router/index.js
const router = new VueRouter({ ... })
// beforeEach 前置守卫
router.beforeEach((to, from, next) => {
// 从 from 跳转到 to 之前
document.title = to.matched[0].meta.title
console.log(to);
// next 必要要调用
next()
})
注意当一个组件使用了组件嵌套时,meta 属性中将不会有数据,而是会放到 matched 中
meta 数据可以在路由映射关系中定义
const routes = [
{
path: '/home',
name: 'HelloWorld',
component: Home,
meta: {
title: '首页'
}
}
]
- 后置勾子
在路由跳转后做的一些操作
使用
// src/router/index.js
const router = new VueRouter({ ... })
// afterEach 后置勾子
router.afterEach((to, from) => {
// 从 from 跳转到 to 之后
})
路由独享守卫
你可以在路由配置上直接定义 beforeEnter
守卫:
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
这些守卫与全局前置守卫的方法参数是一样的
组件内的守卫
你可以在路由组件内直接定义以下路由导航守卫:
beforeRouteEnter
beforeRouteUpdate
(2.2 新增)beforeRouteLeave
const Foo = {
template: `...`,
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
keep-alive
keep-alive
是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染
keep-alive
有三个属性:
include
- 字符串或正则表达式。只有名称匹配的组件会被缓存exclude
- 字符串或正则表达式。任何名称匹配的组件都不会被缓存max
- 数字。最多可以缓存多少组件实例
详细使用可以查看官网
生命周期函数 created()
也只会执行一次, destroyed()
函数一次也不会执行
activated()
函数和 deactivated()
函数只有组件被
- created() 组件被创建之后
- destroyed() 组件被销毁之后
- activated() 组件路由活跃时
deactivated() 离开组件路由时
示例
保持路由状态
<template>
<div id="app">
<!-- 只有避免渲染才能保持状态 -->
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>
</template>
<script>
export default {
name: "Home",
data() {
return {
// 用于记录状态
path: "/home/news",
};
},
// 路由活跃时
activated() {
this.$router.push(this.path);
},
// 离开组件路由之前
beforeRouteLeave(to, from, next) {
this.path = this.$route.path;
next();
},
};
</script>
属 性
- include
字符串或正则表达式,只有匹配的组件会被缓存
- exclude
字符串或正则表达式,任何匹配的组件都不会被缓存
<template>
<div id="app">
// exclude 值为组件的 name 属性,多个以逗号分开(不要加空格)
<keep-alive exclude="profile">
<router-view></router-view>
</keep-alive>
</div>
</template>
tab-bar 案例
1、把大致框架搭好(html、css)
2、提取成组件(子组件和父组件)
3、需要动态展示组件的地方使用插槽
4、配置 router
5、动态监听跳转链接使用父子通信(props)
6、通过 this.$route.path
可以判断该路由是否处于活跃状态