路由(routing)就是通过互联的网络把信息从源地址传输到目的地址的活动
路由器提供了两种机制: 路由和转送
路由中有一个非常重要的概念叫路由表,路由表本质上就是一个映射表, 决定了数据包的指向
后端路由和后端路由
阶段一:后端路由 后端渲染
如下图,当浏览器请求数据,服务器会渲染出一个 jsp 页面,使得每个 jsp 和 url 形成一个映射关系。后端渲染,也是服务器渲染,就是页面是通过服务器渲染,而不是在浏览器渲染。早期的网站开发是服务器直接生产渲染好对应的HTML页面, 返回给客户端进行展示。

每个页面有自己对应的网址, 也就是URL,URL会发送到服务器, 服务器会通过正则对该URL进行匹配, 并且最后交给一个Controller进行处理,Controller进行各种处理, 最终生成HTML或者数据, 返回给前端,这就完成了一个IO操作。上面的这种操作, 就是后端路由,也就是服务器来处理 URL 和 页面之间的映射关系。
后端路由的优点:
当我们页面中需要请求不同的路径内容时, 交给服务器来进行处理, 服务器渲染好整个页面, 并且将页面返回给客户顿。这种情况下渲染好的页面, 不需要单独加载任何的 js 和 css , 可以直接交给浏览器展示, 这样也有利于 SEO 的优化。
后端路由的缺点:
- 整个页面的模块由后端人员来编写和维护的.
- 前端开发人员如果要开发页面, 需要通过PHP和Java等语言来编写页面代码,而且通常情况下 HTML 代码和数据以及对应的逻辑会混在一起, 编写和维护都是非常糟糕的事情.
阶段二 :前后端分离阶段
随着Ajax的出现, 有了前后端分离的开发模式。后端只提供 API 来返回数据, 前端通过 Ajax 获取数据, 并且可以通过JavaScript将数据渲染到页面中。

阶段三:单页面富应用阶段
SPA 最主要的特点就是在前后端分离的基础上加了一层前端路由。也就是前端来维护一套路由规则。
前端路由的核心是什么呢?改变URL,但是页面不进行整体的刷新。

实现改变 URL,页面不刷新
1)URL 的 hash
URL 的 hash 也就是锚点(#), 本质上是改变 window.location 的 href 属性,我们可以通过直接赋值 location.hash来改变 href , 但是页面不发生刷新。
1 | location.hash = 'aa' |
2)HTML5 的 history 模式
history 接口是 HTML5 新增的, 它有五种模式改变 URL 而不刷新页面
- history.pushState() :改变 url,页面不刷新,可以点击浏览器左上角的 回退 和 前进 按钮。
- history.replaceState() :替换 url ,页面不刷新。没有回退和前进
- history.back():回退一个url
- history.forward():前进一个url
- history.go():参数是整数,history.go(-1)就是回退到上一个 url ,-2 就是回退两个,1 就是 前进到下一个 url
1 | history.pushState({},'','/nar') |
1 vue-router 基本使用
es6 增强语法经常用 ,{cpn:cpn}=》 {cpn}
安装
1 | npm install vue-router --save |
1.1 配置 router
第一步:导入路由对象,并且调用 Vue.use(VueRouter)
第二步:创建路由实例,并且传入路由映射配置 (路由配置是放在routes上面,下图写错了)
第三步:在Vue实例中挂载创建的路由实例(通过 router 属性挂载)
1.2 vue-router 使用步骤
第一步: 创建路由组件,在 component 文件夹下创建 vue 组件,并导出

第二步: 配置路由映射: 组件和路径映射关系,在 router 的 routes 配置 url 和 组件的对应关系

