基於vue開發活動頁-路由相關

mykurisu發表於2019-02-28

距離第一篇的釋出過了好久,因為這次真的踩了不少坑..以後還是儘量不要新立專案好了,就跟立了個flag一樣T T

本篇主要內容有以下幾點--

  • 使用vue-router除了mode之外,可能還需要知道base屬性
  • 關於hash與history這兩種模式的選擇
  • 關於微信授權的處理方式
  • 域名限制下如何帶著鐐銬跳舞

使用vue-router除了mode之外,可能還需要知道base屬性

首先介紹一下專案的背景,脫離了背景談需求都在瞎逼逼,本次活動頁是獨立於主站專案(SPA)的新獨立專案,但是由於對微信的強依賴,新專案還是需要掛在主域名下。

這有啥的,讓運維同事幫忙配個nginx把主站的某個路徑指向新專案目錄就好唄~事實上確實是的,一開始配的域名是m.kurisu.fm/my(對..這個是假域名)凡是這個路徑的都直接指向vue專案的目錄,很完美跑起來了,這個也是微信的安全域名所以登入應該也是穩妥的。但是問題來了...它沒法支付...這個域名並不是可支付的域名,所以我們必須要改動域名。

我們的可支付路徑是m.kurisu.fm/pay/:id,只好再麻煩一下同事幫忙配個m.kurisu.fm/pay/my的路徑了,好啦,restart之後一看,涼了。白屏,靜態資源都載入出來了,#app也掛載上了,但是router-view那邊沒有顯示。這是啥玩意?看了下vue-router的文件,發現了這個玩意--應用基路徑也就是說我們應該將router的配置修改一下

let link = ''

if (window.location.host === 'm.kurisu.fm') {
  link = '/pay/my'
}

export default new Router({
  base: link, // 在此設定應用的基路徑
  /*在window.location.host === 'm.kurisu.fm'的情況下,路由會將path為'/'的路徑變為'/pay/my',如果沒有這個配置的話router還是會按'/'的路徑去匹配路由,這樣使用者將會無法正常訪問到頁面*/
  routes: [
    {
      path: '/',
      name: 'JXGlobal',
      component: JXGlobal
    },
    {
      path: '/auth',
      component: Auth
    }
  ]
})
複製程式碼

修改之後我們就可以愉快的進行下面的開發啦~而且還可以悄咪咪的線上上跑微信的各個功能[冷漠]

關於hash與history這兩種模式的選擇

說到了router就必然少不了對mode的討論了,這裡兩種模式我都想說一說,因為實際開發中都TM用到了,最後我得出的結論是--強烈建議使用hash路由

雖然最後我還是用了history的模式由於專案的歷史原因,此次開發還是使用了history的模式,具體原因會在下闡述。

Hash模式路由

vue-router 預設 hash 模式 —— 使用 URL 的 hash 來模擬一個完整的 URL,於是當 URL 改變時,頁面不會重新載入。

使用這種模式的時候,大家會發現url會出現一個很奇葩的'#',但是這個模式除了醜了點,真的沒有啥問題。特別是在微信上面跑單頁應用的時候,簡直是良心之選。

但是為什麼最後我沒有采用這個模式呢?下面我還是想結合一下專案的實際,說說我選擇的原因--

根本原因支付授權目錄限制是最多隻能配置3個,超過3個將無法配置,並且路徑深度不能大於2層用了hash之後我們的url後面都會有'#/'這樣就導致微信支付時匹配的目錄有問題。經過測試,微信貌似之後根據'/'來判斷路徑層級的,所以如果使用了'#/'就代表天然少了一層路徑,權衡之後還是放棄了使用hash模式

History模式路由

關於History模式路由的介紹也已經爛大街了,它主要是依靠HTML5新增的API--history.pushState來實現的,具體實現就不在這裡囉嗦了,日後爭取出一篇router的詳解。

由於種種原因(詳情見上方Hash部分)我選擇了history模式跑這個專案,在這期間也是遇到了一些坑的地方。

