vue学习的一系列,全部来自于表哥---表严肃,是我遇到过的讲课最通透,英文发音最好听的老师,想一起听课就去这里吧 https://biaoyansu.com/i/hzhj1206
1概述
vue-router是vue的一个库,可以快速的开发一个单页应用;
在导航切换时,页面根本就不刷新,没有整页刷新的概念,所以用户的输入可以被保留下来,不丢失状态,不丢失数据;
不用每切换一次导航就重新拉取一遍数据,只需要取一次数据,就可以一直用;
在网页上最频烦的操作就是点点点,这样页面不刷新,就可以极大的节省前端和后端的资源。
2安装和基本配置
引用vue文件和vue-router的库文件,https://cdn.bootcss.com/vue-router/3.0.6/vue-router.js
例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>路由</title> </head> <body> <div id="app"> <div> <router-link to="/">首页</router-link> <router-link to="/about">关于</router-link> </div> <div> <router-view></router-view> </div> </div> </body> <script src="js/vue.js"></script> <script src="js/vue-router.js"></script> <script> var rounts=[ { path:\'/\', component:{ template:` <div><h1>首页</h1></div> ` } }, { path:\'/about\', component:{ template:` <div><h1>关于</h1></div> ` } } ]; var router=new VueRouter({ routes:rounts }); var app=new Vue({ el:\'#app\', router:router }) </script> </html>
配置说明:
定义一个数组rounts,数组中的每一项都是一个配置,path代表路由的地址,/就表示首页(默认页);可以直接传进一个component,这个component和普通的component一样,该有的功能都可以用;
然后把定义的规则传给构造的路由,var router=new VueRouter();定义一个routes,把数组传给它就可以了;
在new Vue中加一个router属性,把定义的构造器router传进去;
html中,要加一个标签router-link,用to指定地址,再用router-view表示显示的视图,它显示的就是template中定义的内容
3传参及获取传参
vue-router中传参有两种方式,
第一种,User后面加个冒号:/user/:name,在template中通过{{$route.params.name}}来获取。
例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>路由-传参</title> </head> <body> <div id="app"> <div> <router-link to="/">首页</router-link> <router-link to="/about">关于</router-link> <router-link to="/user/大美女">大美女</router-link> <router-link to="/user/小东西">小东西</router-link> </div> <div> <router-view></router-view> </div> </div> </body> <script src="js/vue.js"></script> <script src="js/vue-router.js"></script> <script> var rounts=[ { path:\'/\', component:{ template:` <div><h1>首页</h1></div> ` } }, { path:\'/about\', component:{ template:` <div><h1>关于</h1></div> ` } }, { path:\'/user/:name\', component:{ template:` <div> <p>我叫{{$route.params.name}}</p> <!-- <p>我今年{{$route.query.age}}岁</p> --> </div> ` } } ]; var router=new VueRouter({ routes:rounts }); var app=new Vue({ el:\'#app\', router:router }) </script> </html>
第二种,通过query,就是地址后面跟着?这样,例如:
http://127.0.0.1:8848/vuetest.html?age=20#/,在template中把params换成query就可以了,{{$route.query.age}}
例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>路由-传参</title> </head> <body> <div id="app"> <div> <router-link to="/">首页</router-link> <router-link to="/about">关于</router-link> <router-link to="/user/大美女">大美女</router-link> <router-link to="/user/小东西">小东西</router-link> </div> <div> <router-view></router-view> </div> </div> </body> <script src="js/vue.js"></script> <script src="js/vue-router.js"></script> <script> var rounts=[ { path:\'/\', component:{ template:` <div><h1>首页</h1></div> ` } }, { path:\'/about\', component:{ template:` <div><h1>关于</h1></div> ` } }, { path:\'/user/:name\', component:{ template:` <div> <p>我叫{{$route.params.name}}</p> <p>我今年{{$route.query.age}}岁</p> </div> ` } } ]; var router=new VueRouter({ routes:rounts }); var app=new Vue({ el:\'#app\', router:router }) </script> </html>
4子路由
在上面例子的基础上,想实现“大美女”后面加个/more,就是嵌套的路由,
再加一个children的配置项,代表子路由,也是一个数组,写法和父级的rounts一样,path可以写成more,传参写法也一样,$route.params.name。
然后在父级路由添加链接<router-link>,这里的to的写法有两种方式:
一种是用v-bind,中间动态的部分就用传参的形式写,
:to="\'/user/\'+$route.params.name+\'/more\'",就类似这样的。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>路由-传参</title> </head> <body> <div id="app"> <div> <router-link to="/">首页</router-link> <router-link to="/about">关于</router-link> <router-link to="/user/大美女">大美女</router-link> <router-link to="/user/小东西">小东西</router-link> </div> <div> <router-view></router-view> </div> </div> </body> <script src="js/vue.js"></script> <script src="js/vue-router.js"></script> <script> var rounts=[ { path:\'/\', component:{ template:` <div><h1>首页</h1></div> ` } }, { path:\'/about\', component:{ template:` <div><h1>关于</h1></div> ` } }, { path:\'/user/:name\', component:{ template:` <div> <p>我叫{{$route.params.name}}</p> <router-link :to="\'/user/\'+$route.params.name+\'/more\'">更多</router-link> <router-view></router-view> </div> ` }, children:[{ path:\'more\', component:{ template:` <div> 用户 {{$route.params.name}} 的详细信息:内容内容在这里写很多... </div> ` } } ] } ]; var router=new VueRouter({ routes:rounts }); var app=new Vue({ el:\'#app\', router:router }) </script> </html>
另一种比较简洁,to的值直接写成more,然后给router-link加个属性append,表示追加的意思,这样就是在原来的后面加上一个more了,但是这种写法只能点击一次,再点“更多”,就又会多追加一个/more
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>路由-传参</title> </head> <body> <div id="app"> <div> <router-link to="/">首页</router-link> <router-link to="/about">关于</router-link> <router-link to="/user/大美女">大美女</router-link> <router-link to="/user/小东西">小东西</router-link> </div> <div> <router-view></router-view> </div> </div> </body> <script src="js/vue.js"></script> <script src="js/vue-router.js"></script> <script> var rounts=[ { path:\'/\', component:{ template:` <div><h1>首页</h1></div> ` } }, { path:\'/about\', component:{ template:` <div><h1>关于</h1></div> ` } }, { path:\'/user/:name\', component:{ template:` <div> <p>我叫{{$route.params.name}}</p> <router-link to="more" append>更多</router-link> <router-view></router-view> </div> ` }, children:[{ path:\'more\', component:{ template:` <div> 用户 {{$route.params.name}} 的详细信息:内容内容在这里写很多... </div> ` } } ] } ]; var router=new VueRouter({ routes:rounts }); var app=new Vue({ el:\'#app\', router:router }) </script> </html>
5手动访问和传参
5.1手动访问
想实现,点击某个按钮,button,加个点击事件,每隔一秒去访问一个链接,这样用户只点一次,就自动访问设置的链接了,使用this.router.push,push这个接口就是专门用来访问某个链接的,写在push中的链接就被推到了历史记录中,
例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>手动访问和传参</title> </head> <body> <div id="app"> <div> <router-link to="/">首页</router-link> <router-link to="/about">关于</router-link> <router-link to="/user/大美女">大美女</router-link> <router-link to="/user/小东西">小东西</router-link> <button @click="surf">漫游</button> </div> <div> <router-view></router-view> </div> </div> </body> <script src="js/vue.js"></script> <script src="js/vue-router.js"></script> <script> var rounts=[ { path:\'/\', component:{ template:` <div><h1>首页</h1></div> ` } }, { path:\'/about\', component:{ template:` <div><h1>关于</h1></div> ` } }, { path:\'/user/:name\', component:{ template:` <div> <p>我叫{{$route.params.name}}</p> <router-link to="more" append>更多</router-link> <router-view></router-view> </div> ` }, children:[{ path:\'more\', component:{ template:` <div> 用户 {{$route.params.name}} 的详细信息:内容内容在这里写很多... </div> ` } } ] } ]; var router=new VueRouter({ routes:rounts }); var app=new Vue({ el:\'#app\', router:router, methods:{ surf:function(){ setTimeout(function(){ this.router.push(\'/about\'); setTimeout(function(){ this.router.push(\'/user/大美女\'); },2000); },2000); } } }) </script> </html>
这种手动触发的方式,虽然比较麻烦,但能做到更细粒度的控制,做出来的功能可以很强大,
router-link方式更方便简单,多数情况下还是用router-link
5.2手动传参
在push中直接传一个对象,{name:’user’},这里的name指的是路由的名称,是在上面写规则那里指定的name,
然后传参可以写在params里,也是一个对象params:{name:\'大美女\'}
完整示例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>手动访问和传参</title> </head> <body> <div id="app"> <div> <router-link to="/">首页</router-link> <router-link to="/about">关于</router-link> <router-link to="/user/大美女">大美女</router-link> <router-link to="/user/小东西">小东西</router-link> <button @click="surf">漫游</button> </div> <div> <router-view></router-view> </div> </div> </body> <script src="js/vue.js"></script> <script src="js/vue-router.js"></script> <script> var rounts=[ { path:\'/\', component:{ template:` <div><h1>首页</h1></div> ` } }, { path:\'/about\', component:{ template:` <div><h1>关于</h1></div> ` } }, { path:\'/user/:name\', name:\'user\', component:{ template:` <div> <p>我叫{{$route.params.name}}</p> <router-link to="more" append>更多</router-link> <router-view></router-view> </div> ` }, children:[{ path:\'more\', component:{ template:` <div> 用户 {{$route.params.name}} 的详细信息:内容内容在这里写很多... </div> ` } } ] } ]; var router=new VueRouter({ routes:rounts }); var app=new Vue({ el:\'#app\', router:router, methods:{ surf:function(){ setTimeout(function(){ this.router.push(\'/about\'); setTimeout(function(){ this.router.push({ name:\'user\', params:{ name:\'大美女\' } }); },2000); },2000); } } }) </script> </html>
6命名视图
如果页面中有两个router-view,可以用name属性进行区分,
然后写rounts时,把原来的component换成components,也是一个对象,键就是name,值又是一个对象,在对象里面定义template,例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>路由-传参</title> </head> <body> <div id="app"> <div> <router-link to="/">首页</router-link> <router-link to="/user">用户管理</router-link> <router-link to="/post">文章管理</router-link> </div> <div> <router-view></router-view> <router-view name="sidebar"></router-view> <router-view name="content"></router-view> </div> </div> </body> <script src="js/vue.js"></script> <script src="js/vue-router.js"></script> <script> var rounts=[ { path:\'/\', component:{ template:` <div><h1>首页</h1></div> ` } }, { path:\'/user\', components:{ sidebar:{ template:` <div> <ul> <li>用户1</li> <li>用户2</li> </ul> </div> ` }, content:{ template:` <div> 内容1在这里... </div> ` }, } }, { path:\'/post\', components:{ sidebar:{ template:` <div> <ul> <li>文章1</li> <li>文章2</li> </ul> </div> ` }, content:{ template:` <div> 内容2在这里... </div> ` }, } }, ]; var router=new VueRouter({ routes:rounts }); var app=new Vue({ el:\'#app\', router:router }) </script> </html>
想命名多少个router-view都可以,但不建议命名太多,不好维护,一个页面2-5个比较合适。
7导航钩子
状态和权限的检查,最好在路由层面就检查好,该驳回的驳回,该访问的访问,这样节省资源。而不要再到组件级别去检查,因为页面中组件太多。
例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>导航钩子</title> </head> <body> <div id="app"> <div> <router-link to="/">首页</router-link> <router-link to="/login">登录</router-link> <router-link to="/post">帖子管理</router-link> </div> <div> <router-view></router-view> </div> </div> <script src="lib/vue.js"></script> <script src="js/vue-router.js"></script> <script> var rounts=[ { path:\'/\', component:{ template:` <h1>首页</h1> ` } }, { path:\'/login\', component:{ template:` <h1>登录</h1> ` } }, { path:\'/post\', component:{ template:` <h1>帖子管理</h1> ` } } ]; var router=new VueRouter({ routes:rounts }); var zyx=new Vue({ el:\'#app\', router:router }); </script> </body> </html>
现在想实现,访问“帖子管理”时,会进行检查,
vue中通过router的实例的一个方法beforeEach实现,
参数说明:
to,表示到哪里去
from,表示从哪里来
next,指定接下来要怎么做:
直接next();就是正常执行,
如果next(false);那么所有的路由都不工作了,
还可以传个地址, next(\'/login\');
例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>导航钩子</title> </head> <body> <div id="app"> <div> <router-link to="/">首页</router-link> <router-link to="/login">登录</router-link> <router-link to="/post">帖子管理</router-link> </div> <div> <router-view></router-view> </div> </div> <script src="lib/vue.js"></script> <script src="js/vue-router.js"></script> <script> var rounts=[ { path:\'/\', component:{ template:` <h1>首页</h1> ` } }, { path:\'/login\', component:{ template:` <h1>登录</h1> ` } }, { path:\'/post\', component:{ template:` <h1>帖子管理</h1> ` } } ]; var router=new VueRouter({ routes:rounts }); router.beforeEach(function(to,from,next){ //是否登录 var logged_in=false; //如果没登录并且要访问post if(!logged_in && to.path==\'/post\'){ //转到登录页 next(\'/login\'); }else{ //正常执行 next(); } }); var zyx=new Vue({ el:\'#app\', router:router }); </script> </body> </html>
如果logged_in为false,那么点击帖子管理会跳到登录页,为true时才会正常访问
说白了这就是个中间件,也可称为路由的生命周期。
还可以在访问之后进行操作,用router.afterEach,其参数只有to和from,不大常用。因为表示路由已加载完毕,可以真正运行这个路由下的所有组件了,那么就可以在这里发送一些全局的异步请求,写一些业务逻辑。这里面的内容放到组件的生命周期也可以。
例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>导航钩子</title> </head> <body> <div id="app"> <div> <router-link to="/">首页</router-link> <router-link to="/login">登录</router-link> <router-link to="/post">帖子管理</router-link> </div> <div> <router-view></router-view> </div> </div> <script src="lib/vue.js"></script> <script src="js/vue-router.js"></script> <script> var rounts=[ { path:\'/\', component:{ template:` <h1>首页</h1> ` } }, { path:\'/login\', component:{ template:` <h1>登录</h1> ` } }, { path:\'/post\', component:{ template:` <h1>帖子管理</h1> ` } } ]; var router=new VueRouter({ routes:rounts }); router.beforeEach(function(to,from,next){ //是否登录 var logged_in=true; //如果没登录并且要访问post if(!logged_in && to.path==\'/post\'){ //转到登录页 next(\'/login\'); }else{ //正常执行 next(); } }); router.afterEach(function(to,from){ console.log(\'to:\',to); console.log(\'from:\',from); }); var zyx=new Vue({ el:\'#app\', router:router }); </script> </body> </html>
8元数据及路由匹配
还是上面的登录的例子,如果帖子管理中还有子路由,地址是/post/article,
如果没有登录,那么“post/”后面所有内容,都是不能访问的,
可以使用to的属性matched,意思是匹配了的路由,
例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>元数据及路由匹配</title> </head> <body> <div id="app"> <div> <router-link to="/">首页</router-link> <router-link to="/login">登录</router-link> <router-link to="/post">帖子管理</router-link> </div> <div> <router-view></router-view> </div> </div> <script src="js/vue.js"></script> <script src="js/vue-router.js"></script> <script> var rounts=[ { path:\'/\', component:{ template:` <h1>首页</h1> ` } }, { path:\'/login\', component:{ template:` <h1>登录</h1> ` } }, { path:\'/post\', component:{ template:` <div> <h1>帖子管理</h1> <router-link to="article" append>文章一</router-link> <router-view></router-view> </div> ` }, children:[ { path:\'article\', component:{ template:`<h2>文章一的内容...</h2>` } } ] } ]; var router=new VueRouter({ routes:rounts }); router.beforeEach(function(to,from,next){ //是否登录 var logged_in=false; //如果没登录并且要访问post if(!logged_in && to.matched.some(function(item){ return item.path==\'/post\'; })){ //转到登录页 next(\'/login\'); }else{ //正常执行 next(); } }); var zyx=new Vue({ el:\'#app\', router:router }); </script> </body> </html>
访问/post和/post/article都是跳到登录页
Tips:some方法,
some() 方法用于检测数组中的元素是否满足指定条件(函数提供)。
some() 方法会依次执行数组的每个元素:
如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。
如果没有满足条件的元素,则返回false。
详情:https://www.runoob.com/jsref/jsref-some.html
还有第二种方法,如果页面中有非常多的路由,不可能一个个的都去写匹配,太麻烦,那么可以在路由的配置中来写,可以加一个meta的配置,元数据的意思,可以在meta中自定义一个login_required:true,然后return item.meta.login_required; 这样只要路由加上了这个配置,就能控制登录权限了,不用每一个都去js中判断了。
例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>元数据及路由匹配</title> </head> <body> <div id="app"> <div> <router-link to="/">首页</router-link> <router-link to="/login">登录</router-link> <router-link to="/post">帖子管理</router-link> <router-link to="/test">新加测试</router-link> </div> <div> <router-view></router-view> </div> </div> <script src="js/vue.js"></script> <script src="js/vue-router.js"></script> <script> var rounts=[ { path:\'/\', component:{ template:` <h1>首页</h1> ` } }, { path:\'/login\', component:{ template:` <h1>登录</h1> ` } }, { path:\'/post\', meta:{ login_required:true }, component:{ template:` <div> <h1>帖子管理</h1> <router-link to="article" append>文章一</router-link> <router-view></router-view> </div> ` }, children:[ { path:\'article\', component:{ template:`<h2>文章一的内容...</h2>` } } ] }, { path:\'/test\', meta:{ login_required:true }, component:{ template:` <h1>测试</h1> ` } } ]; var router=new VueRouter({ routes:rounts }); router.beforeEach(function(to,from,next){ //是否登录 var logged_in=false; //如果没登录并且要访问post if(!logged_in && to.matched.some(function(item){ return item.meta.login_required; })){ //转到登录页 next(\'/login\'); }else{ //正常执行 next(); } }); var zyx=new Vue({ el:\'#app\', router:router }); </script> </body> </html>