Vue-Router
文章目录
简介
路由就是通过互联的网络把信息从源地址传输到目的地址的活动
后端路由:后端处理URL和页面之间的映射关系
随着前后端分离阶段的出现,之后便到了单页面富应用阶段:
- 其实SPA最主要的特点就是在前后端分离的基础上加了一层前端路由
- 也就是前端来维护一套路有规则
当我们进入一个网站我们会把他的所有资源html+css+js全部资源下载下来,这时候我们使用前端路由 比如 url:自己的域名/home url:自己的域名/about
这时候我们是从所有资源里面抽取所需要的资源,在我们vue里面就是一个个组件
实质上前端路由就是url和组件的映射关系,抽取资源是从所有资源中抽取相映射的资源并渲染
准备阶段
URL的hash
实质上是对网址的参数拼接,是不会刷新页面请求资源的
可以在控制台中
location.hash = “home”
H5中的pushState
可以在控制台中
history.pushState({},’’,‘home’);
这里我们比较类似栈结构
history.pushState({},’’,‘me’);
当前的栈顶是me页面
输入history.back()我们会回到home页面
H5中的replaceState
history.replaceState({},’’,‘home’)
history.replaceState({},’’,‘about’)
这时候我们没有返回按钮
H5中的go
这个是跳到某个栈的作用
history.pushState({},’’,‘home’);
history.pushState({},’’,‘me’);
history.pushState({},’’,‘about’);
history.pushState({},’’,‘test’);
history.pushState({},’’,‘demo’);
这时候我们在demo页面输入
history.go(-3)
这时候栈跳出三个,我们到达了me页面
history.go(2)
这时候我们给栈压入两个到达了test页面
所以history.back()相当于history.go(-1),history.forward()则等价于go(1)
认识配置
目前前端流行的三大框架,都有自己的路由实现:
- Angular的ngRouter
- React的ReactRouter
- Vue的vue-router
安装
①npm install vue-router --save
②在模块化工程中使用它(因为是一个插件,所以可以通过Vue.use()来安装路由功能)
- 导入路由对象,并调用Vue.use(VueRouter)
- 创建路由实例,并且传入路由映射配置
- 在Vue实例中挂载创建的路由实例
搭建路由框架
以下是我们router文件夹中index.js路由相关的配置
//配置路由信息
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
// 1. 调用Vue.use来安装这个vue的插件,前提是引入了Vue(第三行)
Vue.use(VueRouter)
// 这是我们配置url映射关系的地方
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
// 2.创建VueRouter对象
const router = new VueRouter({
routes
})
// 3.将router对象传入到Vue实例
export default router
配置路由的映射关系
- 创建路由组件
- 配置路由映射:组件和路径映射关系
在router文件夹中的index.js文件中的配置关系
//导入组件
import Home from "../components/Home"
import About from "../components/About"
const routes = [
{
path:'/home',
component:Home
},
{
path:'/about',
component:About
}
]
- 使用路由:通过和
在app.vue文件中
<template>
<div id="app">
<router-view></router-view>
<router-link to="/home">首页</router-link>
<router-link to="/about">关于</router-link>
</div>
</template>
路由的默认路径
- 即一打开网页就到某个组件处
这里我们同样需要在router里面定义,
const routes = [
{
path:'/',
// redirect重定向
redirect:'/home'
}
{
path:'/home',
component:Home
},
{
path:'/about',
component:About
}
]
修改哈希模式
在我们更改路径时会有“#”的出现,只要我们将模式从哈希更改为history模式即可
在创建VueRouter对象时候设置
// 2.创建VueRouter对象
const router = new VueRouter({
routes,
mode:'history'
})
使用
<router - link>
- tag属性
默认情况下,我们这个标签会渲染成a标签,但是我们可以利用它的tag属性改变它的标签
tag="button"这时候就会变成一个按钮
<router-link to="/home" tag="button">首页</router-link>
- replace
有时候我们不希望后退和前进按钮的出现,就利用replace标签
- active-class属性
修改被点击时候的默认名,在结合CSS使用时很方便
active-class = “actived”
<router-link to="/home" tag="button" active-class="actived">首页</router-link>
<router-link to="/home" tag="button" active-class="actived">关于</router-link>
有时候我们不希望大量标签都要写上active-class,所以我们可以在index.js中修改被点击后的组件默认的名称
const router = new VueRouter({
routes,
mode:'history',
linkActiveClass:'actived'
})
代码实现路由
<template>
<div id="app">
<router-view></router-view>
<!-- <router-link to="/home" tag="button" replace>首页</router-link>
<router-link to="/about" tag="li" replace>关于</router-link> -->
<button @click="homeBtn">首页</button>
<button @click="aboutBtn">关于</button>
</div>
</template>
<script>
export default{
name:'App',
methods: {
homeBtn(){
//这里的router就是index.js文件中的变量名const router
//this.$router.push('/home')有返回按键的
this.$router.replace('/home').catch(err=>[])
console.log("home")
},
aboutBtn(){
this.$router.replace('/about').catch(err=>[])
console.log("about")
},
}
}
</script>
动态路由
有时候我们需要加载用户界面,我们是需要根据用户id来跳转的
-
首先我们需要在index.js中配置用户
-
- 变量名 是规定的
{
path:'/user/:userId',
component:User
},
data中的数据
data(){
return{
userId:"Joseph"
}
模板中我们要使用v-bind来使用变量
<template>
<div id="app">
<router-link v-bind:to="/user/+userId" tag="button" replace>我的</router-link>
<router-view></router-view>
</div>
</template>
我们从父组件里面获取数据在子组件user中进行展示
User.vue文件中我们进行展示
<template>
<div>
<h2>我是用户</h2>
<p>我是用户的相关信息</p>
<h3>{{userId}}</h3>
<h3>{{$route.params.userId}}</h3>
</div>
</template>
<script>
export default {
name:"User",
computed: {
userId(){
// 这里拿到的route是路由表中处于活跃状态的路由
// 活跃状态就是指你当前显示(使用)的哪个组件
return this.$route.params.userId
}
}
}
</script>
$router 和 $route不一样
前者是我们获取我们的VueRouter,后者是我们获取当前活跃状态的路由
路由的懒加载
由于我们的bundle.js的资源过大(因为各种打包底层代码转换都在里面),我们并不希望一打开加载资源要很很久,即有一个页面 空白期
- 所以如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了
实质:将不同的路由打包到不同的JS文件即可
写法我们只需要在index.js中的路由表修改写法即可
const routes = [
{
path:'/',
redirect: '/home'
},
{
path:'/user/:userId',
//新写法
component:()=>import('../components/User')
},
{
path:'/home',
component:()=>import('../components/Home')
},
{
path:'/about',
component:()=>import('../components/About')
}
]
或者说我们一开始定义变量为一个导入的语句
const Home = ()=> import('../components/Home');
const About = ()=> import('../components/About');
const User = ()=> import('../components/User');
const routes = [
{
path:'/',
redirect: '/home'
},
{
path:'/user/:userId',
component:User
},
{
path:'/home',
component:Home
},
{
path:'/about',
component:About
}
]
路由的嵌套使用
- 比如我们在home/news和home/message访问一些内容
- 一个路径映射一个组件,访问这两个路径也会分别渲染两个组件
实现嵌套路由有两个步骤:
- 创建对应的子组件,并且在路由映射中配置对应的子路由
这里我们创建了HomeNews和HomeMessage两个vue组件,引入方式如下
const HomeNews = ()=> import('../components/HomeNews');
const HomeMessage = ()=> import('../components/HomeMessage');
//我们在路由表中的Home的childern进行添加
{
path:'/home',
component:Home,
childern:[
{
path:'news',
component:HomeNews
},
{
path:'message',
component:HomeMessage
}
]
}
注意:这里的子路径path前面不用加 “ / ”,因为是相对路径
- 在组件内部使用标签
这里我们就需要在home组件的模板中添加标签
Home.vue文件中的模板
<template>
<div>
<h2>我是首页</h2>
<p>
我是首页内容
</p>
<router-link to="/home/news" tag="button">新闻</router-link>
<router-link to="/home/message" tag="button">消息</router-link>
<router-view></router-view>
</div>
</template>
注意:这里的to我们要用绝对路径
同样,我们给其一个进入页面的默认路径
children:[
{
path:'',
redirect: 'news'
},
...
]
参数传递
- 在动态路由时,我们用到了$route.params.userId,这是其中一直传递参数的方式
我们传递参数主要有两种类型 :params和quert
-
params的类型
- 配置路由格式: /router/:id
- 传递的方式:在path后面跟上对应的值(router-link标签里面to属性的字符串拼接)
- 传递后形成的路径: /router/123 , /router/abc
-
query
- 配置路由格式:/router,也就是普通配置
- 传递的方式:对象中使用query的key作为传递方式
- 传递后形成的路径: /router?id=123, /router?id=abc
<router-link :to="{path:'/profile',query:{name:userId,age:19,height:1.70}}" tag="button" replace>档案</router-link>
http://localhost:8080/profile?name=Joseph&age=19&height=1.7
URL: 协议://主机:端口/路径?查询#片段
scheme://host:port/path?query#fragment
那么我们如何从query中取信息呢?
this.$route.query我们就可以取到这个对象
<template>
<div>
<h2>
我是Profile
</h2>
<span>
{{this.$route.query}}
</span>
</div>
</template>
<script>
export default {
name:'Profile'
}
</script>
导航守卫
- 有时候我们想要对组件来换跳转进行监听,按照以前的方法我们会采用生命周期,created,mouted,updated,当被创建,挂载和数据更新的时候执行的回调函数
- 现在的需求:点击组件,document.title会改变,我们可以在生命周期函数里面回调,但是这样会在每个组件中都写
所以我们利用的我们全局导航守卫
首先我们要给每个路由都定义一下元数据(描述数据的数据)meta
const routes = [
{
path:'/',
redirect: '/home',
meta:{
title:'首页'
}
},
{
path:'/user/:userId',
component:User,
meta:{
title:'用户'
}
},
{
path:'/home',
component:Home,
children:[
{
path:'',
redirect: 'news'
},
{
path:'news',
component:HomeNews
},
{
path:'message',
component:HomeMessage
}
],
meta:{
title:'首页'
}
},
{
path:'/about',
component:About,
meta:{
title:'关于'
}
},
{
path:'/profile',
component:Profile,
meta:{
title:'用户档案'
}
}
]
然后我们利用导航守卫
//这是VueRouter对象
const router = new VueRouter({
routes,
mode:'history',
linkActiveClass:'actived'
})
//前置钩子、守卫(需要主动调用next()函数)
router.beforeEach((to,from,next)=>{
//实现需求的代码
document.title = to.meta.title
//这个next()一定要写,不写的话router路由器中所有的组件都无法跳转
//调用该方法后,才能进入下一个钩子
next();
//有时候我们也不希望用户直接跳转,比如登录注册
//我们可以加一些条件判断,然后跳转到对应的路径
//比如if(){next('/login')}
})
注意
当我们进去第一个得到的首页标题是undefined,这是由于路由的嵌套造成的
我们打印to,发现里面有一个matched数组,matcher[0]的path是’/home’,matcher[1]的path是’/home/news’,所以我们要更改一下document.title = to.matched[0].meta.title
后置钩子、守卫
router.afterEach((to,from)=>{
})
以上我们用的都是全局守卫,除此之外还有
- 路由独享的守卫
const routes = [
{
path:'/user/:userId',
component:User,
meta:{
title:'用户'
},
beforeEnter:(to,from,next)=>{
...
next()
}
},
...
]
- 组件内的守卫(文档使用)
keep-alive
-
注意:当我们切换组件的时候,是没有东西保留下来的,所以每次切换组件都会调用他们生命周期函数中的create函数,也就是每次都是创建。
- 因此我们组件内部存在一个destoryed()这样一个周期函数
-
keep-alive是Vue内置的一个组件,可以使包含的组件保留状态,或避免重新渲染
-
router-view 也是一个组件(是new vue-router时候定义的),如果直接被包在keep-alive里面,所有路径匹配到的视图组件都会被缓存
<keep-alive>
<router-view></router-view>
</keep-alive>
现在需求:进入首页默认是显示新闻,先点击首页消息,然后点击用户,再回到首页的时候,看到的是首页消息
这个可以用我们之前提到的导航守卫,活跃状态函数来解决
在home.vue中
<template>
<div>
<h2>我是首页</h2>
<p>
我是首页内容
</p>
<router-link to="/home/news" tag="button">新闻</router-link>
<router-link to="/home/message" tag="button">消息</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name:"Home",
data(){
return{
path:'/home/news'
}
},
created () {
console.log("created")
},
destroyed () {
console.log("destory")
},
//下面这两个函数是在keep-alive情况下才是有效的
activated () {
console.log("HOME组件正处于活跃中");
this.$router.push(this.path).catch(err=>{})
//这里要.catch的原因是避免/home/news这个路径重复提交路由控制台报错
},
deactivated () {
console.log("HOME组件在缓存中休息中")
},
beforeRouteLeave(to,from,next){
console.log(this.$route.path);
this.path = this.$route.path
next();
}
}
</script>
注意:有时候我们并不希望app里面所有的组件都被缓存,比如档案一些我们是希望能够重新create,这时候我们有两个非常重要的属性
-
include - 字符串或正则表达,照顾匹配的组件会被缓存
-
exclude - 字符串或正则表达式,任何匹配的组件都不会被缓存
-
里面放的字符串是我们每个vue组件导出时候的name属性
<keep-alive exclude="Profile,User">
<router-view></router-view>
</keep-alive>