- vue路由安装与基本使用
- vue嵌套路由
- vue动态路由(路由组件传参)
- vue路由重定向和一些其他的路由相关
- 官方手册:https://router.vuejs.org/zh/
一、vue路由安装与基本使用
在项目根文件夹下执行以下命令安装vue-ruoter:
vue add router
执行命令后会有以下提示:
这个提示是让我们配置router的实现模式(hash或者history),这个模式在后面的路由配置中可以设置,只是这里选择的是默认的模式,如果后面的路由配置中不设置就是用这里选择的模式,y表示选择history模式,n选择hash模式,使用hash模式在后面的url中会多出#/。
安装完成以后可在项目根目录下看到新增了router.js文件和views文件夹。
到这里有必要提一下路由是干嘛的(有计算机网络基础的甚至大神者此地绕路),路由一词是用来描述网络数据传输端到端的网络路径范围进程,简单的说就是将数据从一个端口送到另一个端口的路径。而在vue项目中vue-router(路由)就是帮助vue应用找到资源并加载到页面的路径,并且不会触发页面整体刷新重载,而是实现局部刷新。
这里有两个重要的点,在编写代码时遇到url和src不需要添加完整的资源路径,而是通过vue-router来配置路径;其二是不刷新页面,其核心实现是通过hash值或者HTML5的History interface特性来实现。
关于vue-router的实现原理可以参考这篇博客:https://segmentfault.com/a/1190000014822765
关于HTML5的History可以参考这篇博客:https://www.cnblogs.com/roucheng/p/html5api.html
起步示例:
1 //组件模板结构 2 <a href="/">home</a> 3 <a href="/about">about</a> 4 <router-view></router-view> 5 6 //入口js文件 7 import Router from 'vue-router' //导入路由插件 8 import Home from './views/Home' //导入子组件 9 import About from './views/About' //导入子组件 10 11 Vue.use(Router) 12 13 const myRouter = new Router({ //创建路由实例 14 routes:[ 15 { 16 path: '/', 17 component:Home 18 }, 19 { 20 path: '/about', 21 component:About 22 } 23 ], 24 mode: 'history' //配置路由模式,配置history后url上不会多出#/ 25 }) 26 27 new Vue({ 28 router: myRouter, //在vue示例中引入路由实例 29 render: h => h(App) 30 }).$mount('#app')
<a>标签的href属性的路径指向路由实例的path属性;
路由实例中的component属性指示路由path指向的组件;
结构文本中的<router-view>标签标识通过路由配置的连接获取的组件的加载位置(a标签href获取的路由实例配置的path指向的组件在这个标签的位置上显示);
vue实例中router指示配置的路由实例;
注:在上面的示例中虽然实现了vue路由的应用,但是使用的是<a>标签来实现的跳转,这样的实现会导致再加载组件的时候会刷新整个页面,所以再组件模板中需要使用vue提供的自定义标签<router-link>来代替<a>标签实现导航,才能真正的实现vue路由局部更新的效果,所以示例中的模板结构需要修改成:
1 <router-link to="/">home</router-link> 2 <router-link to="/about">about</router-link> 3 <router-view></router-view>
1.使用data数据绑定:
1 <router-link :to="homeLink">home</router-link> 2 3 //在组件内些绑定数据 4 <script> 5 export default { 6 data(){ 7 return { 8 homeLink: '/' 9 } 10 } 11 } 12 </script>
2.通过vue实例的router实例的名称绑定:
1 //:to属性JSON数据会根据模板所在组件绑定的vue实例中的router中的name进行匹配 2 <router-link :to="{name:'about'}">about</router-link> 3 //在router实例中给每个路由申明name属性 4 const myRouter = new Router({ 5 routes:[ 6 { 7 path: '/', 8 name:'home', 9 component:Home 10 }, 11 { 12 path: '/about', 13 name: 'about', 14 component:About 15 } 16 ], 17 mode:'history' 18 })
将router实例从入口js文件中分离出来作为独立模块,然后在入口js文件中引入模块,再在vue实例上绑定这个引入的路由模块:
1 --主入口组件中的模板与数据 2 <template> 3 <div id="app"> 4 <router-link :to="homeLink">home</router-link> 5 <router-link :to="{name:'about'}">about</router-link> 6 <router-view></router-view> 7 </div> 8 </template> 9 10 <script> 11 export default { 12 data(){ 13 return { 14 homeLink: '/' 15 } 16 } 17 } 18 </script> 19 20 --主入口js文件 21 import router from './router' //主入口js文件中引入路由模块 22 new Vue({ 23 router:router, //绑定引入的路由模块这里单独写一个‘router’同等与‘router:router’ 24 render: h => h(App) 25 }).$mount('#app') 26 27 --router模块 28 import Vue from 'vue' 29 import Router from 'vue-router' 30 import Home from './views/Home.vue' 31 import About from './views/About' 32 33 Vue.use(Router) 34 35 export default new Router({ 36 routes:[ 37 { 38 path: '/', 39 name:'home', 40 component:Home 41 }, 42 { 43 path: '/about', 44 name: 'about', 45 component:About 46 } 47 ], 48 mode:'history' 49 })
二、嵌套路由
在vue-cli搭建的项目中是基于模块来构建项目,模块化构建项目就必然会需要在各个模块中应用到模块化的导入导出,需要大量的路径操作,而且组件之间还存在嵌套操作,需要追踪组件链级关系,控制页面局部刷新等一系列的操作。如果在每个组件中去重复的做些工作一是会导致代码冗余,二是会导致难以维护,三是组件的链级关系不好处理。下面通过一些关键的代码来演示嵌套路由的应用:
--src //src文件夹 ----components //组件文件夹 ------ChilComOne //嵌套子组件1文件夹 --------ChilComOne1.vue --------ChilComOne2.vue----Views //示图文件夹:(这里写一级组件) ------ComOne.vue ------ComTwo.vue ----App.vue //主入口组件 ----main.js //主入口文件 ----router.js //路由管理器
主入口组件:
<template> <div id="app"> <div> <router-link to="/comOne">ComOne</router-link> <router-link :to="{name:'comTwo'}">ComTwo</router-link> </div> <router-view></router-view> </div> </template>
主入口文件:
1 import Vue from 'vue' 2 import App from './App.vue' 3 import router from './router' 4 new Vue({ 5 router, 6 render: h => h(App) 7 }).$mount('#app')
路由管理器:
1 import Vue from 'vue' 2 import Router from 'vue-router' 3 import ComOne from './views/ComOne.vue' 4 import ComTwo from './views/ComTwo.vue' 5 6 import ChilComOne1 from './components/chilComOne/ChilComOne1.vue' 7 import ChilComOne2 from './components/chilComOne/ChilComOne2.vue' 8 9 Vue.use(Router) 10 11 export default new Router({ 12 routes: [ 13 { 14 path: '/comOne', 15 name: 'comOne', 16 component: ComOne, 17 children:[ 18 { 19 path: '/comOne/one1', 20 name:'one1', 21 component: ChilComOne1 22 }, 23 { 24 path: '/comOne/one2', 25 name: 'one2', 26 component: ChilComOne2 27 } 28 ] 29 }, 30 { 31 path: '/comTwo', 32 name: 'comTwo', 33 component: ComTwo 34 } 35 ], 36 mode:"history" 37 })
子组件ComOne:
1 <template> 2 <div class="comOne"> 3 <div> 4 <router-link to="/comOne/one1">ChilComOne1</router-link> 5 <router-link to="/comOne/one2">ChilComOne2</router-link> 6 </div> 7 <router-view></router-view> 8 </div> 9 </template>
子组件ComTwo:
1 <template> 2 <div class="comTwo"> 3 我是comTwo 4 </div> 5 </template>
ComOne的子组件ChilComOne1:
1 <template> 2 <div class="one1"> 3 我是ChilComOne1 4 </div> 5 </template>
ComOne的子组件ChilComOne2:
<template>
<div class="one2">
我是ChilComOne2
</div>
</template>
在示例中有一处代码明显合乎router的初衷,就是ComOne组件间模板中的次子组件的to属性值应该不能是这样写:
<router-link to="/comOne/one1">ChilComOne1</router-link> <router-link to="/comOne/one2">ChilComOne2</router-link>
这样的写法虽然实现了我们想要的效果,但是如果是在配置中重写了路径,这时候就必须到模板中做相应的更改,而且还在代码中重复出现了父级组件的路由“/comOne”,更不符合router的配置初衷,这里应该写入name的键值对更符合配置初衷,不然你想想这还指示两级路由,如果在实际开发中出现十几级的路由配置估计这个写法会疯掉,所以可以改写成下面这样的代码:
<router-link :to="{name:'one1'}">ChilComOne1</router-link> <router-link :to="{name:'one2'}">ChilComOne2</router-link>
这个写法你会发现似曾相识,模板语法中的表达式,没错,这个小小的改动也是为后面的动态路由做准备。
.router-link-exact-active
.router-link-active
这两个类名是由分别表示当前选中的路由链接元素、当前路由在url节点上的链接元素。比如用下面这个RUL来分析:
http://localhost:8081/comOne/one1
绑定“/comOne”和“/comOne/one1”的<router-link>会被路由管理器添加上router-link-active这个class值;而绑定“/comOne/one1”的<router-link>元素还会添加上router-link-exact-active这个class值,也就是被选中的当前元素的意思。主要被应用与导航按钮,当前选中和在url路径上的导航按钮可以依据这个类名来切换样式。
这个类名还可以在router实例中配置,使用自定义的类名:
//在router实例中配置 linkExactActiveClass:"exact", //与.router-link-exact-active相对应 linkActiveClass:"active" //与.router-link-active相对应
这样配置后就可以在样式表中使用exact 和active 来设置样式了。
多个组件普配同一个router-view是非常普遍的现象,如果样式不是通过数据来控制,而是直接写在每个组件的style内的话,这时候就会导致样式冲突,解决这个文本很简单:只需要在组件的style标签上添加一个scoped空属性就可以实现每个组件作用各自的样式了。这个问题其实在vue的代码片段那篇博客我就已经解决了,在自动生成代码片段时我就已经设置了scoped属性,如果你的代码片段模板中没有配置可以按照下面的方式手动添加,如果需要配置代码片段可以参考这篇博客:https://www.cnblogs.com/ZheOneAndOnly/p/11213183.html
<style scoped></style>
三、vue动态路由(路由组件传参)
路由传参基本保持两种方式,一种是基于路由的params的方式传参,一种是基于组件的props属性传参方式。params主要还是为路由提供路径参数,props则是路由为组件提供参数。
关于params和props的传参还有一个相互作用的应用,就是当props的值被设置为booler值true时,会将通过params传递的参数转换成props,在vue实例中同样可以使用props:[]来接收params中的key来获取参数。并同时在$route.params中仍然可以获得params完整的数据。
最后props还可以是一个方法,这个方法可以接收一个参数,这个参数就是$route,然后方法返回一个对象,与直接设置props为一个对象的操作完全一致,只是这里为我们揭开了props转换params的值的底层原理。
详细内容可以参考官方手册:https://router.vuejs.org/zh/guide/essentials/passing-props.html
关于router传参解析可以参考这篇博客:https://blog.csdn.net/mapbar_front/article/details/79362602
1 //router-link组件模板代码(仅展示关键实现代码) 2 <router-link 3 tag="li" 4 :to="{ 5 name:'studentsPre', 6 params:{ 7 stuId:item.id, 8 stuClass:item.class 9 } 10 }" 11 v-for="item in studentsInfor" 12 :key="item.id" 13 ></router-link>
路由关键代码:
1 { 2 path: '/studentsPre/:stuClass', 3 name:'studentsPre', 4 component:StudetsAddress, 5 props:true 6 }
通过params从router-link传递的stuClass实现路由的path参数动态切换,从而实现动态路由。
//接上面实例的代码(StudetsAddress组件关键代码) props:['stuId','stuClass']
全部代码:
1 //文件结构 2 src 3 ----components 4 --------StudetsAddress.vue 5 ----views 6 --------Home.vue 7 -----App.vue 8 -----main.js 9 -----router.js
1 <template> 2 <div class="wrapper"> 3 <div class="head"> 4 <h2>成绩系统</h2> 5 班级:{{stuClass}} 6 </div> 7 <ul> 8 <li> 9 <span class="stuId">学生编号</span> 10 <span class="stuName">学生姓名</span> 11 <span class="stuInfo">语文</span> 12 <span class="stuInfo">数学</span> 13 <span class="stuInfo">英语</span> 14 </li> 15 <li 16 v-for="item in stuClassList" 17 :key="item.id" 18 :class="{'assign':stuId == item.id}" 19 > 20 <span class="stuId">{{item.id}}</span> 21 <span class="stuName">{{item.name}}</span> 22 <span class="stuInfo">{{item.language}}</span> 23 <span class="stuInfo">{{item.math}}</span> 24 <span class="stuInfo">{{item.english}}</span> 25 </li> 26 </ul> 27 <router-view></router-view> 28 </div> 29 </template> 30 31 32 <script> 33 export default { 34 props:['stuId','stuClass'], 35 created(){ 36 this.stuAddressList.forEach(element => { 37 if(element.class == this.stuClass){ 38 this.stuClassList.push(element); 39 } 40 }); 41 }, 42 data(){ 43 return { 44 stuClassList:[], 45 stuAddressList:[ 46 { 47 id:'00001', 48 class:'001', 49 name:'张三', 50 language:86, 51 math:95, 52 english:91 53 }, 54 { 55 id:'00002', 56 class:'001', 57 name:'李四', 58 language:86, 59 math:95, 60 english:91 61 }, 62 { 63 id:'00003', 64 class:'001', 65 name:'王五', 66 language:86, 67 math:95, 68 english:91 69 }, 70 { 71 id:'00004', 72 class:'001', 73 name:'小明', 74 language:86, 75 math:95, 76 english:91 77 }, 78 { 79 id:'00005', 80 class:'001', 81 name:'小红', 82 language:86, 83 math:95, 84 english:91 85 }, 86 { 87 id:'00006', 88 class:'002', 89 name:'小军', 90 language:86, 91 math:95, 92 english:91 93 }, 94 { 95 id:'00007', 96 class:'002', 97 name:'小刘', 98 language:86, 99 math:95, 100 english:91 101 }, 102 { 103 id:'00008', 104 class:'002', 105 name:'小邓', 106 language:86, 107 math:95, 108 english:91 109 }, 110 { 111 id:'00009', 112 class:'002', 113 name:'小强', 114 language:86, 115 math:95, 116 english:91 117 } 118 ] 119 } 120 } 121 } 122 </script> 123 124 <style scoped> 125 *{ 126 margin: 0px; 127 padding: 0px; 128 } 129 .wrapper{ 130 100%; 131 } 132 .head,ul{ 133 1000px; 134 margin: 0 auto; 135 background-color: #888; 136 } 137 li{ 138 list-style: none; 139 100%; 140 height: 50px; 141 line-height: 50px; 142 border-bottom: 1px solid #000; 143 } 144 span{ 145 display: inline-block; 146 text-align: center; 147 } 148 .stuId{ 149 100px; 150 } 151 .stuName{ 152 149px; 153 border-left: 1px solid #000; 154 } 155 .stuInfo{ 156 249px; 157 border-left:1px solid #000; 158 } 159 .assign{ 160 background-color: #f00; 161 color: #fff; 162 } 163 </style>
1 <template> 2 <div id="home" class="warpper"> 3 <div class="head"> 4 <h2>学生信息表</h2> 5 </div> 6 <ul id="nav"> 7 <li> 8 <span class="stuId">学生编号</span> 9 <span class="stuName">名字</span> 10 <span class="stuAddress">地址</span> 11 <span class="stuClass">班级</span> 12 </li> 13 <!-- <router-link to="/">Home</router-link> | 14 <router-link to="/about">About</router-link> --> 15 <router-link 16 tag="li" 17 :to="{ 18 name:'studentsPre', 19 params:{ 20 stuId:item.id, 21 stuClass:item.class 22 } 23 }" 24 v-for="item in studentsInfor" 25 :key="item.id" 26 > 27 <span class="stuId">{{item.id}}</span> 28 <span class="stuName">{{item.name}}</span> 29 <span class="stuAddress">{{item.address}}</span> 30 <span class="stuClass">{{item.class}}</span> 31 </router-link> 32 </ul> 33 </div> 34 </template> 35 <script> 36 export default { 37 data(){ 38 return { 39 studentsInfor:[ 40 { 41 id:'00001', 42 class:'001', 43 name:'张三', 44 address:"sdkfjlsdkjlfkjldsjlfkldsjlfjs" 45 }, 46 { 47 id:'00002', 48 class:'001', 49 name:'李四', 50 address:"sdkfjlsdkjlfkjldsjlfkldsjlfjs" 51 }, 52 { 53 id:'00003', 54 class:'001', 55 name:'王五', 56 address:"sdkfjlsdkjlfkjldsjlfkldsjlfjs" 57 },{ 58 id:'00004', 59 class:'001', 60 name:'小明', 61 address:"sdkfjlsdkjlfkjldsjlfkldsjlfjs" 62 }, 63 { 64 id:'00005', 65 class:'001', 66 name:'小红', 67 address:"sdkfjlsdkjlfkjldsjlfkldsjlfjs" 68 }, 69 { 70 id:'00006', 71 class:'002', 72 name:'小军', 73 address:"sdkfjlsdkjlfkjldsjlfkldsjlfjs" 74 }, 75 { 76 id:'00007', 77 class:'002', 78 name:'小刘', 79 address:"sdkfjlsdkjlfkjldsjlfkldsjlfjs" 80 },{ 81 id:'00008', 82 class:'002', 83 name:'小邓', 84 address:"sdkfjlsdkjlfkjldsjlfkldsjlfjs" 85 }, 86 { 87 id:'00009', 88 class:'002', 89 name:'小强', 90 address:"sdkfjlsdkjlfkjldsjlfkldsjlfjs" 91 } 92 ] 93 } 94 } 95 } 96 </script> 97 98 <style scoped> 99 *{ 100 margin: 0px; 101 padding: 0px; 102 } 103 .warpper{ 104 100%; 105 /* background-color: #000; */ 106 } 107 .head{ 108 1000px; 109 height: 150px; 110 margin: 0 auto; 111 border: 1px solid #f0f; 112 background-color: #888; 113 } 114 ul{ 115 1000px; 116 margin: 0 auto; 117 } 118 li{ 119 list-style: none; 120 998; 121 height: 50px; 122 border-right: 1px solid #000; 123 border-bottom: 1px solid #000; 124 border-left: 1px solid #000; 125 background-color: #888; 126 } 127 li span { 128 display: inline-block; 129 color: #fff; 130 line-height: 50px; 131 padding: 0 10px; 132 } 133 .stuId{ 134 146px; 135 } 136 .stuName{ 137 199px; 138 border-left: 1px solid #f0f; 139 } 140 .stuAddress{ 141 399px; 142 border-left: 1px solid #f0f; 143 } 144 .stuClass{ 145 149px; 146 border-left: 1px solid #f0f; 147 } 148 </style>
1 import Vue from 'vue' 2 import Router from 'vue-router' 3 import Home from './views/Home.vue' 4 5 import StudetsAddress from './components/StudetsAddress.vue' 6 7 Vue.use(Router) 8 9 export default new Router({ 10 mode: 'history', 11 base: process.env.BASE_URL, 12 routes: [ 13 { 14 path: '/home', 15 name: 'home', 16 component:Home 17 }, 18 { 19 path: '/studentsPre/:stuClass', 20 name:'studentsPre', 21 component:StudetsAddress, 22 props:true 23 } 24 ] 25 })
1 <template> 2 <div id="app"> 3 <router-link :to="{name:'home'}">home</router-link> 4 <router-view></router-view> 5 </div> 6 </template>
1 import Vue from 'vue' 2 import App from './App.vue' 3 import router from './router' 4 5 Vue.config.productionTip = false 6 7 new Vue({ 8 router, 9 render: h => h(App) 10 }).$mount('#app')
四、vue路由重定向与一些其他路由相关
有时候当打开一个页面时,除了加载当前路由的内容以外,还需要初始加载一个子路由数据作为初始化页面的内容,就可以通过重定向到指定的路由:(路由属性:redirect)
1 { 2 path: '/community', 3 name: 'community', 4 redirect:'/community/academic', //重定向到/community/academic 5 component: Community, 6 children: [ 7 { 8 path: '/community/academic', 9 name: 'academic', 10 component: Academic 11 } 12 ] 13 } 14 }
还有一些情况就是出现修改浏览器上的url导致出现不存在的路由,导致无法正确加载页面,这时候可以通过识别错误数据,可以使用$router.go()方法来实现重定向,也可以使用$router.replace()和$router.push()方法实现重定向跳转。先来简单的介绍以下这三个重定向方法的使用:
1.this.$router.push():跳转到指定的url,这个方法会向history栈添加一个记录,点击后退到上一个页面。
2.this.$router.replace():跳转到指定的url,这个方法是在history栈中替换当前记录,所以点击放回是回退到上上个页面。之前的页面被这个跳转页面替换掉了,所以无法回退到之前的页面。
3.this.$router.go(n):相对当前页面向前或向后跳转多少个页面,雷士window.history.go(n)。n可以为正负数,正数返回上一个页面。负数按照history栈逐级向上跳转。
这里需要注意的是在我前面提出来的问题,当我们使用这三个方法来解决错误路径时,如果出现错误路径就回到之前跳转过来的页面就可以使用go(-1)来解决。如果要跳转到提示错误路径的页面的话最好使用replace(err)来实现,因为replace使用的是替换错误路径的栈,而push(err)是在history栈上添加一个新的err路径,当使用浏览器的回退功能时,又会回到上一个错误路径,导致检测到错误后又进入错误提示页面err。
1 created(){ 2 const questionId = this.$route.params.id; 3 const index = this.questionList.findIndex(item => item.questionId == questionId); 4 if(index == -1){//index为-1时表示未获得正确的路由 5 // this.$router.go(-1); 6 // this.$router.push(err) 7 this.$router.replace({name:'err'}); 8 }else{ 9 this.question = this.questionList[index].title; 10 } 11 }
重定向除了以上的方法以外,还有一个动态返回重定向目标的方法retdirect(to),看代码:
1 { 2 path: '*', 3 redirect(to){ 4 if(to.path == '/'){ 5 return '/home' //当通过网址登入时,重定向到‘/home’页面(默认home首页) 6 }else { 7 return {name : 'err'} //当出现无法识别的路由时跳转到错误提示页面 8 } 9 } 10 }
配置多组件路由:
1 //router.js的配置 2 { 3 path: '/home', 4 name: 'home', 5 components: { //配置多组件路由时components是带了s的复数 6 default: Home, //默认组件,在router-view中不配置name就会自动加载这个组件 7 'academic': Academic //配置自定义组件 8 } 9 } 10 11 //vue的结构 12 <router-view /> <!--匹配默认路由组件--> 13 <router-view name="academic"> <!--通过name匹配自定义路由组件academic-->
通常在配置子路由path的参数时,采用‘/’的配置方式就必须重最开始的父级写到当前路由,路过路由嵌套到了十几层呢?这个写法绝对崩溃,简写的方式就是去掉'/'然后子需要写当前路由节点的字段,router在解释时会逐级添加父级路由和‘/’斜杆标识:(注:起始的第一个父级路由的斜杆‘/’不能省)
1 { 2 path: '/community', 3 ... 4 children: [ 5 { 6 path: '/community/academic', 7 ... 8 children: [ 9 { 10 path: '/community/academic/aaa', 11 } 12 } 13 ] 14 } 15 //简写方式 16 { 17 path: '/community', 18 ... 19 children: [ 20 { 21 path: 'academic', 22 ... 23 children: [ 24 { 25 path: 'aaa', 26 } 27 } 28 ] 29 }
1 { 2 path: '/home', 3 alias: '/b', //这时候访问/b路由可以同样获得/home的组件,但是在rul中依然显示的是/home 4 name: 'home', 5 component: Home 6 }
所谓别名嘛就是多个名字但指代的是同一个人,但是官方的展示的信息还是官方认定的主名称,这与路由别名是同一个道理。