Vue Router
一、URL 组成部分
路由就是根据一个地址找到对应的页面。
url组成部分:一个完整的URL由几部分组成?
https://www.icketang.com:443/static/img/banner_news.jpg?color=red&num=100#title
协议 https://
域名 www.icketang.com
端口号 :443
路径 /static/img/
文件名 banner_news.jpg
搜索词 ?color=red&num=100
哈希 #title
一个url由七部分组成,前三个部分的改变会导致跨域文件。前六个部分的改变会导致浏览器端向服务器端发送新的请求。只有hash的改变不会向服务器端发送新的请求,因此前端路由就是基于hash实现的。
二、 前端路由的实现
由于hash的改变不会向服务器端发送新的请求,因此我们可以监听hash的变化(通过hashchange事件),根据不同的hash渲染不同的页面(通过location.hash获取当前的hash值)。
这种不向服务器端发送请求,而实现切换页面的功能,就是单页面应用程序(SPA:single pageapplication)。
单页面应用程序就是基于前端的hash路由实现的。特点就是:快。
01 模拟前端路由的实现.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h3>{{msg}}</h3>
<input type="text" v-model="msg">
<hr>
<component :is="page"></component>
</div>
<script src="./dist/00.js"></script>
</body>
</html>
00 .js
import Vue from 'vue';
// 定义组件
let Home = Vue.extend({
template:`
<div>
<h1>Home page</h1>
</div>
`
})
let List = Vue.extend({
template:`
<div>
<h1>list page</h1>
</div>
`
})
let Detail = Vue.extend({
template:`
<div>
<h1>detail page</h1>
</div>
`
})
let app = new Vue({
el:"#app",
data:{
msg:"hello page",
page:'home'
},
components:{
'home':Home,
'list':List,
'detail':Detail
}
})
// 定义路由方法
function router(){
// 获取页面名称
let page = location.hash.slice(2);
// console.log(page);
// 修改数据
app.page = page;
}
// 监听 hash 的变化
// window.addEventListener('hashchange',()=>{
// console.log("111");
// })
window.addEventListener("hashchange",router)
// 执行路由的方法
router()
效果图
三、Vue 路由的实现
vue为了让我们更方便的使用路由,提供了路由模块:vue-router
使用路由分成六步
第一步 安装路由:Vue.use方法安装
第二步 创建组件对象:定义 Vue.extend 的参数对象(简化了对组件的定义)
第三步 定义路由规则:是一个数组,每一个成员代表一条规则
name:代表名称
component:代表渲染的组件,
path:匹配规则(与express类似)
静态路由:一个规则对应一个页面地址,
如:/home/search,
匹配:/home/search,
不匹配:/home, /home/search/1, /list/search
动态路由:一个规则对应多个页面地址
如:/list/:page
匹配:/list/1, /list/100, /list/demo,
不匹配:/list, /list/1/2, /home/1
第四步 实例化路由,new Router({ routes })。通过routes属性传递路由规则
第五步 在vue实例化对象中,注册路由,通过router属性注册。
第六步 在模板中,通过router-view组件,定义路由的渲染位置。
01 vue 中的 Router.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<hr>
<!-- 第六步 渲染页面 -->
<router-view></router-view>
</div>
<script src="./dist/01.js"></script>
</body>
</html>
01 .js
import Vue from 'vue';
import Router from 'vue-router';
// 第一步安装 路由
Vue.use(Router);
// 第二步定义组件
// 之前是
// let Home = Vue.extend({template:`<h1>Home page</h1>`})
// 简化了定义
let Home = { template:'<h1>Home page</h1>' }
// 列表
let List = { template:'<h1>List page</h1>' }
// 详情页
let Detail = { template:'<h1>Detail page</h1>' }
// 第三步 定义路由规则
let routes=[
// 每一个成员是一个对象,代表一个规则
{ name:'home',path:'/home',component: Home },
// 列表页
{ name:'list',path:'/list/:page',component: List },
// 详情页
{ name:'detail',path:'/detail/:page',component:Detail }
]
// // 第四步进行实例化
let router = new Router({ routes })
let app = new Vue({
el:'#app',
// 第五步 注册路由
// router:router,
router,
data:{
msg:"hello",
}
})
效果图
四、路由数据
当我们在vue中注册了路由之后,每一个组件都会具有两个属性:$route, $router
$router 表示路由实例,包含一些切换路由的方法
push 进入一个新页面
replace 替换当前的页面
back 返回上一个页面
forward 进入下一个页面
go 返回第一个页面
$route 存储了路由相关数据
路径,名称,query,动态路由数据(params)等等
注意:在hash策略下,hash属性代表的是第二个#后面的内容,
由于这些数据都设置了特性,因此既可以在模板中使用,也可以在js中使用。
五、props
我们在定义路由规则的时候,可以传递props属性,属性值有两种情况
第一种:属性值是true
会将动态路由数据传递给组件
第二种:属性值是函数
参数是$route数据对象
返回值表示给组件传递的数据
我们在组件中,通过props属性去接收这些数据(类似父组件向子组件通信)
六、默认路由
我们让path匹配 *。既可以定义默认路由
注意:由于*匹配的很广,因此通常定义在最后面。
默认路由:当前地址没有匹配的规则,就会渲染默认路由定义的组件。
七、路由重定向
我们通过path定义匹配的规则
我们通过redirect属性定义重定向的路径
当有与path匹配的路径就会重定向到新的路径。
bug:当重定向的时候,携带query,hash等数据,会导致路由对象解析错误。
注意:工作中,做路由重定向的时候,不要携带query等其它数据。
02 路由数据 + 默认路由 + 路由重定向.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<hr>
<router-view></router-view>
</div>
<script src="./dist/02.js"></script>
</body>
</html>
02 .js
import Vue from 'vue';
import Router from 'vue-router';
// 第一步安装
Vue.use(Router);
// 第二步定义组件
let Home = { template:'<h1>home page</h1>' };
let List = {
// 接收数据
props:['page',"path","hash","color"],
template:`
<div>
<h1>list page</h1>
<hr>
<h5>获取的参数----fullPath:{{$route.fullPath}} ---- path :{{$route.path}} --- hash:{{ $route.hash}} ---color: {{$route.query.color}}</h5>
<!-- 对上面的打点语法进行简化 -->
<h6>page:{{page}} ---- path:{{path}} --- hash:{{hash}} --- color:{{color}}</h6>
</div>
`,
created() {
console.log(this,"list")
},
}
let Detail = { template:'<h1>detail page</h1>' }
// 第三步 定义路由规则
let routes =[
{ name:'home' ,path:'/home',component:Home },
{
name:'list',
path:'/list/:page',
component:List,
// 值为布尔值 只传递动态路由参数
// props:true,
// 传递更多的数据
props(route){
console.log(route,"route");
return {
page:route.params.page,
path:route.path,
hash:route.hash,
color:route.query.color
}
}
},
{ name:'detail',path:'/detail/:id',component:Detail},
// 重定向
// { path:'/dazhaxie',redirect:'/detail/dazhaxie' },
// bug: 携带 query 和 hash 等数据,会出现问题,解析错误
{ path:'/dazhaxie',redirect:'/list/dzx?color=red&num=100#demo' },
// 默认路由
{ path:'*',component:Home }
]
// 第四步实例化
let router =new Router({ routes })
let app = new Vue({
el:"#app",
// 第五步 注册路由
router,
data:{
msg:"hello page"
}
})
// console.log("app",app);
效果图
八、子路由
子路由允许我们在页面的局部切换视图。
使用子路由分成两步
第一步 在父路由模板中,通过 router-view 组件定义子路由渲染位置。
第二步 在父路由规则中,通过children属性定义子路由规则。
是一个数组,每一个成员代表一条规则:path, name, component, redirect, children …
定义规则的要注意:
如果是绝对路径:子路由的路径就是子路由的绝对路径(子)
如果是相对路径:子路由的路径就是父路由路径+子路由的相对路径(父 + 子)
九、 路由策略
vue中默认使用的是hash策略(根据hash的变化,切换页面,实现SPA)
我们想使用path策略,可以通过设置路由实例化对象的mode属性实现
mode: ‘history‘ 此时就是path策略。需要服务器端的配合(重定向)。
由于切换路径会向服务器端发送请求,因此这是一个多页面应用程序
使用多页面应用,要配置服务器
前提 配置 app.js 服务器
和html 文件 同级目录下定义 app.js
// 引入 express
let express = require('express');
// 引入 ejs
let ejs = require('ejs');
// 创建应用
let app = express();
// 更改 ejs 默认拓展名
app.engine('.html',ejs.__express);
// 静态化
app.use('/dist/',express.static('./dist/'));
// 将自定义的 json 数据进行静态化
app.use('/data/',express.static('./data/'));
// 定义 post 请求
app.post('/demo',(req,res)=>{
res.json({ msg:'你好 DaZhaXie' })
})
// 配置路由
// app.get('*',(req,res)=>{
// // 渲染模板
// // 默认是 views 所以这里需要跳出
// // 定义子路由的显示
// // res.render('../03 定义子路由.html')
// // 定义 子路由导航+过渡+滚动条
// // res.render('../04 路由导航+过渡+滚动条.html')
// // 实现路由的守卫
// // res.render('../05 路由守卫.html')
// // 实现 axios
// res.render("../06 axios.html")
// })
app.get('/',(req,res)=>{
// axios get 请求
// res.render("../06 axios get 请求.html")
// axios post 请求
res.render("../07 axios post 请求.html")
})
// 启动服务
app.listen(3000);
注意: 右击 app.js 文件 启动服务器 指令: nodemon app.js
**使用端口号进行访问: 例如: localhost:3000 **
03 子路由 + 路由策略.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<hr>
<!-- 使用 router-link 实现页面的跳转 -->
<router-link to="/home">home</router-link>
<router-link to="/list/20">list</router-link>
<router-link to="/detail/10">detail</router-link>
<router-view></router-view>
</div>
<!-- 相对路径 -->
<script src="./dist/03.js"></script>
<!-- 使用绝对路径 实现页面的跳转 -->
<!-- <script src="dist/03.js"></script> -->
</body>
</html>
03 .js
import Vue from 'vue';
import Router from 'vue-router';
Vue.use(Router);
// 定义组件
let Home = {
template:`
<div>
<h6>home page</h6>
</div>
`
}
let List = {
// 接收数据
props:['page','path','hash','num'],
template:`
<div>
<h6>list page</h6>
<hr>
<!-- 渲染页面 -->
<strong>返回过来的数据--- {{page}} --- {{path}} --- {{hash}} --- {{num}}</strong>
</div>
`
}
let Detail = {
template:`
<div>
<h6>detail page</h6>
<!-- 定义子路由 -->
<router-view></router-view>
</div>
`,
}
// 定义子页面
let Demo = {
template:`
<div>
<ins>demo page</ins>
</div>
`
}
// 第三步定义路由规则
let routes = [
{ name:'home', path:'/home',component:Home },
{
name:'list',
path:'/list/:page',
component:List,
props(route){
// console.log(route,"list route");
// 返回数据
return{
page:route.params.page,
path:route.path,
hash:route.hash,
num:route.query.num
}
}
},
{
name:'detail',
path:'/detail/:id',
component:Detail,
// 定义子路由
children:[
// 定义绝对路径
{ path:'/demo',component:Demo },
// 定义相对路径
{
path:'search/:page',
component:{
template:`
<div>
<em>search page</em>
</div>
`,
}
}
]
},
// 默认显示页面
{ path:'*',component:Home }
]
// 第四步进行实例化
let router = new Router({
routes,
// 在 服务器端启动
// mode:'history'
})
let app = new Vue({
el:"#app",
// 安装 路由
router,
data:{
msg:"hello page"
}
})
也可以 通过 服务器访问 也可以 右击浏览器访问 前提 需要改 路由策略
通过右键的方式 显示的效果图
通过服务器的方式显示的效果图
十、路由导航
为了方便我们切换路由,vue路由为我们提供了路由导航组件:router-link组件
tag 定义渲染的标签(默认是a标签)
渲染成a标签,通过a标签的href属性实现切换
渲染成其它标签,通过js实现切换。
to 定义目标地址,必须要定义的,
即使是hash路由,也不要以#开始。
router-link组件与a标签相比,router-link组件会适配不同的策略
十一、路由过渡
我们切换页面的时候,可以添加过度动画。
在router-view组件的外面,定义transition组件,添加过渡动画。
mode属性定义切换方式
appear属性定义是否在加载的时候引入动画。
十二、监听滚动
vue的路由允许我们在切换页面的时候,改变滚动条的位置。
我们在路由实例化对象中,通过scrollBehavior方法监听页面切换
第一个参数表示当前路由对象
第二个参数表示上一个路由对象
第三个参数表示当前滚动条位置
x表示横向滚动条位置
y表示纵向滚动条位置
返回值表示新的滚动条位置。
04 路由导航 + 过渡 + 滚动条.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<!-- <a href="#/home">home</a>
<a href="#/list/10">list</a>
<a href="#/detail/100">detail</a> -->
<router-link tag="div" to="/home">home</router-link>
<router-link to="/list/10">list</router-link>
<router-link to="/detail/20">detail</router-link>
<hr>
<!-- 添加过渡 -->
<transition name="dzx" appear mode="out-in">
<router-view></router-view>
</transition>
</div>
<!-- <script src="./dist/04.js"></script> -->
<!-- 使用服务器 -->
<script src="/dist/04.js"></script>
</body>
</html>
04 .js
import Vue from 'vue';
import Router from 'vue-router';
// 注意 在这里引入样式的时候会报错 应该加载 package.json
// 引入样式
import './demo.scss'
// 安装路由
Vue.use(Router);
// 定义组件
let Home = { template:'<div class="home"><h6>home page</h6></div>' }
let List = {
// 接收数据
props:['page','path','hash','num'],
template:`
<div class="list">
<h6>list page</h6>
<hr>
<h6>直接渲染元素 ---{{page}} --- {{path}} ---- {{hash}} ---- {{num}}</h6>
</div>
`,
created() {
console.log(this)
},
}
let Detail = {
template:`
<div class="detail">
<h6>detail page</h6>
<!-- 定义子路由 -->
<router-view></router-view>
</div>
`
}
// 定义子路由
let Demo = {
template:`
<div>
<em>demo page</em>
</div>
`
}
// 定义路由规则
let routes = [
{ name:'home',path:'/home',component:Home },
{
name:'list',
path:'/list/:page',
component:List,
// 传递数据
props(route){
return{
page:route.params.page,
path:route.page,
hash:route.hash,
num:route.query.num
}
}
},
{
name:'detail',
path:'/detail/:id',
component:Detail,
// 定义子路由
children:[
// 绝对路径
{ path:'/demo',component:Demo },
// 相对路径
{ path:'search/:page',component:{
template:`
<div>
<p>search page</p>
</div>
`
} }
]
},
// 定义默认页面
{ path:'*',component:Home }
]
// 进行实例化
let router = new Router({
routes,
// 定义路由 策略
// mode:'history',
// 监听滚动条位置
// scrollBehavior (to, from, savedPosition) {
// console.log(to.name,from,savedPosition,"222222");
// if (to.name === 'list') {
// return { y: 200 }
// } else {
// return { y : 0 }
// }
// }
})
let app = new Vue({
el: '#app',
// 安装路由
router,
data: {
msg: 'hello page'
}
})
demo.scss
.home,
.list,
.detail{
height: 1000px;
}
.home{
border: 1px solid green;
}
.list{
border: 1px solid yellow;
}
.detail{
border: 1px solid blue;
}
/* 添加过渡 */
.dzx-enter,
.dzx.line-to{
// 注意 在添加滚动条时 这里不能设置高度
height: 0;
opacity: 0;
}
.dzx-enter-active,
.dzx-live-active{
transition: all 1s;
}
通过右键的 效果图
通过服务器的效果图
十三、路由守卫
路由守卫就是监听路由的切换(改变)。
在vue中有三种方式可以监听路由的改变:
第一种 全局路由守卫
可以监听所有的页面的切换
通过对路由实例化对象定义beforeEach,afterEch等方法,来监听
第一个参数表示当前路由对象
第二个参数表示上一个路由对象
如果是beforeEach有第三个参数,表示next方法,必须要执行,否则看不到新的页面。
第二种 局部路由守卫
可以监听当前页面的路由的切换。
在组件实例化对象中,定义beforeRouteEnter,beforeRouteLeave,beforeRouteUpdate等
方法监听。
第一个参数表示当前路由对象,
第二个参数表示上一个路由对象,
第三个参数表示next方法,必须执行,
第三种 在watch监听器中,监听$route数据的改变。
第一个参数表示当前的路由对象,
第二个参数表示上一个路由对象
注意:
为了页面载入的时候可以被监听,可以配合局部路由守卫一起使用。
为了让页面消失的时候也可以监听路由,可以配置keep-alive组件使用。
05 路由守卫.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<hr>
<router-link to='/home'>home</router-link>
<router-link to='/list/10'>list</router-link>
<router-link to='/detail/20'>detail</router-link>
<hr>
<transition name="dzx" appear mode="out-in">
<!-- 防止组件被销毁 -->
<keep-alive>
<router-view></router-view>
</keep-alive>
</transition>
</div>
<!-- 相对路径 -->
<!-- <script src="./dist/05.js"></script> -->
<!-- 绝对路径 -->
<script src="dist/05.js"></script>
</body>
</html>
05 .js
import Vue from 'vue';
import Router from 'vue-router';
// 引入样式
import './demo.scss';
Vue.use(Router);
// 定义组件
let Home = { template:'<div class="home"><h6>home page</h6></div>' }
let List = {
// 接收数据
props:['page','path','hash','num'],
template:`
<div class="list">
<h6>list page</h6>
<hr>
<h6>直接渲染元素 ---{{page}} --- {{path}} ---- {{hash}} ---- {{num}}</h6>
</div>
`,
// 局部的路由守卫
// beforeRouteEnter (...args) {
// console.log(args,"局部的路由守卫");
// },
// 监听路由变化
beforeRouteEnter (to, from, next) {
// console.log(to,from,next);
// 执行放行函数
next()
},
// watch 监听 路由 变化
watch:{
$route(){
console.log(arguments,"arguments");
}
},
created() {
console.log(this)
},
}
let Detail = {
template:`
<div class="detail">
<h6>detail page</h6>
<!-- 定义子路由 -->
<router-view></router-view>
</div>
`
}
// 定义子路由
let Demo = {
template:`
<div>
<em>demo page</em>
</div>
`
}
// 定义路由规则
let routes = [
{ name:'home',path:'/home',component:Home },
{
name:'list',
path:'/list/:page',
component:List,
// 传递数据
props(route){
return{
page:route.params.page,
path:route.page,
hash:route.hash,
num:route.query.num
}
}
},
{
name:'detail',
path:'/detail/:id',
component:Detail,
// 定义子路由
children:[
// 绝对路径
{ path:'/demo',component:Demo },
// 相对路径
{ path:'search/:page',component:{
template:`
<div>
<p>search page</p>
</div>
`
} }
]
},
// 定义默认页面
{ path:'*',component:Home }
]
// 进行实例化
let router = new Router({
routes,
// 定义路由 策略
mode:'history',
})
// 添加全局的路由守卫
// router.afterEach((...args)=> console.log(args,"全局的路由守卫"))
// router.beforeEach((route,oldRoute,next)=>{
// console.log(route,oldRoute,next);
// // 必须执行放行函数
// next()
// })
let app = new Vue({
el:"#app",
// 注册路由
router,
data:{
msg:'hello page'
}
})
demo.scss
.home,
.list,
.detail{
height: 1000px;
}
.home{
border: 1px solid green;
}
.list{
border: 1px solid yellow;
}
.detail{
border: 1px solid blue;
}
/* 添加过渡 */
.dzx-enter,
.dzx.line-to{
// 注意 在添加滚动条时 这里不能设置高度
height: 0;
opacity: 0;
}
.dzx-enter-active,
.dzx-live-active{
transition: all 1s;
}
通过 右键的方式访问 效果图
** 通过 服务器访问的 效果图**
注意 : 在使用服务器启动的时候 应该先放开 当前 js 文件 路由实例中的 mode:'history"
如果正常右键启动 应该注释此位置