上一篇: Vue - 6 - 脚手架 - vue cli(安装、使用、目录结构)、ESLint(自动匹配、关闭)
下一篇:Vue - 8 - 回顾 - Promise
视频:https://www.bilibili.com/video/av89760569?p=100
Vue 知识点汇总(下)–附案例代码及项目地址 - https://blog.csdn.net/wuyxinu/article/details/103966175
文章目录
- vue-router详解
- # 认识路由
- ## 路由表
- ## 后端路由(渲染)阶段
- 早期 的网站开发整个 `HTML` 页面是由 **服务器** 来渲染的.
- 一个网站, 这么多页面服务器如何处理呢?
- 优点、缺点
- ## 前端路由(渲染)阶段
- 前后端分离阶段
- ## 单页面富应用阶段:改变URL,但是页面不进行整体的刷新
- 方法一:URL的hash
- 方法二:HTML5的history模式
- 1. pushState
- 2. replaceState
- 3. go
- 4. back
- 5. forward
- ## 前后端路由 - 总结 ([跳到 vue-router 安装](#_vuerouter_274))
- 前端渲染:(趋势)
- 后端渲染:(过时)
- 前后端分离?
- 什么是前端路由, 什么是后端路由?
- 最后是几个常见误解的说明:
- # 认识vue-router
- # Vue的vue-router - 官网:[https://router.vuejs.org/zh/](https://router.vuejs.org/zh/)
- # 安装和使用vue-router
- ## 步骤一: 安装vue-router
- ## 步骤二: 在模块化工程中使用它
- ## 使用vue-router的步骤:
- 最终效果如下
- ## 细节处理
- 路由的默认路径 : `redirect`
- HTML5 的 History 模式
- router-link补充
- 批量修改 link-Active-Class
- exact-active-class (路由嵌套再讲)
- 不用 ` router-link` ,应该怎么做? - `this.$router`
- # 动态路由 - 官网:[https://router.vuejs.org/zh/guide/essentials/dynamic-matching.html](https://router.vuejs.org/zh/guide/essentials/dynamic-matching.html)
- ## router 和 route 的区别
- # 路由的懒加载 + 打包源码(浅析)
- ## 认识路由的懒加载
- ## 打包源码(浅析)([直接跳到“懒加载的使用”](#font_colorea2027___font_804))
- ## 懒加载的使用
- 懒加载的方式
- 路由懒加载的效果
- # 路由嵌套
- ## 嵌套路由实现
- # 传递参数
- ## 传递参数的方式
- params的类型:
- query的类型:
- 补充:URL的组成
- ## 传递方式总结
- 传递参数方式一: ``
- 传递参数方式二: `JavaScript代码`
- 获取参数
- # 导航守卫
- ## 什么是导航守卫?
- ## 为什么使用导航守卫?
- ## 导航守卫使用
- ## 导航守卫补充
- ## keep-alive
- ## include 、 exclude
- # 案例:TabBar
路由是一个网络工程里面的术语。
路由(routing
)就是通过互联的网络把信息从源地址传输到目的地址的活动. — 维基百科{一个不存在的网站}
如:路由器提供了两种机制: 路由和转送.
路由是决定数据包从来源到目的地的路径.(转送将输入端的数据转移到合适的输出端.)
路由中有一个非常重要的概念叫 路由表
.
路由表本质上就是一个映射表
, 决定了数据包的指向.
注意哦:不是说路由等于渲染。
HTML
页面是由 服务器 来渲染的.(服务器直接生产渲染好对应的HTML页面, 返回给客户端进行展示.)
一个页面有自己对应的网址, 也就是 URL
.
URL
会发送到服务器, 服务器会通过正则对该 URL
进行匹配, 并且最后交给一个 Controller
进行处理.
Controller
进行各种处理, 最终生成 HTML
或者数据, 返回给前端.
这就完成了一个 IO
操作.
上面的这种操作, 就是后端路由.
优点:
当我们页面中需要请求不同的路径内容时, 交给服务器来进行处理, 服务器渲染好整个页面, 并且将页面返回给客户顿.
这种情况下渲染好的页面, 不需要单独加载任何的js和css, 可以直接交给浏览器展示, 这样也有利于SEO的优化.
缺点:
PHP
和 Java
等语言来编写页面代码.HTML
代码和数据以及对应的逻辑会混在一起, 编写和维护都是非常糟糕的事情.随着 Ajax
的出现, 有了前后端分离的开发模式.
后端只提供 API
来返回数据, 前端通过 Ajax
获取数据, 并且可以通过 JavaScript
将数据渲染到页面中.
优点:前后端责任的清晰,
并且当移动端(iOS/Android)出现后, 后端不需要进行任何处理, 依然使用之前的一套API即可.
目前很多的网站依然采用这种模式开发.
其实 SPA
(single page web application
= 整个网页只有一个html页面)最主要的特点就是在前后端分离的基础上加了一层前端路由.
也就是前端来维护一套路由规则.
前端路由的核心是什么呢?
改变URL,但是页面不进行整体的刷新。
那么问题来了,
如何实现 改变页面不改变url呢?
有两种方法:
URL
的 hash
也就是锚点 (#)
, 本质上是改变 window.location的href
属性.
我们可以通过直接赋值 location.hash
来改变 href
, 但是页面不发生刷新
history接口是HTML5新增的, 它有五种模式改变URL而不刷新页面.
history.pushState()
history.replaceState()
和 push 有什么区别呢?
这个 replace 是替换的意思,就是不入栈的。即,不会再history保留记录
history.go()
后退
history.back()
等价于 history.go(-1)
向前
history.forward()
则等价于 history.go(1)
这三个接口(go、back、forward
)等同于浏览器界面的前进后退。
看到这里,你可以自问自答一下,下面这个几个问题。
指的是后端返回 JSO
N数据,前端利用预先写的 html
模板,循环读取 JSON
数据,拼接字符串( es6
的模板字符串特性大大减少了拼接字符串的的成本),并插入页面。
好处:网络传输数据量小。
不占用服务端运算资源(解析模板),模板在前端(很有可能仅部分在前端),改结构变交互都前端自己来了,改完自己调就行。
坏处:前端耗时较多,对前端工作人员水平要求相对较高。
前端代码较多,因为部分以前在后台处理的交互逻辑交给了前端处理。占用少部分客户端运算资源用于解析模板。
前端请求,后端用后台模板引擎直接生成html,前端接受到数据之后,直接插入页面。
好处:前端耗时少,即减少了首屏时间,模板统一在后端。前端(相对)省事,不占用客户端运算资源(解析模板)
坏处:占用服务器资源。而且代码严重耦合
前端人员和后端人员约定好接口后,前端人员彻底不用再关心业务处理是怎么回事,他只需要把界面做好就可以了,
后端人员也不用再关系前端界面是什么样的,他只需要做好业务逻辑处理即可。
服务的切离,代码管理,服务部署也都独立出来分别管理,系统的灵活性也获得了极大的提升。
注意,这不是个微服务架构,那是另外一个议题了
总结,任何系统架构设计,实际上是对组织结构在系统上进行映射,前后端分离,就是在对前端开发人员和后端开发人员的工作进行解耦,尽量减少他她们之间的交流成本,帮助他她们更能专注于自己擅长的工作。
什么是前端路由?
.
很重要的一点是页面不刷新,前端路由就是把不同路由对应不同的内容或页面的任务交给前端来做,每跳转到不同的URL都是使用前端的锚点路由. 随着(SPA)单页应用的不断普及,前后端开发分离,目前项目基本都使用前端路由,在项目使用期间页面不会重新加载。
什么是后端路由?
.
浏览器在地址栏中切换不同的url时,每次都向后台服务器发出请求,服务器响应请求,在后台拼接html文件传给前端显示, 返回不同的页面, 意味着浏览器会刷新页面,网速慢的话说不定屏幕全白再有新内容。后端路由的另外一个极大的问题就是 前后端不分离。
.
缺点:
当项目十分庞大时,加大了服务器端的压力,同时在浏览器端不能输入制定的url路径进行指定模块的访问。
另外一个就是如果当前网速过慢,那将会延迟页面的加载,对用户体验不是很友好。
什么时候使用前端路由?
.
在单页面应用,大部分页面结构不变,只改变部分内容的使用
前端路由有什么优点和缺点?
.
优点:
.
缺点:
前后端分离是说浏览器和后端服务分离吗?
.
不是,前后端分离里的前端不是浏览器,指的是生成 HTML 的那个服务,它可以是一个仅仅生成 HTML 的 Web 服务器,也可以是在浏览器中通过 JS 动态生成 HTML 的 单页应用。实践中,有实力的团队往往在实现前后端分离里时,前端选用 node 服务器,后端选用 C#、Java 等(排名不分先后)
前后端分离是种技术吗?
.
不是,前后端分离是种架构模式,或者说是最佳实践。所谓模式就是大家这么用了觉得不错,你可以直接抄来用的固定套路。
前后端分离是最佳实践吗?
.
看你团队和项目的情况,如果是短平快的小项目,真的没必要。如果是面向简历开发,那绝对在任何时候都应该使用前后端分离这种架构。
目前前端流行的三大框架, 都有自己的路由实现:
Angular
的 ngRouter
React
的 ReactRouter
当然, 我们的重点是 vue-router
vue-router
是 Vue.js
官方的路由插件,它和 vue.js
是深度集成的,适合用于构建单页面应用。
我们可以访问其官方网站对其进行学习:
https://router.vuejs.org/zh/
vue-router
是基于路由和组件的
路由用于设定访问路径, 将路径和组件映射起来.
在 vue-router
的单页面应用中, 页面的路径的改变就是组件的切换.
npm install vue-router --save
注意,生产时候也用的,所以不是开发依赖哦!
安装完后,多了 /src/router 文件夹
(因为是一个插件, 所以可以通过 Vue.use()
来安装路由功能)
第一步:导入路由对象,并且调用 Vue.use(VueRouter)
第二步:创建路由实例,并且传入路由映射配置
// 第一、二步 ,在 /src/router/index.js
// 配置路由的相关信息
import VueRouter from 'vue-router'
import Vue from 'vue'
// 1. 通过 Vue.use(插件), 安装插件
Vue.use(VueRouter)
// 2. 创建 VueRouter 对象
const routes = [
// 未来要在这里配置很多映射关系
// 让一个url映射一个组件
// ....
]
const router = new VueRouter({
// 配置路由和组件之间的响应关系
routes
})
第三步:在 Vue
实例中挂载创建的路由实例
import Vue from 'vue'
import App from './App'
// import router from './router'
// 如果导入的是目录,会自动去找目录的 index 文件
import router from './router'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
// router,
render: h => h(App)
})
第一步: 创建路由组件
这里创建两个 一个 About.vue
另外一个 Home.vue
添加 src/components/About.vue
<template>
<div>
<h2>我是关于h2>
<p>我是关于内容,哈哈哈哈哈p>
div>
template>
<script>
export default {
name: 'About'
}
script>
<style>
style>
添加 src/components/Home.vue
<template>
<div>
<h2>我是首页h2>
<p>我是首页内容,哈哈哈哈哈p>
div>
template>
<script>
export default {
name: 'Home'
}
script>
<style>
style>
第二步: 配置路由映射: 组件和路径映射关系 - routes
官方
routes
讲解:https://router.vuejs.org/zh/api/#routes
// 配置路由的相关信息
import VueRouter from 'vue-router'
import Vue from 'vue'
// 导入自定义组件
import Home from '../components/Home.vue'
import About from '../components/About.vue'
// 通过 Vue.use(插件), 安装插件
Vue.use(VueRouter)
// 创建 VueRouter 对象
const routes = [
{
path: '/home',
component: Home
}, {
path: '/about',
component: About
}
]
const router = new VueRouter({
// 配置路由和组件之间的响应关系
routes
})
// 将 router 对象传入到 Vue 实例
export default router
第三步: 使用路由: 通过
和
: https://router.vuejs.org/zh/api/#router-link
:https://router.vuejs.org/zh/api/#router-view
: 该标签是一个
vue-router
中已经内置的组件, 它会被渲染成一个标签.
: 该标签会根据当前的路径, 动态渲染出不同的组件.
.
网页的其他内容, 比如顶部的标题/导航, 或者底部的一些版权信息等会和处于同一个等级.
.
在路由切换时, 切换的是挂载的组件, 其他内容不会发生改变.
修改 src/App.vue
<template>
<div id="app">
<router-link to="/home">首页router-link>
<router-link to="/about">关于router-link>
<router-view>router-view>
div>
template>
<script>
export default {
name: 'App'
}
script>
<style>
style>
redirect
默认情况下, 进入网站的首页, 占位符
没有内容
我们希望
渲染首页的内容.
怎么办?
非常简单, 我们只需要配置多配置一个映射就可以了.
我们在 routes
中又配置了一个映射.
path
配置的是根路径: /
redirect
是重定向, 也就是我们将根路径重定向到 /home
的路径下,这样就可以得到我们想要的结果了.
我们前面说过改变路径的方式有两种:
URL
的 hash
HTML5
的 history
默认情况下, 路径的改变使用的 URL
的 hash
.
如果希望使用 HTML5
的 history
模式, 非常简单, 进行如下配置即可:
在前面的
中, 我们只是使用了一个属性: to
, 用于指定跳转的路径.
还有一些其他属性:
tag
: tag
可以指定
之后渲染成什么组件,
如:
<router-link to="/home" tag="button">首页router-link>
replace
: replace
不会留下 history
记录,
所以指定 replace
的情况下, 后退键返回不能返回到上一个页面中
如:
<router-link to="/home" replace>首页router-link>
active-class
: 当
对应的路由匹配成功时, 会自动给当前元素设置一个 router-link-active
的 class
, 设置 active-class
可以修改默认的名称.
如:
- 在进行高亮显示的导航菜单或者底部
tabbar
时, 会使用到该类.- 但是通常不会修改类的属性, 会直接使用默认的
router-link-active
即可.
该 class
具体的名称也可以通过 router
实例的属性进行修改
进行下图修改即可
类似于 active-class
, 只是在精准匹配下才会出现的 class
.
后面看到嵌套路由时, 我们再看下这个属性.
router-link
,应该怎么做? - this.$router
vue
底层会为每个 实例添加 一个属性 this.$router
, 就是用来做自定义 push 的
使用如下
<template>
<div id="app">
<button @click="to('/home')">首页button>
<button @click="to('/about')">关于button>
<router-view>router-view>
div>
template>
<script>
export default {
name: 'App',
methods: {
to (path) {
this.$router.push(path) // or replace
}
}
}
script>
<style>
style>
注意:错误例子
你可能会想到 直接调用原生的history.pushState
to (path) history.pushState({}, {}, path) }
.
但是,这是允许的!
理由有两个
- 没办法直接让
的内容改变
- 原则上,不要绕过 vue 操作 浏览器
在某些情况下,一个页面的 path
路径可能是不确定的
比如
我们进入用户界面时,希望是如下的路径:
/user/aaaa或/user/bbbb
除了有前面的/user
之外,后面还跟上了用户的 ID
这种 path
和 Component
的匹配关系,我们称之为动态路由(也是路由传递数据的一种方式)。
更多:https://router.vuejs.org/zh/api/#%E8%B7%AF%E7%94%B1%E5%AF%B9%E8%B1%A1
官方解释:https://router.vuejs.org/zh/api/#%E8%B7%AF%E7%94%B1%E5%AF%B9%E8%B1%A1%E5%B1%9E%E6%80%A7
英文上:
官方给出了解释:
Javascript 包
会变得非常大,影响页面加载。路由懒加载做了什么?
测试:
执行指令,进行打包(构建)
npm run build
打包结果:(多了 dist
文件夹,就可以拷给后台服务器了)
下面看目录结构
js
文件进行了拆分index.hml
文件,给客户引用上面看到了 ,打包后,js被分成了几个文件
大致能分成三种:
app
xxxxxxxxxxxxxxxx.js - 我们制定的组件manifest
xxxxxxxxxxxxxxx.js - 我们制定的组件的驱动vendor
xxxxxxxxxxxxxxxxxx.js - 第三方组件为了上面的
js
源码可读, 我们先 到build/webpack.prod.conf.js
里面,在生产环境下 把 丑化插件给关了
再次编译,打开 manifest
xxxxx.js 文件 (先看驱动)
明显,这个方法,是在组件加载时候做显示的
这时,我们看 app
xxxxxx.js 就有点头绪了。
(上图)代码分了几部分,均是立即执行函数。
中间最大一块代码,被隐藏的那部分就是我们自己写的代码的封装
(上面)可以看到,所谓打包,就是把我们的代码以立即执行函数的形式,复制到 webpackJsonp
里面,
当我们有需要的时候,声明一下,就能被调用了!
补充:《jsonp原理详解——终于搞清楚jsonp是啥了》
官方教程 - https://router.vuejs.org/zh/guide/advanced/lazy-loading.html#%E8%B7%AF%E7%94%B1%E6%87%92%E5%8A%A0%E8%BD%BD
之前,讲了路由懒加载是什么,还有前提知识(vue的分包)
下面讲 懒加载怎么用
现在都用第三种方法
我们修改之前的代码 src/router/index.js
// 配置路由的相关信息
import VueRouter from 'vue-router'
import Vue from 'vue'
// 导入自定义组件
// import Home from '../components/Home.vue'
// import About from '../components/About.vue'
// import User from '../components/User.vue'
// 通过 Vue.use(插件), 安装插件
Vue.use(VueRouter)
// 创建 VueRouter 对象
const routes = [
{
path: '/',
redirect: '/home'
}, {
path: '/home',
component: () => import('../components/Home.vue')
}, {
path: '/about',
component: () => import('../components/About.vue')
}, {
path: '/user/:username',
component: () => import('../components/User.vue')
}
]
const router = new VueRouter({
// 配置路由和组件之间的响应关系
routes,
mode: 'history',
linkActiveClass: 'active'
})
// 将 router 对象传入到 Vue 实例
export default router
官方教程:
嵌套路由是一个很常见的功能
比如在 home
页面中, 我们希望通过 /home/news
和 /home/message
访问一些内容.
一个路径映射一个组件, 访问这两个路径也会分别渲染两个组件.
实现嵌套路由有两个步骤:
创建对应的子组件, 并且在路由映射中配置对应的子路由.
在组件内部使用 < router-view>
标签.
src/router/index.js
// 配置路由的相关信息
import VueRouter from 'vue-router'
import Vue from 'vue'
// 导入自定义组件
// import Home from '../components/Home.vue'
// import About from '../components/About.vue'
// import User from '../components/User.vue'
// 通过 Vue.use(插件), 安装插件
Vue.use(VueRouter)
// 创建 VueRouter 对象
const routes = [
{
path: '/',
redirect: '/home'
}, {
path: '/home',
component: () => import('../components/Home.vue'),
children: [
{
path: '/home',
redirect: '/home/news'
},
{
// 注意,这里不加斜杠'/'
path: 'news',
component: () => import('../components/HomeNews.vue')
},
{
path: 'message',
component: () => import('../components/HomeMessage.vue')
}
]
}, {
path: '/about',
component: () => import('../components/About.vue')
}, {
path: '/user/:username',
component: () => import('../components/User.vue')
}
]
const router = new VueRouter({
// 配置路由和组件之间的响应关系
routes,
mode: 'history',
linkActiveClass: 'active'
})
// 将 router 对象传入到 Vue 实例
export default router
添加两个 组件
src/component/HomeMessage.vue
<template>
<div>
<p>消息p>
div>
template>
<script>
export default {
name: 'Message'
}
script>
<style>
style>
src/component/HomeNews
<template>
<div>
<ul>
<li v-for="(n,i) in news" :key='i'>{{ n }}li>
ul>
div>
template>
<script>
export default {
name: 'News',
data () {
return {
news: [
'Fantastic Granite Chips',
'Fantastic Granite Chips',
'Fantastic Granite Chips',
'Fantastic Granite Chips'
]
}
}
}
script>
<style>style>
在父组件内指定添加的内容的位置
<template>
<div>
<h2>我是首页h2>
<p>我是首页内容,哈哈哈哈哈p>
<router-link to="/home/news">新闻router-link>
<router-link to="/home/message">消息router-link>
<router-view>router-view>
div>
template>
<script>
export default {
name: 'Home'
}
script>
<style>
style>
传递参数主要有两种类型: params
和 query
前面也用过了
配置路由格式: /router/:id
传递的方式: 在 path
后面跟上对应的值
传递后形成的路径: /router/123, /router/abc
<template>
<div>
<h2>{{username}}h2>
<h2>我是用户h2>
<p>用户,嘿嘿嘿~p>
<router-link :to="profile">详情router-link>
<router-view>router-view>
div>
template>
<script>
export default {
name: 'User',
computed: {
username () {
console.log(this)
return this.$route.params.username
},
profile () {
return {
path: `/user/${this.username}/profile`,
query: {
age: 18,
gender: 'female',
height: 180
}
}
}
}
}
script>
<style>
style>
添加 src/components/UserProfile.vue
<template>
<div>
<h3>age:{{$route.query.age}}h3>
<h3>gender:{{$route.query.gender}}h3>
<h3>height:{{$route.query.height}}h3>
div>
template>
<script>
export default {
name: 'Profile'
}
script>
<style>
style>
JavaScript代码
官方解释:https://router.vuejs.org/zh/api/#%E8%B7%AF%E7%94%B1%E5%AF%B9%E8%B1%A1%E5%B1%9E%E6%80%A7
补充 or 回顾
- route 和 router 的区别
- 视频: router 和 route 的由来
官方文档: 导航守卫
vue-router
提供的导航守卫,主要用来监听监听路由的 进入
和 离开
的.
vue-router
提供了 beforeEach
和 afterEach
的钩子函数, 它们会在路由即将改变前和改变后触发.
比如说, 修改网页的标题
如果在,每一个路由对应的组件 .vue
文件中指定 title
。太麻烦
当页面比较多时, 这种方式不容易维护(因为需要在多个页面执行类似的代码).
解决方案: 使用导航守卫即可.
我们可以利用 beforeEach
来完成标题的修改.
hook
)当中定义一些标题, 可以利用 meta
来定义
meta
元素的元素
很多语言都有meta
概念,如:metaClass
导航钩子的三个参数解析:
to
: 即将要进入的目标的路由对象.from
: 当前导航即将要离开的路由对象.next
: 调用该方法后, 才能进入下一个钩子.
修改代码 src/router/index.js
// 配置路由的相关信息
import VueRouter from 'vue-router'
import Vue from 'vue'
// 导入自定义组件
// import Home from '../components/Home.vue'
// import About from '../components/About.vue'
// import User from '../components/User.vue'
// 通过 Vue.use(插件), 安装插件
Vue.use(VueRouter)
// 创建 VueRouter 对象
const routes = [
{
path: '/',
redirect: '/home'
}, {
path: '/home',
meta: {
title: '首页'
},
component: () => import('../components/Home.vue'),
children: [
{
path: '/home',
redirect: '/home/news'
},
{
// 注意,这里不加斜杠'/'
path: 'news',
component: () => import('../components/HomeNews.vue')
},
{
path: 'message',
component: () => import('../components/HomeMessage.vue')
}
]
}, {
path: '/about',
component: () => import('../components/About.vue'),
meta: {
title: '关于'
}
}, {
path: '/user/:username',
component: () => import('../components/User.vue'),
meta: {
title: '用户'
},
children: [
{
path: '/user/:username/profile',
component: () => import('../components/UserProfile.vue')
}
]
}
]
const router = new VueRouter({
// 配置路由和组件之间的响应关系
routes,
mode: 'history',
linkActiveClass: 'active'
})
router.beforeEach((to, from, next) => {
// 从 from 跳转到 to
console.log(to)
document.title = to.matched[0].meta.title
next()
})
// 将 router 对象传入到 Vue 实例
export default router
afterEach
, 不需要主动调用 next()
函数.用来保存用户点击记录
keep-alive
是 Vue
内置的一个组件,可以 使被包含的组件保留状态,或避免重新渲染。
它们有两个非常重要的属性:
include
- 字符串或正则表达,只有匹配的组件会 被缓存
exclude
- 字符串或正则表达式,任何匹配的组件都不会被缓存
router-view
也是一个组件,如果直接被包在 keep-alive
里面,所有路径匹配到的视图组件都会被缓存:
注意:
配合方法
activated()
设置默认值 和beforeRouteLeave()
记录默认值 可以实现页面的访问记录
但是,要注意。activated不能单独使用,必须在 keep-Alive 里面使用
.
后期,也会有类似的功能要实现。
不过那时,我们会用currentIndex
。很方便