今天來談談vue項目{vue,vue-router,component}三大神將之一的vue-router。做爲咱們先後端分離很重要的實踐之一,router幫咱們完成了SPA應用間的頁面跳轉。前端
而且,配合axios這樣的第三方庫,咱們能夠實現配合後臺接口的攔截器功能。vue
對於一個小型項目而言,router這個文件夾裏面就包含了一個router.js就足夠了,java
可是,當咱們的頁面比較多的時候,咱們就須要分出兩個文件出來:一個定義咱們的路由和組件,另外一個實例化組件,並將路由掛載到vue的實例上。webpack
基本的用法就很少贅述,你們能夠看vue-router的官網,認真過一遍的話,基本使用確定沒什麼問題。ios
這裏有個很是重要的一點就是當咱們去構造VueRouter的實例的時候,傳入的參數的問題。web
import routes from '@/router/router' const router = new VueRouter({ routes // (ES6語法)至關於 routes: routes }) new Vue({ router }).$mount('#app')
若是你這裏引入的不是routes,你就要按照下面的方式來寫。vue-router
import vRoutes from '@/router/router' const router = new VueRouter({ routes :vRoutes }) new Vue({ router }).$mount('#app')
對於咱們的vue項目,咱們基本都是運用webpack打包的,若是沒有懶加載,打包後的文件將會異常的大,形成首頁白屏,延時嚴重,不利於用戶體驗,而運用懶加載則能夠將頁面進行劃分,webpack將不一樣組件打包成不少個小的js文件。須要的時候再異步加載,優化用戶的體驗,換而言之,有的頁面可能100個用戶只有一兩個會進去,何須把流量花在它身上。axios
import App from '@/App.vue' const index = r => require.ensure([], () => r(require('@/pages/index/index')), 'index') export default [{ path: '/', component: App, children: [ { path: '/index', name:'index', component: index }] }]
若是某個組件包含了嵌套路由,咱們也能夠將兩個路由打包到一個js chunk中。後端
// 這兩條路由被打包在相同的塊中,訪問任一路由都會延遲加載該路由組件 const orderUser= r => require.ensure([], () => r(require('@/pages/order/user')), 'order') const payRecord= r => require.ensure([], () => r(require('@/pages/order/payRecord')), 'order')
對於瀏覽器,咱們的router分爲兩種模式。瀏覽器
1.hash模式(默認)
按照一個uri的基本結構來講,hash模式就是在一個基本的URI的片斷進行的處理。若是拋開SPA的話,比較常見的應用場景就是咱們在作pc商城的時候,會有好比說:商品詳情,評論,商品參數這樣的tab切換,就可使用a標籤配合id使用,加上一點運動的特效,效果甚佳。
這也是router默認使用的路由方式。不過,這種方式有一個弊端,就是在接入第三方的支付的時候,咱們傳入一個url給到第三方支付做爲回調地址,可是在支付完成之後,有的第三方支付會把咱們的#做爲一個截取符號,僅保留第一個#符號前面的url內容,後面再添加相應的回調參數。致使支付完成之後沒法跳轉到相應的支付頁面
傳入的url: http://xx.xx.com/#/pay/123 回調後的地址: http://xx.xx.com/pay/123?data=xxxxx%xxxx
2.history模式
還有一種就是history的模式。它是使用h5的history.pushState來完成URL的跳轉的。使用這種方式來處理跳轉的好處就是,url和咱們日常看到的沒有什麼區別。和hash模式做比較的話就是沒有了#。不過使用history模式,咱們在後臺也要去作相應的處理,由於若是直接去訪問一個地址,例如http://www.xxxx.com/user/id的時候,若是後端沒有配置的時候,後端就會返回404頁面。
4.router-link在循環中this.參數名=undefined
<router-link>組件是咱們在view層中須要用到的跳轉組件。它替代了<a>標籤須要作的事情,而且幫助咱們作了更多的事情。
不管是 h5 history 模式仍是 hash 模式,它的表現行爲一致,因此,當你要切換路由模式,或者在 IE9 降級使用 hash 模式,無須任何變更。
在 HTML5 history 模式下,router-link
會守衛點擊事件,讓瀏覽器再也不從新加載頁面。
當你在 HTML5 history 模式下使用 base
選項以後,全部的 to
屬性都不須要寫(基路徑)了。
不過當咱們在v-for的循環中使用了router-link的時候,通常來講,咱們須要取的都是循環裏的值,經過定義的item.xxx就能夠取到。若是說須要取一個咱們在data中定義的值的時候,咱們是經過this.foo來取呢?仍是經過foo來取呢?仍是均可以?
這裏的話,咱們是不能經過this.foo來取的,由於這裏的this,再也不是指向vue的實例了,而是指向了[object Window]。因此用this.foo來取的話,實際上是undefined.
<router-link tag="li" :to="{path:`/user/${item.userID}`}" v-for="(item, index) in userList" :key="index"> //含有固定的值 <p>{{this.foo}}</p>
<p>{{foo}}</p> </router-link>
data(){ return { foo:'bar', } }
初次接觸攔截器這個概念是在java中,經過攔截器,咱們能夠對用戶的登陸狀態進行更加粒度的操做。而對於一個SPA的應用來講,沒有了後臺路由的介入,咱們就須要在前端實現一套本身的登陸狀態的管理機制。
最直觀的一點就是,經過用戶的token來判斷用戶是否登陸?
router.beforeEach((to, from, next) => { const NOW = new Date().getTime(); if (to.matched.some(r => r.meta.requireAuth)) { if(NOW > store.state.deadLine){ store.commit('CLEAR_USERTOKEN') } if (store.state.message.login === true) { next(); } else { next({ path: '/login', query: {redirect: to.fullPath} }) } } else { next(); } })
上面的代碼中,咱們經過vue-router中的全局守衛,在導航觸發的時候大體作了以下幾件事:
(1)判斷導航的頁面是否須要登陸
(2)超過登陸持久期限,清除持久化的登陸用戶token
(3)沒有超過登陸期限,判斷是否登陸狀態
(4)沒登陸,重定向到登陸頁面
可是,僅僅這樣是不夠的。由於用戶直接不正常註銷而直接後臺運行網頁是很正常的事情,這就致使雖然token是存在的,可是對於後臺而言,這個token是無效的,過時的了。因此,咱們須要axios配合後臺給出的狀態碼來完善咱們的攔截器。
import router from '@/router/routes' axios.interceptors.response.use( success => { switch (success .code) { case -100: router.replace({ path: 'login', query: {redirect: router.currentRoute.fullPath} }) console.warn('注意,登陸已過時!') break; } return success; }, error => { switch (error.code) { case 404: console.warn('請求地址有誤或者參數錯誤!') break; } return Promise.reject(error.response.data) });
經過後端給到的登陸過時狀態碼,這裏以-100爲例,咱們能夠用axios的響應攔截器實現,當咱們的token過時的時候,咱們將頁面重定向到登陸頁面去。
在項目中,我有的同事就是一直this.$router.push(...),從開始push到結尾。
碰到有的頁面,好比說,在選擇地址的時候須要知道用戶當前所在的城市,若是沒有的話,就是重定向到城市列表頁面去手動選取。選擇完成之後再回到選擇地址的頁面,若是一直使用push的話,點擊選擇地址的後退時,就會回退到城市列表頁。而後形成頁面間的死循環。
這裏若是使用replace來操做就沒有什麼問題了,問題就是咱們不該該讓城市列表頁出如今咱們的瀏覽歷史裏面。