一、介绍路由
1. 路由就是一组key-value的对关系,多个路由需要经过路由器进行管理
2. 主要应用在SPA(单页面应用)
- 在一个页面展示功能之间的跳转
特点:
- 当跳转时候不进行页面刷新
- 路径随着变化
- 展示区变化但是不开启新的页签
3. 跳转规则:
- 点击指定模块
- 路径变化(加后缀)
- router监测:路径变化
- 展示对应的路径的组件内容
- 如果不存在路由存储的路径,页面就不会进行展示
二、路由使用(十之前使用的都是声明式路由导航)
1. 安装路由
vue-router4(默认版本)只能在vue3中使用
vue-router3 对应 vue2
此处使用vue2进行了解,所以基于vue2进行安装
npm i vue-router@3
2. 插件的引入和使用(入口文件中)
// 引入vue-router import VueRouter from 'vue-router' //使用vue-router Vue.use(VueRouter)
3.配置路由
- 引入安装的路由
- 引入各个组件
- 暴露路由器:此处注意配置routes
//该文件专门创建整个应用中路由器 import VueRouter from "vue-router"; // 引入组件 import About from '../commponents/About' import Home from '../commponents/Home' // 创建并暴露一个路由器 export default new VueRouter({ routes: [ { path: '/about', component:About }, { path: '/home', component:Home }, ] })
4. 声明式导航进行展示(简单实现情况下使用)
- router-link链接包裹需要导航的按钮或者模块(可以理解router-link就是a标签)
- to:表示从当前页面跳转到哪个组件中去:值是路由路径,因为to中的值都是js表达式,所以进行绑定
- active-class:导航被激活时候的样式
在不使用传参和name属性的情况下都不需要对to进行绑定
<router-link class="list-group-item" active-class="active" to="/about">About</router-link>
- 链接路由路径之后进行展示:利用标签放在需要展示的部分
<router-view></router-view>
三、使用路由的注意点
1. 关于组件分类
- 一般(公共)组件:放在components文件夹中
- 路由组件:由路由器渲染的组件叫做路由组件放在pages文件夹中
2. 路由组件的销毁和挂载
- a组件切换成b组件
- a组件被销毁
- b组件完成挂载
3. 路由组件都出现了路由和路由器
- 路由就是自身相关的路由规则(每个路由都不同)
- 路由器在每个路由组件理都有并且相同
//配置代码的挂载之后取得路由 //a路由组件的 window.aboutRoute = this.$route window.aboutRouter = this.$router //b路由组建的 window.homeRoute = this.$route window.homeRouter = this.$router
四、嵌套路由(配置使用区别于基本使用)
1. 基于安装路由并使用插件的基础上进行配置
此处多出来一个新的子级配置项:children
配置每个路由的路径和组件方式还是相同
routes: [ // about和home是一级路由 { path: '/about', component:About }, { path: '/home', component: Home, // 二级路由规则 children: [ { path: 'news', component:News }, { path: 'message', component:Message }, ] }, ]
2. 跳转路由:带爹路径
- 在路由组件中进行链接子级路由
- 注意此处router-link链接跳转的to不能直接写子级路径,需要带父级路径
<router-link class="list-group-item " active-class="active" to="/home/news">News</router-link>
五、路由传参query
- 当进行路由进行跳转的时候,如果某个路由组件需要传递参数,这时候就可以用到参数链接
- 开发中:路由的嵌套一般只会到三四级,不会更多
- 注意:如果一个组件中存在多个消息,每个消息都需要传递出来一个新的展示组件,这时候使用路由组件就比较麻烦
1. 父组件进行遍历获取自身数据并展示在页面
//模板遍历 <li v-for="m in messageList" :key='m.id'> //组件中的数据 data() { return { messageList:[ {id:'001',title:'消息001'}, {id:'002',title:'消息002'}, {id:'003',title:'消息003'}, ] } },
2. 配置路由规则
//此处只展示三级路由规则 { path: 'message', component: Message, // 三级路由进行传参 children: [ { path: 'detail', component:Detail } ] },
3. 创建一个展示组件获取参数
此处是利用$route的query参数进行接收和传递
传递:在父级组件中链接路由路径的时候利用query添加参数
- 字符串写法:注意获取到的参数使用模板字符串进行包裹
- 对象写法:直接布置路径和query参数,一对象方式进行配置数据
注意:此时路由链接展示在li标签中,可以直接获取到遍历的数据,但是to需要进行绑定才能识别js格式代码
<li v-for="m in messageList" :key='m.id'> <!-- 注意此处传递信息通过获取数据进行传递 --> <!-- 通过模板写法将里面字符都变成模板,模板里面参杂的js语用符号包裹 --> <!-- 想要知道获取的是上面遍历数据就得添加: --> <!-- 跳转路由并携带query参数,to的字符传传参写法 --> <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{m.title}}</router-link> <!-- 跳转路由并携带query参数,to的对象写法 --> <router-link :to="{ path:'/home/message/detail', query:{ id:m.id, title:m.title } }">{{m.title}}</router-link> </li>
获取:在获取参数组件中利用$route.query.name进行获取(每个组件中都有自己的route)
//直接利用组件参数进行获取 <ul> <li>消息编号:{{$route.query.id}}</li> <li>消息标题:{{$route.query.title}}</li> </ul>
六、命名路由
1. 给每一个路由起一个名字
routes: [ { name:'guanyu', path: '/about', component:About children: [ { name:'xiangqing', path: 'detail', component:Detail } ] }, ] }, ]
2. 路由通过名字进行链接
- 如果使用字符串格式链接名字时候需要对to进行绑定并将name配置包裹在{}中
- 对象写法进行传递时候需要要配置name属性
//字符串写法 <router-link :to="{name:'guanyu'}">About</router-link> //对象写法 <router-link :to="{ name:'xiangqing', query:{ id:m.id, title:m.title } }">{{m.title}}</router-link>
七、params参数传递
1. 配置路由声名接收params参数
创建新的路由配置:注意path需要绑定传入的数据名字
{ path: '/home', component: Home, children: [ { path: 'news', component:News }, { path: 'message', component: Message, children: [ { name: 'xiangqing', // 占位符声名接收参数 path: 'detail/:id/:title', component:Detail } ] }, ] },
2. 传递参数
注意传递的时候字符串写法直接使用路径方式写法进行写参数(不建议)
建议写成对象格式:如果写成params参数传递对象格式,必须使用name,不能使用path
<!--params参数传参:to的字符串写法--> <router-link :to="`/home/message/detail/666/你好啊`">固定参数跳转</router-link> <!--params参数传参:to的对象写法--> <router-link :to="{ name:'xiangqing', params:{ id:666, title:'你好' } }">{{m.title}}</router-link>
3. 接收参数
$route.params.id $route.params.title
八、路由组件如何读取传递进来的参数
为什么关注以上问题:
- 当传递进来的参数包含多个,那么如何简化接收参数代码
$route.params.id $route.params.title $route.params.id2 $route.params.title2 $route.params.id3 $route.params.title3
props:外部传给路由获取参数组件的方式
首先传递参数,以下都会使用到此参数传递效果(第二种种通过params传递,最后函数式使用query进行传递)
<router-link :to="{ name:'xiangqing', params:{ id:666, title:'你好' } }">{{m.title}}</router-link>
:to="{ path:'/home/message/detail', query:{ id:m.id, title:m.title }
1. 在路由配置中添加新的配置并在获取参数组件处进行接收
注意数据是由路由中props进行控制
children: [ { name: 'xiangqing', path: 'detail', component: Detail, // 第一种写法,值为对象,该对象的所有key-value都会通过props的形式传给detail组件 props: { a:1, b:'hello' } }
//组件中首先接收,然后在模板中直接使用 props:['a','b'],//对象形式 //使用 <li>消息编号:{{a}}</li> <li>消息标题:{{b}}</li>
使用较少,传递的是固定数据
2. 在路由中配置布尔值(params参数传递)
布尔值为真就会把该路由组件接收到的params参数,以props的形式传给组件
children: [ { name: 'xiangqing', // 占位符:声名接收参数 path: 'detail/:id/:title', component: Detail, //第二种写法:布尔值,若布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传给detail组件 props:true }
然后再组件中进行接收,直接接收到的就是绑定的数据
并可以直接使用
//首先进行接收 props:['id','title'],//布尔值形式 //模板中直接使用 <li>消息编号:{{id}}</li> <li>消息标题:{{title}}</li>
3. 路由匹配值中使用参数为函数式(query参数传递)
- 直接写死函数形式
children: [ { name: 'xiangqing', path: 'detail', component: Detail, props() { return{id:'666',title:'你好'} } }
- 可以动态获取其中数据(传递参数$route)
children: [ { name: 'xiangqing', path: 'detail', component: Detail, props($route) { return{ id:$route.query.id, title:$route.query.title} } }
- 优化参数:多层结构赋值
children: [ { name: 'xiangqing', path: 'detail', component: Detail, //结赋值连续写法:先解构参数来源query,再解构id和title props({query:{id,title}}) { return{id,title} } }
此参数不仅动态,还可以路由添加
九、路由组件的历史记录
历史记录保存方式:栈的方式进行保存历史记录
默认push保存方式:指针默认指向最上层
replace保存方式:新的地址总是替换最上层的地址
replace的书写方式:直接再router-link绑定路由处进行添加标签属性
//此处对比了replace的两种写法和绑定路径和名字的方式 <router-link :replace="true" :to="{name:'guanyu'}">About</router-link> <router-link replace to="/home">Home</router-link>
十、编程式路由导航
声明式导航必须借助router-link的to属性进行跳转(router-link就是a标签):必须点击
某种情况不能使用router-link:
- 导航块使用的是button而不是a标签就不能使用
- 定时器倒计时进行展示
1. 使用导航模块按钮进行展示编程式导航
此处使用了路由器($router)的历史记录方式
然后将传递参数和链接路由路径/名字的对象方式相同
- 注意:传递的参数来自于模板被li包裹所遍历的,此处不用注意,只用看方法
//创建代码按钮:里面绑定事件和事件的回调 <button @click="pushShow(m)">push查看</button> <button @click="replaceShow(m)">replace查看</button>
//设置事件回调并传递参数 methods: { pushShow(m){ // 执行push历史记录 this.$router.push({ name:'xiangqing', query:{ id:m.id, title:m.title } }) }, replaceShow(m){ // 执行replace历史记录 this.$router.replace({ name:'xiangqing', query:{ id:m.id, title:m.title } }) } },
十一、路由组件历史纪录的跳转api
前进
<button @click="forward">前进</button> //方法 forward(){ this.$router.forward() },
后退
<button @click="back">后退</button> //方法 back(){ this.$router.back() },
指定步数:此处指定后退2步
<button @click="test">测试go</button></div> //方法 test(){ // 注意此处的go需要使用参数(表示执行几步) this.$router.go(-2) }
十二、缓存路由组件
当路由组件进行切换之后,之前组件就会销毁
如何让路由组件切换之后输入的内容还能保存
1. 保持活跃标签<keep-alive></keep-alive>(放置在最外层链接路由展示位置)
:如果直接使用以上标签,后面会出现的路由组件都会缓存
<keep-alive> <router-view></router-view> </keep-alive>
2. 缓存指定组件:添加新的配置:include
缓存指定路由组件,注意进行绑定,一位内使用的是js格式代码
此处缓存的名字使用的是组件名字,而不是路由名字
<keep-alive :include='["News","Message"]'> <router-view></router-view> </keep-alive>
十三、新的生命周期钩子(激活失活)
挂载组件切换被销毁:实现路由组件缓存之后:
缓存的input组件内容会存在
组件中定时器的内容会持续更新(此时不需要进行持续更新)
使用激活和失活生命周期钩子(放在展示组件内)
activated(切换查看的会激活) / deactivated(切走之后会失活)
在展示组件内将不需要缓存的内容进行激活和失活操作
// 激活 activated() { // console.log('news组件被激活'); this.timer = setInterval(() => { console.log('@'); this.opacity -= 0.01; // 此处注意:js不会计算小数:如果opacity的值小于等于0 if (this.opacity <= 0) this.opacity = 1; }, 16); }, // 失活 deactivated() { // console.log('news组件失活'); clearInterval(this.timer) },
十四、全局路由守卫(权限控制):路由器中进行配置router.beforeEach
路由点击之后会展示不同的组件
但是有的路由组件必须是指定账户或者信息才能进行查看
配置前置路由守卫
- 首先将路由器中的规则进行命名
- 利用beforeEach-api全局前置路由组件守卫(开启就需要进行参数设置)
- 参数包含to,from,next
- to表示准备去哪个组件
- from来自于那个组件
- 暴露路由器
- 执行next方法:继续执行(就是在这里配置相关守卫通过信息)
//命名规则 const router = new VueRouter({} //配置全局前置路由守卫 router.beforeEach((to, from, next) => { next() } //暴露路由器 export default router
前置路由守卫:router.beforeEach中进行判断什么时候放行并继续执行
注意此处使用判断条件是准备去往的路径(此处自行设置)
以及本地用户是否匹配
router.beforeEach((to, from, next) => { if(to.path === '/home/news' || to.path === '/home/message'){ //以上的判断语句也可以直接使用路由名字进行配置 // if (to.name === 'xinwen' || to.path === 'xiaoxi') if (localStorage.getItem('school') === 'xlf') { next() } else { alert('学校名不对,无权限查看') } } }
路由跳转条件如果有很多代码冗余问题(解决)
路由中配置路由元属性:meta对象中(key值为true表示路由是否需要路由权限判断)
- 哪个路由中需要就放在哪个路由中:调整值为true
meta: { isAuth: true, title:'详情'},
然后在守卫中配置判断条件就根据meta中isAuth进行判断是否需要权限
router.beforeEach((to, from, next) => { if(to.meta.isAuth){ //以上的判断语句也可以直接使用路由名字进行配置 // if (to.name === 'xinwen' || to.path === 'xiaoxi') if (localStorage.getItem('school') === 'xlf') { next() } else { alert('学校名不对,无权限查看') } } }
后置路由守卫,没有next():使用在title属性和每个组件对应
每次路由切换之后被调用
- 每个要切换的路由中配置了路由元信息:meta:{title:'自己命名'}
- 然后路由守卫中就可以通过meta进行获取
- 进行判断:解决页面初始没有名字问题(也可以直接在html主要进行修改)
router.afterEach((to, from) => { document.title = to.meta.title || '路由守卫系统' })
十五、独享路由守卫(beforeEnter)
某一个路由单独需要守卫
直接在路由中进行配置(放在需要进入的路由组件原则内)
beforeEnter: (to, from, next) => { if (to.meta.isAuth) { if (localStorage.getItem('school') === 'xlf') { next() } else { alert('学校名不对') } } else { next() } }
如果还需要进行全局后置路由守卫,重新打开
router.afterEach((to, from) => { document.title = to.meta.title || '路由守卫系统' })
十六、组件内路由守卫
-
beforeRoteEnter:通过路由规则进入该组件时候调用
-
beforeRoteLeave:通过路由规则离开此组件时候调用
顾名思义,在组件种单独设置路由守卫:
都包含三个参数,每次执行当前跳转需求之后就添加next()继续往下
添加权限设置组件内路由守卫
注意前提就是路由规则配置了meta还有本地存储的信息是否合适
beforeRouteEnter (to, from, next) { if (to.meta.isAuth) {//控制是否需要权限:注意需要在路由配置中设置路由基本配置项meta // 限制权限 if (localStorage.getItem('school') === 'xlf') { next() } else { alert('学校名不对,无权限查看') } } else { // 不是指定位置直接可以获取组件 next() } }, // 通过路由规则进入该组件 beforeRouteLeave (to, from, next) { // 独享离开路由守卫 console.log('app--- beforeRouteLeave'); next() }
十七、路由器的两种工作模式
# :哈希
#包括后面的路径是哈希值:不会随着http请求发给服务器
比如:当访问一个服务器就会存在返回信息,但是哈希值存在就不会发送给服务器(只会获取到哈希值前的路径给服务器)
默认开启哈希工作模式:
可以在路由器配置中添加属性mode:默认hash,
如果修改则可以使history:mode:history
const router = new VueRouter({ mode:'hash' //各种路由 }
区别
两种工作模式呈现在页面路径的区别
hash的兼容性略强(并且在上线之后可以通过路径进行跳转路由)
十八、上线打包
当代码完成之后执行打包然后交给后端首选需要打包
打包:通过package.json中的buil语句运行
执行之后机会将工程文件生成一个dist文件夹中,里面都是html,css,js等文件
"build": "vue-cli-service build",
上线:将打包生成的文件执行部署
- 根据express框架生成一个服务器
// 首先合法包 npm init //包命名 随便自己命名 //安装express npm i express //创建一个server.js服务器文件 //引入express const express = require('express') const history = require('connect-history-api-fallback'); const app = express() app.use(history()) app.use(express.static(__dirname+'/static')) app.get('/person', (req,res) => { // 函数体 res.send({ name: 'tome', age:18 }) }) app.listen(5005, (err) => { if(!err) console.log('服务器启动成功了'); })
- 将静态资源dist文件中所有我呢见放在服务器包的static/public文件夹中
- 注意history模式通过路径跳转路由时候就会404
- 如果hsah进行通过路径跳转就不会404
如何即使用history又可以通过路径进行跳转
后端人员服务器进行制作:
connect-history-api-fallback - npmProvides a fallback for non-existing directories so that the HTML 5 history API can be used.. Latest version: 2.0.0, last published: a year ago. Start using connect-history-api-fallback in your project by running `npm i connect-history-api-fallback`. There are 1401 other projects in the npm registry using connect-history-api-fallback.https://www.npmjs.com/package/connect-history-api-fallback
- 根据文档提示:首先安装
npm install --save connect-history-api-fallback
- 然后引入并应用在静态资源前面
//引入 const history = require('connect-history-api-fallback'); //使用 app.use(history())