vue移動端路由切換完整例項

cd-dongzi發表於2018-05-13

vue移動端路由切換完整例項

在寫移動端時,因為需要給頁面做轉場動畫,便有了這次的研究

其中最主要的時以下兩個問題:

  1. 瀏覽器導航欄的切換
  2. IOS 上滑動切換時,會有兩次頁面的轉場動畫,一次自身滑動時進行的切換,隨後觸發我們設定的轉場動畫。

除了上面兩個問題,其餘的操作都是可以在頁面內進行設定,基本都是可控的。主要就是解決上面兩個問題。

可以看下實際寫出來的效果:線上DEMO

1. 瀏覽器導航欄的切換

通過記錄 歷史記錄 來比較判斷前進還是後退

  • 如下例子

A頁面 -> B頁面 -> C頁面

假如我從 A頁面到 B頁面 再到C頁面,歷史記錄就會產生3條

我們用一個陣列表示: ['/a', '/b', '/c']

然後我在通過點選瀏覽器導航欄的後退按鈕, 我便會回到 B 頁面,

這時候我只要判斷是否存在 B頁面, 存在就證明我點的時後退按鈕。

然後只要我後退過, 我就能點選瀏覽器的前進按鈕了。這時候怎麼判斷它到底是前進的呢。

我們可以這樣。

當我們後退到了B頁面,歷史記錄不是還儲存著['/a', '/b', '/c'] 三個路徑嗎

我們可以刪除B頁面後面的路徑,那現在就是 ['/a', '/b']; 如果我們後退到A頁面, 那麼我們儲存的路徑就是['/a']

只要我們點選前進按鈕, 我們去儲存的路徑裡面找, 是不是就找不到路徑了, 那樣就完成了前進判斷。


上面是一種正常的情況。 但是假如我們有點頁面重複進入了呢。

  • 就如以下這種情況

A頁面 -> B頁面 -> C頁面 -> B頁面 -> C頁面

現在走了5步,到達了第二個C頁面, 然後我們後退一步,到達了B頁面 這個時候問題就出來了,我們是刪除第一個B頁面後面的路徑還是刪除第二個B頁面後面的路徑

  1. 我們先試著刪除第二個B頁面的路徑,那麼我們還儲存的路徑就是: ['/a', '/b', '/c', '/b']。 那這個時候我們按照上面正常情況的邏輯來操作. 我點選前進, 然後我去儲存的路徑裡面找,找不到就算前進, 找到證明是後退。 那麼結果顯而易見,我們找到了第一個C頁面,那就這樣就算後退了,但其實我點選的時前進

  2. 那我們試著刪除第一個B頁面後面的路徑,那麼儲存的路徑就是: ['/a', '/b'], 那麼我在點選後退按鈕,這時候他其實會進入C頁面, 我們可以看以下流程圖

    '圖片描述'
    這個時候我們在此點選後退按鈕,就會到C頁面, 但是儲存的路徑裡面 '/c' 已經被我刪除了, 所以判斷出來的是前進。

  3. 如果我們過濾重複的頁面路徑,是不是就會好了呢,其實也是一樣的 假如我們有5個頁面路徑,過濾了2個重複的,只有3個頁面路徑了 那麼我退到第四個路徑的時候是不是就找不到了, 那麼後面兩個頁面都會算作前進。

所以以目前來看,最好的辦法就是記錄每一個頁面,但是每個頁面,都讓他有區別 那麼我們就可以在url上面放一個隨機字串

程式碼實現:

// 當沒有key的時候會進入兩次 beforeEach,我們只需儲存帶key的就行
const updateNavigations = (to) => {
    if (to.query[pathKey]) {
        store.commit('UPDATE_NAVIGATIONS', {path: to.fullPath})
    }
}

router.beforeEach((to, from, next) => {
    let toIndex = store.state.navigations.findIndex(path => path === to.fullPath)
    if (toIndex >= 0) { // 存在該路徑
        let len = store.state.navigations.length-1
        if (toIndex === len) { // 當前路徑是最後一條,證明是同一個頁面
            console.log('refresh') 
        } else { // 後退
            store.commit('UPDATE_ROUTER_DIRECTION', { routerDirection: 'back' }) // 後退標誌
            store.commit('DELETE_NAVIGATION', { index: toIndex }) // 刪除當前路徑後面的路徑
        }
    }else{ // 不存在該路徑
        store.commit('UPDATE_ROUTER_DIRECTION', { routerDirection: 'forward' }) // 前進標誌
        updateNavigations(to) // 儲存該連線
    }

    const query = { ...to.query }
    // 存在就直接next, 防止死迴圈
    if (!query['APP_KEY']) { // 不存在新增key ,再次 next
        query['APP_KEY'] = Math.random().toString(16).substring(2)
        next({ path: to.path, query})
    }else{
        next()
    }
})
複製程式碼

以上程式碼我們就能就url中新增一個 APP_KEY 的隨機串,那樣就算同一個頁面在我們儲存的路徑裡面也是其實是不同的。就可以正常的執行邏輯了 上面就基本解決了瀏覽器導航欄的問題了

2. IOS上的滑動切換

在IOS的網頁上, 是可以左右滑動進行切換,即使你沒有做轉場動畫。 這個時候就會出現一個問題。

  • 還是ABC頁面

A -> B -> C

當我們到達C頁面,然後向左滑動時,滑完他就進入B頁面,但是這這時它還是會進入我們的 beforeEach 這個鉤子函式裡面,執行我們上面的邏輯。 那樣就會觸發我們的轉場動畫。你就會發現執行了兩次切換。

於是我在網上找到了一種方法 fix ios左滑再次執行動畫 #2259

程式碼是這樣的

let touchEndTime = Date.now()

window.addEventListener('touchend', () => {
    touchEndTime = Date.now()
})

router.beforeEach((to, from, next) => {
    if ((Date.now() - touchEndTime) < 377) { // ios滑動切換
        store.commit('UPDATE_ROUTER_DIRECTION', { routerDirection: '' })
    }
})

複製程式碼

上面也很好理解, 就是我們取到手指最後離開的螢幕的那一刻, 然後在到 beforeEach裡面進行比較, 當手指離開螢幕的最後一刻跟我們自己 beforeEach裡面進行的轉場相差小於 337, 就算是IOS的滑動切換

那樣就解決了IOS的滑動切換問題了。

但是IOS的右滑切換時監聽不到手指離開螢幕的那一刻的(也不知道是什麼鬼), 所以IOS的右滑切換,是沒法像上面那樣判斷的。 這個我也沒找到解決辦法, 暫時只能解決IOS左滑返回的切換。

基本上遇到的比較麻煩的兩個點就是上面這兩個點了,其餘都是可以通過監聽事件進行設定,倒也沒什麼難度

線上DEMO演示

github: DEMO原始碼

個人部落格

相關文章