第三步: 使用路由: 通过<router-link>
和<router-view>
,在App.vue中使用路由
<router-link>
: 该标签是一个 vue-router 中已经内置的组件, 它会被渲染成一个<a>
标签<router-view>
: 该标签会根据当前的路径, 动态渲染出不同的组件,他将决定组件在哪里显示。
在路由切换时, 切换的是<router-view>
挂载的组件, 其他内容不会发生改变
下图中,可以看出<router-view>
可以放在任意位置,他和任意便签兄弟标签都是同级的。
1.3 路由默认路径
当我们打开这个应用的时候,如何让页面默认显示首页组件呢?修改路由的默认配置,在routes中配置了一个映射
1 | const routes = [ |
1.4 HTML5的History模式
默认情况下, 路径的改变使用的 URL 的 location.hash,hash 会在路径前面追加一个 #,但是 history 模式下是没有这个 # 的。如何去掉 url 路径中的 # ?
1 | //在路由实例中,配置 mode 属性的 为 history |
1.5 router-link 补充
<router-link>
的属性:
1.to :指定跳转的路径
2.tag : tag 可以指定<router-link>
之后渲染成什么组件,默认是 a 标签
3.replace:replace不会留下history记录, 所以指定replace的情况下, 后退键返回不能返回到上一个页面中
4.active-class:当<router-link>
对应的路由匹配成功时, 会自动给当前元素默认一个router-link-active
的class, 设置 active-class 可以修改默认的名称
- 如果觉得批量修改
active-class
太麻烦,可以配置 router 实例里面的linkActiveClass
属性。
1 | <router-link to='/home' tag='button' replace active-class='active'>Home</router-link> |
1.6 路由通过代码跳转
之前一直通过<router-link>
的 to 属性进行 url 跳转, 但是当页面的跳转需要执行对应的 JavaScript 代码, 就需要使用路由代码跳转。
1 | this.$router.push('/home') |

1.7 动态路由
某些情况下,一个页面的 path 路径可能是不确定的,比如我们进入用户界面时,希望是如下的路径:/user/xiaoming,除了有前面的 /user 之外,后面还跟上了用户的 ID。这种 path 和 Component 的匹配关系,我们称之为动态路由。
1.在routes 中配置 User 组件 和 ‘/user’的路由映射关系
1 | { |
2.通过 router-link 的 to 属性实现跳转,不同的事 id 属性是动态获取的数据,这里需要 v-bind 绑定to属性.
1 | <router-link :to="'/user/'+id" tag='button' >User</router-link> |
3.如果想要在 User 组件中显示 id 的属性,需要使用组件的 $route 属性
1 | <p>{{$route.params.Id}}</p> |
1.8 路由的懒加载
Vue CLI 默认会将我们的 js 代码打包到如下 3 个 js 文件中。
app.
***
.js:我们的业务代码,manifest.
***
.js:主要是做底层支撑的代码,比如浏览器是不识别模块化的代码,在这个 js 里面会将一些 commonJS 代码转化为浏览器认识的代码。vendor.
***
.js:主要是第三方代码,比如 vue 框架
路由中通常会定义很多不同的页面,如上面所说所有业务代码都会放在 app.***
.js 中,这个 js 文件就会很大,用户在第一次加载页面的时候会很慢,可能出现短暂空白的情况。如何避免这种情况呢?
路由懒加载。
路由懒加载的主要作用就是将路由对应的组件打包成一个个的 js 代码块,只有在这个路由被访问到的时候, 才加载对应的组件。
路由懒加载的方式:
方式一: 结合Vue的异步组件和Webpack的代码分析
1 | const Home = resolve => { require.ensure(['../components/Home.vue'], () => { resolve(require('../components/Home.vue')) })}; |
方式二: AMD写法
1 | const About = resolve => require(['../components/About.vue'], resolve); |
方式三: 在ES6中, 我们可以有更加简单的写法来组织Vue异步组件和Webpack的代码分割
推荐使用上面定义一个变量保存懒加载的对象。

路由懒加载的效果
打包文件之后,发现除了一起出现的 3 个最初的 js 和 3 个组件的 js 之外,每个 js 都有对应的map 文件。map文件的作用:项目打包后,代码都是经过压缩加密的,如果运行时报错,输出的错误信息无法准确得知是哪里的代码报错。有了map就可以像未加密的代码一样,准确的输出是哪一行哪一列有错。想要去除这个 map 文件修改 index.js 配置:productionSourceMap:false
。

2 vue-router 嵌套路由
嵌套路由就是比如在 home 页面中, 我们希望通过 /home/news 和 /home/message 访问一些内容,一个路径映射一个组件, 访问这两个路径也会分别渲染两个组件。
实现嵌套路由有两个步骤:
1.创建对应的子组件, 并且在路由映射中配置对应的子路由。在子组件中配置 path 不需要添加 /

2. 在组件内部使用<router-view>
标签, to 属性里的路径要从父组件开始,并加上/

3 vue-router 参数传递
3.1 params 和 query
vue-router 传递参数主要有两种类型: params 和 query
params的类型:
配置路由格式: /router/:id
传递的方式: 在path后面跟上对应的值
传递后形成的路径: /router/123, /router/abc
query 的类型:
配置路由格式: /router, 也就是普通配置
传递的方式: 对象中使用 query 的 key 作为传递方式
传递后形成的路径: /router?id=123, /router?id=abc
传递方式又分为两种,<router-link>
的方式和 JavaScript 代码方式, 在 1.6 和 1.7 节分别讲了 params 类型传递参数。
1)<router-link>
用于 query 类型传参
1 | <router-link :to="{ |
2)JavaScript 代码方式 用于 query 类型传参
1 | <button @click="proClick">Profile</button> |
补充生命周期函数:
created;创建出组件之后回调
mounted;组件被挂载在某个实例之后回调
updated:页面发生更新,数据发生变化回调
3.2 导航守卫
vue-router 提供的导航守卫主要用来监听监听路由的进入和离开的, vue-router 提供了 beforeEach 和 afterEach 的钩子(回调)函数, 它们会在路由即将改变前和改变后触发。
beforeEach 方法
参数是一个箭头函数,箭头函数有 3 个参数;
to: 即将要进入的目标的路由对象
from: 当前导航即将要离开的路由对象
next: 调用该方法后, 才能进入下一个钩子
1 | // 路由守卫 |
后置钩子 afterEach, 不需要主动调用 next() 函数
1 | router.afterEach((to, from) => { |
上面我们使用的导航守卫, 被称之为全局守卫,除此之外还有路由独享的守卫和组件内的守卫。
1 | { |
4 keep-alive
当我们从一个组件跳转到另一个组件的时候,前1️⃣个组件就会被 destroyed,后一个组件会被 created ,这里可以在组件的声明周期函数 destroyed 和 created 函数查看。来回切换组件,频繁创建和销毁同一个组件,如何避免这种开销?
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态。router-view 也是一个组件,如果直接被包在 keep-alive 里面,所有路径匹配到的视图组件都会被缓存,也就是这些组件不会被 destroy ,也不用重新 created
1 | <keep-alive> |
此外 keep-alive 有两个属性
include - 字符串或正则表达,只有匹配的组件会被缓存
exclude - 字符串或正则表达式,任何匹配的组件都不会被缓存
1 | <!-- name 属性为 profile 的组件不会被缓存,里面的值要和组件的 name 属性对应 --> |
5 TabBar 案例
需求:利用组件化思想做一个 TabBar 模块。
1)基础和结构搭建
2)页面大致组件划分
页面下方有一个 TabBar 组件,用来封装各种菜单按钮;TabBar 组件中的每个菜单,抽离出来用一个 TabBarItem 组件来封装。所以大致分了 3 个组件:App.vue(整个页面)、TabBar.vue(整个底部导航)、TabBarItem.vue(整个菜单)
3)组件编写
- 对于公共一些基础 css 代码,新建一个 base.css(src\assets\css\base.css),然后在 App.vue 中引入文件。
1 | <style> |
- 分析 App 组件:在 app 组件中可以挂在 TabBar组件 和 TabBarItem 组件,并决定每个组价的 slot 里面显示的内容
1 | <div id="app"> |
- 分析 TabBar 组件:是一块区域,因此设置一个 slot ,这个 slot 将决定底部导航里面的内容,在 App组件中使用 TabBar组件,并在TabBar 组件中指明 slot 的具体内容——4 个按钮——4 个TabBarItem 组件
- 分析 TabBarItem 组件:每个 TabBarItem 包含上面的图片(激活状态和未激活状态)和下面的文字 ,分为3 部分,因此使用三个 slot(每个 slot 标记 name 属性)。 TabBarItem 组件内部的 css 代码均留在原组件,因为在编译的时候 TabBarItem .vue 会作为一个对象被导入 app.vue中。
1 | <li class="tab-bar-item"> |
tabbar颜色动态控制
多么沉痛的领悟啊,动态绑定 class 一直出问题,报错是 data 里面的 isActive 未定义。我把 data 写成了 date。哪里报错找哪里,书写错误最有可能。
4)配置路由映射关系
每个页面(主页、购物车等)称之为视图,在src下新建一个 views 文件夹专门存放视图组件。比如 home.vue的路径是 src\views\home\Home.vue
为每个组件配置路由,在 TabBarItem .vue 中每个 TabBarItem 上面绑定点击事件切换路由 。
动态获取 path
path 是一个变量,需要通过 props 从TabBar 中获取,在 TabBar 中需要绑定每个组件的 path
1 | <tab-bar-item path="/home"> |
tabbar颜色动态控制
图片的颜色的控制,是通过 v-if 和 v-else 切换两张照片,文字的颜色是通过 v-bind 绑定 style,并且color的值是通过 props 父组件中传来的。但是两者面临一个判断——怎么表示这个激活状态。
1 | //通过当前路由的path 判断是包含 path |
完善:因为 App 作为最大的组件,仅关于 TabBar 的代码就占了很多,这样不利于代码管理。因此,新建一个 MainTabBar组件 (src\components\MainTabBar\MainTabBar.vue) 来管理关于 TabBar 的代码 ,在 App 组件中注册 MainTabBar 组件即可。
总结:关于路由的配置, <router-view/>
需要在 App.vue 中占位。插槽的 <slot>
标签最终会被组件里面的元素替代,因此 slot 上无法绑定属性,最好在每一个 slot 外面加一层 div ,样式或者其他属性设置在 div 上面。
拓展$router
和 $route
的区别
$router
是路由实例 router ,最大的那个路由对象,想要导航到不同URL,则使用$router.push
方法
在 routes 里面配置了一系路由关系,
$route
是现在正在活跃的路由,对象里面可以获取 name、path、query、params等
5.1 文件路径别名
文件路径随着文件的移动会发生变化,每个路径修改比较麻烦,我们可以给某些特定文件夹其别名,这样就可以不用修改文件路径。
在 CLI2 里面 build/webpack.base.config.js
里面可以添加文件路径别名
1 | resolve: { |
使用别名分两种情况
1)import 导入的路径:直接用别名替代路径
1 | @import "assets/css/base.css"; |
2)在 html 中 src、href等路径,需要在别名前面加一个 ~
1 | <img src="~assets/img/tabbar/profile_active.svg" alt=""> |