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>