因為專案主要是跑在微信上的,總的來說坑的地方就是微信JSSDK對單頁應用的相容差強人意

  • 在安卓與iOS上使用JSSDK會發現有些地方在兩個系統內的表現會莫名的不太一樣,比如對初始location的判斷、支付時同一url會有不同的結果...
    • iOS上微信不會根據history.pushState來更改當前的href這導致的問題可不少,複製連結是錯誤的、授權可能會不成功、支付會有問題;在安卓上則一點問題都沒有。解決方案還是比較粗暴的,在iOS系統中,判定到需要和微信JSSDK互動的頁面就強行重新整理一下以保證互動的正常進行。

    • 測試支付的時候發現一個很有趣的現象,在iOS跑得妥妥的支付調起,到安卓就掛了。在安卓系統中,支付調起了,但是最終付款的彈窗卻沒出現。對比了兩端的支付引數也是一模一樣,問題出在哪呢?掙扎了許久,還是在網上找到了坑友們的回答--安卓調起支付的url不允許'/'結尾,否則支付將會無法正常調起

微信授權處理方式

由於router有兩種模式,所以處理起來也有些許差異,不過總的來說還是一致的。

首先,我們最好有一個獨立的頁面(元件)來處理授權相關的事件,因為實際開發的情況是可能同時存在多種不同途徑的授權,有一個獨立頁面有助於我們很好的與主業務解耦。

然後就是看看微信的文件把授權的連結拼起來,這裡需要注意的是redirect_uri必須要設定過白名單的域名,否則是沒法正常發起授權的。

能正常的重定向到指定的url之後,恭喜你,你已經完成了授權處理的1%一大步啦。重定向的url會帶有code(&state...),我們只需要拿著這個code向後端請求登入資料即可。

在這一步,兩種路由模式的處理方式就有些許不同了。

  • history模式下,我們可以直接通過$route.query來拿到相關的引數,然後可以繼續後面的請求。這是沒有將授權分離的做法,但是在這次的專案中,我還是分離出了一個單獨的授權頁面,這也給自己埋了不少坑。首先是router.push的用法,一開始測試的時候可以順利的從微信授權的頁面回來,但是回來就不動了,即不請求登入介面,也不重定向回原頁面。後面發現是code被沖掉了,罪魁禍首就是我自己router.push,在push的時候沒將引數帶上。另外一個需要注意的是授權完成之後最好使用window.location.href來進行原頁面的回跳,可以避開一些在iOS系統下jssdk的坑。

  • hash模式下,我們則不需要像history模式那樣處處避坑,但是這裡我們會發現無法用$route.query拿到想要的引數,那是因為從微信跳回來之後我們的url變成了這樣-- m.kurisu.fm/?code=1234#/ 發現問題了嗎?'#/'加到了最後,導致$route沒法拿到'#/'之前的code,這裡只需要自己寫個解析window.location.search的工具類就可以輕鬆解決啦。

function urlQuery2Object (search) {
  let result = {}
  search.split('?')[1].split('&').forEach(function (part) {
    let item = part.split('=')
    result[item[0]] = decodeURIComponent(item[1])
  })
  return result
}
複製程式碼

域名限制的情形下如何帶著鐐銬跳舞

由於發起支付的路徑有所限制,活動頁的二級頁面甚至是主頁都不能獨佔一層路由,這該怎麼解決呢?最快捷的解決方案就是通過route.query和watch對route的監聽來實現頁面切換。

最後敲定的url是這樣的m.kurisu.fm/pay/my?:id&:sid其中id是這次活動的id(可能會同時有多個活動使用同一套頁面),sid則是每個活動下的子活動頁面。

<div class="container__content" v-if="activity_init">
    <keep-alive>
        <jx-index :f_content="init_data" v-if="!$route.query.sid"></jx-index>
        <jx-subpage v-else></jx-subpage>
    </keep-alive>
</div>
複製程式碼

在頁面展示上我們只需判斷頁面是否存在sid就可以知道應該展示哪個template,然後對$route的監聽則只需要在子活動頁面的template中進行。

watch: {
    //監聽$route的變動
    '$route' (to, from) {
        if (to.query.sid && (to.query.sid !== from.query.sid)) {
            //對子活動頁的快取,若有快取則優先使用快取資料,並靜默(無loading toast)請求新資料,優化頁面體驗
            if (window.sessionStorage.getItem(String(to.query.id) + String(to.query.sid))) {
                let data = window.sessionStorage.getItem(String(to.query.id) + String(to.query.sid))
                this.column = JSON.parse(data)
                this.page_init = true
                this.GetData(false)
            } else {
                this.GetData(true)
            }
        }
    }
}
複製程式碼

總結

本系列文章主要還是記錄實際開發過程中的一些解決方案,本篇主要是圍繞在路由相關的問題以及微信在其中的相容問題,希望能幫助大家和我規避日後的坑。

往期文章

基於vue開發活動頁-路由相關

相關文章