1. 认识路由
1.1 后端路由
- 早期的网站开发整个HTML页面是由服务器来渲染的.
- 服务器直接生产渲染好对应的HTML页面,返回给客户端进行展示
- 但是,一个网站,这么多页面服务器如何处理呢?
- 一个页面有自己对应的网址,也就是URL.
- URL会发送到服务器,服务器会通过正则对该URL进行匹配,并且最后交给一个Controller进行处理.
- Controller进行各种处理,最终生成HTML或者数据,返回给前端.
- 这就完成了一个IO操作.
- 上面的这种操作,就是后端路由.
- 当我们页面中需要请求不同的路径内容时,交给服务器来进行处理,服务器渲染好整个页面,并且将页面返回给客户顿
- 这种情况下渲染好的页面,不需要单独加载任何的js和css,可以直接交给浏览器展示,这样也有利于SEO的优化.
- 后端路由的缺点:
- 一种情况是整个页面的模块由后端人员来编写和维护的.
- 另一种情况是前端开发人员如果要开发页面,需要通过PHP和Java等语言来编写页面代码.
- 而且通常情况下HTML代码和数据以及对应的逻辑会混在一起,编写和维护都是非常糟糕的事情.
1.2 前端路由阶段
- 前后端分离阶段︰
- 随着Ajax的出现,有了前后端分离的开发模式.
- 后端只提供API来返回数据,前端通过Ajax获取数据,并且可以通过JavaScript将数据渲染到页面中
- 这样做最大的优点就是前后端责任的清晰,后端专注于数据上,前端专注于交互和可视化上.
- 并且当移动端(iOS/Android)出现后,后端不需要进行任何处理,依然使用之前的一套API即可
- 目前很多的网站依然采用这种模式开发.
- 单页面富应用阶段:
- 其实SPA最主要的特点就是在前后端分离的基础上加了一层前端路由
- 也就是前端来维护一套路由规则
- 前端路由的核心是什么呢?
- 改变URL,但是页面否进行整体的刷新。
- 如何实现呢?
2. vue-router的基本使用
2.1 下载vue-router
打开控制台,在当前文件路径下输入命令
npm install vue-router --save
2.2 安装和配置vue-router
-
在
src
文件夹下创建一个新的文件夹router
,再创建一个router.js
文件 -
在
router.js
文件中导入路由对象,并且调用Vue.use(VueRouter)
(因为是一个插件,所以可以通过Vue.use()
来安装路由功能)// 1. 配置路由相关信息 import VueRouter from 'vue-router' import Vue from 'vue' // 2. 安装路由插件 // 所有插件在使用时都要用到,Vue.use() Vue.use(VueRouter);
-
创建路由实例,并且传入路由映射配置
// 3. 创建VueRouter对象 const routes = [ // 配置路由和组件间的映射关系 ]; const router = new VueRouter({ // 配置路由和组件之间的应用关系 routes }); // 4. 将路由实例对象倒出 export default router
-
在Vue实例(
main.js
)中挂载创建的路由实例import router from './router' new Vue({ render: h => h(App), router, }).$mount('#app')
2.3 使用vue-router
-
创建路由组件
- 在
components
文件夹下创建.vue
文件 - 我们创建两个文件
Home.vue
和About.vue
- 在
-
配置路由映射:组件和路径映射关系
// 3. 创建VueRouter对象 const routes = [ // 配置路由和组件间的映射关系 { path:'/home', component: Home }, { path:'/about', component: About } ];
-
使用路由:在
App.vue
中通过<router-link>
和<router-view>
<template> <div id="app"> <router-link to="/home">首页</router-link> <router-link to="/about">关于</router-link> <!-- <router-view>相当于一个占位符,切换到对应路由组件页面就会显示对应的内容 --> <router-view></router-view> </div> </template>
2.3.1 <router-link>
补充
-
<router-link>
的其他属性 -
tag
:tag
可以指定<router-link>
之后渲染成什么组件,比如下面的代码会被渲染成一个<button>
元素,而不是<a>
<router-link to="/home" tag='button'>首页</router-link>
-
replace
:replace
不会留下history
记录,所以指定replace的情况下,后退键返回不能返回到上一个页面中 -
active-class
: 当<router-link>
对应的路由匹配成功时,会自动给当前元素设置一个router-link-active
的class,设置active-class
可以修改默认的名称。<router-link to="/home" active-class="active">首页</router-link>
- 在进行高亮显示的导航菜单或者底部tabbar时,会使用到该类.
- 但是通常不会修改类的属性,会直接使用默认的router-link-active即可
2.4 设置路由默认路径
默认情况下,进入网站的首页,我们希望<router-view>
渲染首页的内容。但是我们的实现中,默认没有显示首页组件,必须让用户点击才可以。如何可以让路径默认跳到到首页,并且<router-view>
渲染首页组件呢?非常简单,我们只需要配置多配置一个映射就可以了。
const routes=[
{
path: '',
redirect: '/home'
}
]
2.5 路由跳转
-
通过js代码实现路由跳转
methods:{ btnClick(){ this.$router.push('/home'); // 使用replace的情况下,后退键返回不能返回到上一个页面中 this.$router.replace('/home') } }
2.6 动态路由的使用
我们希望在路由地址后加上id名,类似于一个按钮谁点击了地址后就加上谁的名字。
-
首先需要改变路由入口文件
router.js
,在要添加id的映射path
上加上/:id
const routes = [ { path:'/user/:id', component: User } ];
-
第二步,在
<router-link>
上通过v-bind=""
动态绑定路由地址,不需要改变的地方写在单引号里,表示为固定的字符串<router-link v-bind:to="'/user/'+userId">用户</router-link>
data(){ return{ userId: 'lisi' } },
2.6.1 获取id并显示
可以通过this.$route.params.参数名
获取路由地址上的参数
.params
后的参数名要跟router.js
里的routes
里的path
路径写的一直,上面写的是id
,那么后面就.id
export default {
name: "User",
computed:{
userId(){
return this.$route.params.id;
}
}
}
显示
<template>
<div id="user">
<p>{{userId}}</p>
</div>
</template>
2.6.2 r o u t e 和 route和 route和router的区别
$router
为VueRouter实例,可以通过this.$router
拿到VueRouter实例里定义好的方法。想要导航到不同URL,则使用$router.push
方法$route
为当前router
跳转对象,可以获取当前路由对象的name、path、query、params
等属性值
2.7 路由懒加载
2.7.1 路由懒加载
-
官方给出了解释:
- 当打包构建应用时,Javascript包会变得非常大,影响页面加载。
- 如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了
-
官方在说什么呢?
- 首先,我们知道路由中通常会定义很多不同的页面
- 这个页面最后被打包在哪里呢?—般情况下,是放在一个js文件中
- 但是,页面这么多放在一个js文件中,必然会造成这个页面非常的大
- 如果我们一次性从服务器请求下来这个页面,可能需要花费一定的时间,甚至用户的电脑上还出现了短暂空白的情况
- 如何避免这种情况呢?使用路由懒加载就可以了
2.7.2 懒加载的方式
-
方式一:结合vue的异步组件和webpack的代码分析
const Home = resolve => {require.ensure(['../components/Home.vue'],()=>{resolve(require('../components/Home.vue'))})};
-
方式二:AMD写法
const About = resolve => require(['../components/About.vue'], resolve);
-
方式三:在ES6中,我们可以有更加简单的写法来组织Vue异步组件和Webpack的代码分割
- 没有使用懒加载时我们是直接在路由入口文件中引入组件,现在我们定义一个变量指向引用组件的箭头函数,实现懒加载
const Home = () => import('../components/Home.vue')
3. vue-router嵌套路由
在组件里嵌套其他组件
-
路由入口文件
router.js
中const HomeNews = () => import('../components/HomeNews.vue') const News = () => import('../components/News.vue') const routes = [ { path: '/home', component: Home, children: [ { path: '', redirect: 'homenews' }, { path: 'homenews', component: HomeNews }, { path: 'news', component: News } ] } ]
-
在父组件
Home.vue
中<template> <div id="home"> <router-link to="/home/homenews">首页新闻</router-link> <router-link to="/home/news">新闻</router-link> <router-view></router-view> </div> </template>
4. vue-router参数传递
4.1 参数传递的方式
-
传递参数主要有两种类型:
params
和query
-
params
- 配置路由格式:
/router/:id
- 传递的方式:
在path后面跟上对应的值
- 传递后形成的路径:
/router/123
,/router/abc
const routes = [ { path: '/home/:id', component: Home } ]
<router-link :to="'/home'+homeId">首页</router-link> <script> export default{ name: "Home", data(){ rerurn{ homeId: lili } } } </script>
获取id
<p>{{$route.params.id}}</p>
- 配置路由格式:
-
query
- 配置路由格式:
/router
, 也就是普通配置 - 传递的方式: 对象中使用
query
的key
作为传递方式 - 传递后形成的路径:
/router?id=123
,/router?id=abc
<router-link :to="{path: '/home', query: {name:'lili', age: 18, height: 158}}">首页</router-link>
- 获取
<p> {{$route.query.name}} {{$route.query.age}} {{$route.query.height}} </p>
- 配置路由格式:
-
补充:URL
URL:
协议://主机:端口/路径?查询
scheme://host:port/path?query#fragment
4.2 js代码数据传递方式
在不使用<router-link>
的情况下也能进行页面跳转并且传递数据
-
例如,当我们点击一个按钮的时候也能进行页面跳转并且传递数据
<template> <div> <button @click="homeClick">首页</button> </div> </template>
methods:{ homeClick(){ // 1.传递一个参数 this.$router.push('/home/' + this.homeId); // 2. 传递多个参数 this.$router.push({ path: '/home', query:{ name: 'lili', age: 17, height: 167 } }) } }
5. vue-router导航守卫
对路由跳转的过程进行监听,使用监听函数实现一些要求
-
示例:在跳转到不同页面时,我们希望网页标题也会发生改变
- 在
router.js
文件中操作
// 1. 首先给每一个组件页面设置一个title const routes = [ { path: '/home', component: Home, meta:{ title: '首页' } }, { path: '/user', component: User, meta:{ title: '用户' } } ] // 2. 通过beforeEach()实现对路由跳转的监听 router.beforeEach((to, from, next)=>{ document.title = to.matched[0].meta.title; next(); })
-
to.matched[0]
的使用说明- 在路由组件存在嵌套的情况下使用
to.matched[0]
- 我们可以打印一下
to
(console.log(to)😉,会发现在路由组件存在嵌套的情况时,meta
是为空的,里面没有我们设置的title
。而数组matched
里有两个元素,子组件和父组件
- 在第一位元素(父组件)里找到了我们设置的
title
- 当不存在组件嵌套时,可以直接写
document.title = to.meta.title;
- 在路由组件存在嵌套的情况下使用
- 在
6. keep-alive
**前提:**有两个组件Home、User
,Home
下又有两个子组件
-
在
Home
组件下创建两个生命周期函数created、destroyed
,在控制台打印一串字符。created(){ // console.log('home created'); }, destroyed(){ // console.log('home destroyed'); },
-
我们可以发现,进入
Home
组件页面时,控制台会打印home created
;l离开时会打印home destroyed
。即Home
组件的创建和销毁 -
每一次切换和重新进入
Home
组件页面都会新建一个组件,这样会导致我们上一次在Home
组件页面上的任何操作都不会被保留。 -
我们可以通过
<keep-alive>
解决这个问题
6.1 解决办法
**要求:**不管我们点击了Home
组件的哪一个子组件,重新进入后页面还是显示上一次点击的子组件的内容
-
第一步:要实现上面的要求,首先就不能将
Home
的某一个子组件重定向(redirect
)为首页 -
第二步:将要缓存的组件用
keep-alive
包裹起来,根据要求我们是在Home
和User
两个组件中来回切换,需要对这两个组件进行缓存- 在哪引用了路由就写在哪
<template> <div id="app"> <router-link to="/home/news" tag="button">首页</router-link> <router-link :to="'/user/'+userId" tag="button">用户</router-link> <keep-alive> <router-view></router-view> </keep-alive> </div> </template>
-
第三步:使用
activated()
生命周期函数初始化页面数据- 在
vue
对象存活的前提下(我们之前使用<keep-alive>
就是为了实现这个目的),进入当前存在activated()函数的页面时,一进入页面就触发this.$router.push('/home/news');
,这样已进入页面就会显示我们想要看到的内容 - 在
Home
组件中实现
export default { name: 'Home', data(){ return{ path: '/home/news' } }, activated(){ this.$router.push(this.path).catch((e) => e); } }
- 在
-
第四步:实现重新进入后页面还是显示上一次点击的子组件的内容
- 可以通过
this.$route.path
获取到每一次是从哪个路由路径离开的
beforeRouteLeave(to, from, next){ this.path = this.$route.path; next(); }
- 可以通过
6.2 补充
当我们不希望某个组件被频繁销毁创建时,我们就需要用到keep-alive
两个属性
-
include
- 字符串或正则表达,只有匹配的组件会被缓存 -
exclude
- 字符串或正则表达式,任何匹配的组件都不会被缓存exclude
里写的字符串是对应组件的name
- 写多个组件时中间不要有空格
<keep-alive exclude="User,Home"> <router-view></router-view> </keep-alive>