八、vue基础-router基础、动态参数、组件复用、配置404、嵌套路由、编程式导航、命名视图、重定向、导航守卫
1.基本使用
a.创建一个VueRouter对象:new VueRouter()
b.在VueRouter中,需要传递一个routes参数,这个参数是一个数组类型,数据中存储的是对象,对象中最少有两个属性,一个是path代表的是url,一个是component代表数据更新的组件,代码如下:
let router = new VueRouter({
routes:[
{path:"/",component:index},
{path:"/find",component:find},
{path:"/friend",component:friend}
]
.将router传给Vue
d.把网页中之前的a标签替换成router-link标签
e.使用router-view指定网页中那个地方要被更新。 全部代码如下:
<!DOCTYPE html> <html lang='en'> <head> <meta charset='UTF-8'> <meta name='viewport' content='width=device-width, initial-scale=1.0'> <meta http-equiv='X-UA-Compatible' content='ie=edge'> <script src='vue.js'></script> <script src="vue-router.js"></script> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"> <title>vue-router练习</title> </head> <body> <div id='app'> <nav class="navbar navbar-default"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <router-link to="/" class="navbar-brand">我的音乐</router-link> </div> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"> <router-link to="/">首页</router-link> </li> <li> <router-link to="/find">发现音乐</router-link> </li> <li> <router-link to="/friend">我的朋友</router-link> </li> </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> <div class="container"> <router-view></router-view> </div> </div> <script> var index = Vue.extend({template:"<h1>首页</h1>"}) var find = Vue.extend({template:"<h1>发现音乐</h1>"}) var friend = Vue.extend({template:"<h1>我的朋友</h1>"}) let router = new VueRouter({ routes:[ {path:"/",component:index}, {path:"/find",component:find}, {path:"/friend",component:friend} ] }) new Vue({ el:'#app', // router:router router }) </script> </body> </html>
2.动态路由
1.在url中,通过定义一个参数那么以后url中就可以动态的传递这个参数,语法是:/profile/:参数名
2.在组件中可以通过this.$route.params.参数名拿到,或者是组件中的模块中,可以通过$route.params.参数名拿到
3.this.$route和this.$router的区别:
a.this.$route:代表是当前这个路由的信息集合
b.this.$router:代表是全局的VueRouter对象
代码如下:
<!DOCTYPE html> <html lang='en'> <head> <meta charset='UTF-8'> <meta name='viewport' content='width=device-width, initial-scale=1.0'> <meta http-equiv='X-UA-Compatible' content='ie=edge'> <script src='vue.js'></script> <script src="vue-router.js"></script> <title>动态参数</title> </head> <body> <div id='app'> <ul> <li> <router-link to="/">首页</router-link> </li> <li> <router-link to="profile/123">个人中心</router-link> </li> </ul> <router-view></router-view> </div> <script> let index = Vue.extend({template:"<h1>首页来了</h1>"}) let profile = Vue.extend({template:"<h1>个人中心来了:{{$route.params.userid}}</h1>"}) let router = new VueRouter({ routes:[ {path:"/",component:index}, {path:"/profile/:userid",component:profile}, ] }) new Vue({ el:'#app', router }) </script> </body> </html>
3.组件复用:当使用路由参数时,原来组件的实力会被复用,意味着组件的生命周期钩子不会再被调用。有两种方法解决:
a.监听this.$router属性,通过判断to和from来更新数据
b.使用导航守卫的beforeRouteUpdate方法们也可以获取to和from,但是要记得调用next(),否则页面不会更新
<!DOCTYPE html> <html lang='en'> <head> <meta charset='UTF-8'> <meta name='viewport' content='width=device-width, initial-scale=1.0'> <meta http-equiv='X-UA-Compatible' content='ie=edge'> <script src='vue.js'></script> <script src="vue-router.js"></script> <title>组件复用</title> </head> <body> <div id='app'> <ul> <li> <router-link to="/profile/张三">张三的个人中心</router-link> </li> <li> <router-link to="/profile/李四">李四的个人中心</router-link> </li> </ul> <router-view></router-view> </div> <script> let index = Vue.extend({ template:"<h1>首页来了</h1>" }) let profile = Vue.extend({ template:"<h1>个人中心来了:{{$route.params.userid}}</h1>", mounted(){ console.log(this.$route.params.userid); }, // watch:{ // "$route": function(to,from){ // console.log("to:",to); // console.log("from:",from); // console.log('看看执行么有'); // } beforeRouteUpdate: function(to,from,next){ console.log("to:",to); console.log("from:",from); next() } }) let router = new VueRouter({ routes:[ {path:"/",component:index}, {path:"/profile/:userid",component:profile}, ] }) new Vue({ el:'#app', router }) </script> </body> </html>
4.404配置:
a.前端页面配置:子啊所有路由后面增加一个*的url,让这个url映射到一个404的组件
b.数据不存在的处理:通过访问服务器来判断,可以通过 this.$router.replace("/404"),跳转到404页面
代码如下:
<!DOCTYPE html> <html lang='en'> <head> <meta charset='UTF-8'> <meta name='viewport' content='width=device-width, initial-scale=1.0'> <meta http-equiv='X-UA-Compatible' content='ie=edge'> <script src='vue.js'></script> <script src="vue-router.js"></script> <title>404配置</title> </head> <body> <div id='app'> <router-view></router-view> </div> <script> let index = Vue.extend({ template:"<h1>首页</h1>" }) let aboutus = Vue.extend({ template:"<p>关于我们</p>" }) let profile = Vue.extend({ template:"<p>个人中心:{{$route.params.userid}}</p>", mounted(){ if(this.$route.params.userid !='123'){ this.$router.replace("/404") } }, }) let notfound = Vue.extend({ template:"<p>404页面没有找到</p>" }) let router = new VueRouter({ routes:[ {path:"/",component:index}, {path:"/aboutus",component:aboutus}, {path:"/profile/:userid",component:profile}, {path:"/404",component:notfound}, {path:"*",component:notfound}, ] }) new Vue({ el:'#app', router:router }) </script> </body> </html>
5.路由嵌套(子路由)
a.在大的路由下面,需要子路由切换数据的时候,可以使用路由嵌套
b.定义路由的时候,不需要在routes中单独添加映射,而应该放在父路由的childern中
c.在父路由的组件中,要谈价路由出口
代码如下:
<!DOCTYPE html> <html lang='en'> <head> <meta charset='UTF-8'> <meta name='viewport' content='width=device-width, initial-scale=1.0'> <meta http-equiv='X-UA-Compatible' content='ie=edge'> <script src='vue.js'></script> <script src='vue-router.js'></script> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <title>嵌套路由</title> </head> <body> <div id='app'> <nav class="navbar navbar-default"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">哈哈</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">学习</a> </div> <div class="collapse navbar-collapse" id="bs-example-navber-collapse-1"> <ul class="nav navbar-nav"> <li class ="active"> <router-link to="/">首页</router-link> </li> <li> <router-link to="/user/123">我的主页</router-link> </li> </ul> </div> </div> </nav> <div class="container"> <router-view></router-view> </div> </div> <script> let index=Vue.extend({ template:"<h1>首页</h1>" }) let user=Vue.extend({ template:` <div> <h1>我的主页</h1> <ul class="nav nav-tabs"> <li role="presentation" class="active"> <router-link to="/user/123/users">设置</router-link> </li> <li role="presentation"> <router-link to="/user/123/xiaoxi">消息</router-link> </li> </ul> <div class="container"> <router-view></router-view> </div> </div> ` }) let users=Vue.extend({ template:"<h3>个人中心</h3>" }) let xiaoxi=Vue.extend({ template:"<h3>个人消息</h3>" }) let router=new VueRouter({ routes:[ {path:"/",component:index}, { path:"/user/:userid", component:user, children:[ {path:"",component:users}, {path:"users",component:users}, {path:"xiaoxi",component:xiaoxi}, ] }, ] }) new Vue({ el:'#app', router }) </script> </body> </html>
6.编程试导航
a. this.$router.puth:转到下一个url,会把新转入的url添加到浏览器的history中,push的参数:
字符串:直接就是路径
对象:path和name都可以,但是如果是使用了path,参数要放在path,放在params中没有效果
b. this.$router.replace:用法和push是一样的,区别是替换当前页面
c. this.$router.go:往前、往后
代码如下:
<!DOCTYPE html> <html lang='en'> <head> <meta charset='UTF-8'> <meta name='viewport' content='width=device-width, initial-scale=1.0'> <meta http-equiv='X-UA-Compatible' content='ie=edge'> <script src='vue.js'></script> <script src='vue-router.js'></script> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <title>嵌套路由</title> </head> <body> <div id='app'> <button @click="gotoPost">跳到帖子列表</button> <button @click="gotoProfile">跳到个人中心</button> <button @click="gotoLogin">登录</button> <button @click="gotoNext">下一步</button> <button @click="gotoUp">上一步</button> <router-view></router-view> </div> <script> let post=Vue.extend({ template:"<h1>帖子列表</h1>" }) let profile=Vue.extend({ template:"<h3>个人中心:{{$route.params.userid}}</h3>" }) let login=Vue.extend({ template:"<h3>登录界面,打印$route.query{{$route.query}},打印$route.params{{$route.params}},打印$route.fullPath{{$route.fullPath}}</h3>" }) let router=new VueRouter({ routes:[ {path:"/post",component:post}, {path:"/profile/:userid",component:profile,name:"myprofile"}, {path:"/login",component:login}, ] }) new Vue({ el:'#app', router:router, methods:{ gotoPost(){ this.$router.push("/post") }, gotoProfile(){ // this.$router.push("/profile/123") // this.$router.push({path:"/profile/123"}) // this.$router.push({name:"myprofile",params:{userid:"222"}}) this.$router.replace({name:"myprofile",params:{userid:"222"}}) }, gotoLogin(){ let currentPath = this.$route.fullPath this.$router.push({path:"/login",query:{from:currentPath}}) }, gotoNext(){ this.$router.go(1) }, gotoUp(){ this.$router.go(-1) } } }) </script> </body> </html>
7.命名视图
a.路由名称:可以在定义路由的时候指定name,使用的时候们可以直接传递name值就可以了
b.命名视图(多组件):在一个页面中,可以通过命名视图展示多个组件,有一下步骤:
(1)在定义路由的时候们需要传递components,然后把所有需要展示的路由都放到这个里面,components是一个对象,{name:组件}的映射。
(2)在模板中,就是通过 <router-view name="组件名"></router-view>来实现
代码如下:
<!DOCTYPE html> <html lang='en'> <head> <meta charset='UTF-8'> <meta name='viewport' content='width=device-width, initial-scale=1.0'> <meta http-equiv='X-UA-Compatible' content='ie=edge'> <script src='vue.js'></script> <script src='vue-router.js'></script> <title>命名视图</title> <style> .header{ width: 100%; height: 80px; background: cadetblue; } .body{ display: flex; height: 500px; } .body .sidebar{ width: 200px; background: darkgrey; } .body .main{ flex: 1; background: cyan; } .footer{ height: 100px; background: darksalmon; } </style> </head> <body> <div id='app'> <div class="header"> <router-view name="header"></router-view> </div> <div class="body"> <div class="sidebar"> <router-view name="sidebar"></router-view> </div> <div class="main"> <router-view name="main"></router-view> </div> </div> <div class="footer"> <router-view name="footer"></router-view> </div> </div> <script> let header = Vue.extend({ template:"<div>这个是header部分</div>" }) let sidebar = Vue.extend({ template:"<div>这个是sidebar部分</div>" }) let main = Vue.extend({ template:"<div>这个是main部分</div>" }) let footer = Vue.extend({ template:"<div>这个是footer部分</div>" }) let router = new VueRouter({ routes:[ {path:"/",components:{ header:header, sidebar:sidebar, main:main, footer:footer }} ] }) new Vue({ el:'#app', router }) </script> </body> </html>
8.重定向和别名:
a.重定向:在定义路由的时候,可以加一个redirect参数,用来重定向到另外一个页面。
b.别名:在定义的时候,可以加一个alias参数,用来标识这个url的别名,之后可以通过别名可以访问到这个页面。
<!DOCTYPE html> <html lang='en'> <head> <meta charset='UTF-8'> <meta name='viewport' content='width=device-width, initial-scale=1.0'> <meta http-equiv='X-UA-Compatible' content='ie=edge'> <script src='vue.js'></script> <script src='vue-router.js'></script> <title>重定向和别名</title> </head> <body> <div id='app'> <router-view></router-view> </div> <script> let index = {template:"<h1>首页</h1>"} let login = {template:"<h1>登录</h1>"} let router = new VueRouter({ routes:[ {path:"/",redirect:'/login'}, {path:"/login",component:login,alias:"/test"} ] }) new Vue({ el:'#app', router }) </script> </body> </html>
9.导航守卫-全局导航守卫:在VueRouter上实现的,有两个函数,beforeEach、afterEach
1.beforeEach(to,from,next) to:代表上一个路由。from:代表下一个路由对象。next:代表是控制下一步路由怎么走
next()按照正常的流程来
next("/")就会把之前的路由断掉,走到首页中去
next(false)或者没有调用next() 不会做任务跳转
2.afterEach(to,from) 路由完成后回调
代码如下:
<!DOCTYPE html> <html lang='en'> <head> <meta charset='UTF-8'> <meta name='viewport' content='width=device-width, initial-scale=1.0'> <meta http-equiv='X-UA-Compatible' content='ie=edge'> <script src='vue.js'></script> <script src='vue-router.js'></script> <title>全局导航守卫</title> </head> <body> <div id='app'> <router-link to="/">首页</router-link> <router-link to="/account">我的账户</router-link> <router-link to="/order">我的订单</router-link> <router-link to="/login">登录界面</router-link> <router-view></router-view> </div> <script> // const logined=false const logined=true let index = {template:"<h1>首页</h1>"} let account = {template:"<h1>我的账户</h1>"} let order = {template:"<h1>我的订单</h1>"} let login = {template:"<h1>登录界面</h1>"} var router = new VueRouter({ routes:[ {path:"/",component:index,name:"index"}, {path:"/account",component:account,name:"account"}, {path:"/order",component:order,name:"order"}, {path:"/login",component:login,name:"login"} ] }) router.beforeEach(function(to,from,next){ //next()按照正常的流程来 //next("/")就会把之前的路由断掉,走到首页中去 //next(false)或者没有调用next() 不会做任务跳转 //请求需要授权的路由 const authRouters=['account','order'] //判断有没有下标 if(authRouters.indexOf(to.name)>=0){ //如果没有登录 console.log('name',to.name); if(!logined){ next('/login') }else{ next() } //如果是访问登录页面 }else if(to.name=='login'){ //如果登录了,到首页 if(logined){ next('/') }else{ next() } }else{ next() } }) router.afterEach(function(to,from){ console.log('to',to); console.log('from',from); }) new Vue({ el:'#app', router }) </script> </body> </html>
10.导航守卫--路由导航守卫:beforeEnter:function(to,from,next)
代码如下:
<!DOCTYPE html> <html lang='en'> <head> <meta charset='UTF-8'> <meta name='viewport' content='width=device-width, initial-scale=1.0'> <meta http-equiv='X-UA-Compatible' content='ie=edge'> <script src='vue.js'></script> <script src='vue-router.js'></script> <title>路由导航守卫</title> </head> <body> <div id='app'> <router-link to="/">首页</router-link> <router-link to="/account">我的账户</router-link> <router-link to="/order">我的订单</router-link> <router-link to="/login">登录界面</router-link> <router-view></router-view> </div> <script> const logined=true //const logined=false let index = {template:"<h1>首页</h1>"} let account = {template:"<h1>我的账户</h1>"} let order = {template:"<h1>我的订单</h1>"} let login = {template:"<h1>登录界面</h1>"} var router = new VueRouter({ routes:[ {path:"/",component:index,name:"index"}, {path:"/account",component:account,name:"account"}, {path:"/order",component:order,name:"order"}, {path:"/login",component:login,name:"login",beforeEnter:function(to,from,next){ if(logined){ next("/") }else{ next() } }} ] }) new Vue({ el:'#app', router }) </script> </body> </html>
11.导航路由--组件导航守卫:
beforeRouteEnter:function(to,from,next):当前页面被进入之前调用
beforeRouteUpdate:function(to,from,next):当前页面被复用,参数改变了会调用
beforeRouteLeave(to,from,next):当前页面即将离开调用
代码如下:
<!DOCTYPE html> <html lang='en'> <head> <meta charset='UTF-8'> <meta name='viewport' content='width=device-width, initial-scale=1.0'> <meta http-equiv='X-UA-Compatible' content='ie=edge'> <script src='vue.js'></script> <script src='vue-router.js'></script> <title>组件导航守卫</title> </head> <body> <div id='app'> <router-link to="/">首页</router-link> <router-link to="/account/111">111我的账户</router-link> <router-link to="/account/222">222我的账户</router-link> <router-view></router-view> </div> <script> let index = {template:"<h1>首页</h1>"} let account = { data:function(){ return { username:"zyb" } }, template:"<h1>我的账户</h1>", beforeRouteEnter:function(to,from,next){ console.log('to',to), console.log('from',from), console.log(this.username),//访问不到 next(vm =>{ console.log("username:",vm.username); }) }, beforeRouteUpdate:function(to,from,next){ console.log('to',to), console.log('from',from), next() }, beforeRouteLeave(to,from,next){ let answer = window.confirm("要离开?") if(answer){ next() } } } var router = new VueRouter({ routes:[ {path:"/",component:index,name:"index"}, {path:"/account/:userid",component:account,name:"account"}, ] }) new Vue({ el:'#app', router }) </script> </body> </html>
*导航守卫执行的流程:
1.导航被触发
2.在失活的组件里调用离开守卫
3.调用全局的berofeEach守卫
4.在重用的组件里调用beforeRouteUpdate 守卫
5.在路由配置李调用
6.解析异步路由组件
7.在被激活的组件里调用
8.调用全局的beforeRouteLeave 守卫
9.导航被确认
10.调用全局的beforeEach 钩子
11.触发 DOM更新
12.用创建好的实例调用beforeRouteEnter 守卫中传给next 的回调函数