單頁面應用微信分享跳坑指南

kunkuntang發表於2018-08-01

前言

最近在開發的時候遇到了一個微信分享的bug,就是無論你在哪個路徑下的頁面,傳送給朋友後點開都只會跳到專案的首頁。本來微信分享這個只算是一個小功能,也很好解決,但由於專案的特殊性,使得在這個bug解決起來並沒有那麼順手,所以記錄一下備以後翻閱。

坑點

  • Vue單頁面應用,前端通過Hash控制路由——iOS在微信中不能正常地改變瀏覽器的hash值,分享出去的頁面地址被莫名其妙地新增了引數。

  • 微信的安全策略——由於存在js安全域名限制,使得在本地除錯更難。

  • jssdk配置簽名。

跳坑方法

分享地址被奇怪的被帶上了引數

在傳統開發中,路由通常都是在後端完成的,但是在Vue單頁面中,都是通過控制history interface來控制頁面之間的跳轉,在我們的專案中我們使用hash的方式,但是在分享給朋友後卻發現分享地址被加上了一些引數,比如:

我分享出去的地址是:market.lenkuntang.cn/#/home,分享後會變成了market.lenkuntang.cn/?from=singlemessage#/home。這到底會不會影響到我們的分享操作呢?這就要了解vue-router的工作原理了,翻看了一下vue-router的原始碼,發現如下程式碼:

// this is delayed until the app mounts
  // to avoid the hashchange listener being fired too early
  setupListeners () {
    const router = this.router
    const expectScroll = router.options.scrollBehavior
    const supportsScroll = supportsPushState && expectScroll

    if (supportsScroll) {
      setupScroll()
    }

    window.addEventListener(supportsPushState ? 'popstate' : 'hashchange', () => {
      const current = this.current
      if (!ensureSlash()) {
        return
      }
      this.transitionTo(getHash(), route => {
        if (supportsScroll) {
          handleScroll(this.router, route, current, true)
        }
        if (!supportsPushState) {
          replaceHash(route.fullPath)
        }
      })
    })
  }
複製程式碼

hash.js

原來在vue-router初始化的時候,會監聽window物件的hashchange屬性,如想發現瀏覽器的hash值發生變化了,就會呼叫History.transitionTo方法,關鍵就在這個方法會傳入一個getHash方法為作引數,如果在這種地址market.lenkuntang.cn/?from=singlemessage#/home也能正確地拿到正確的hash的話,那我們就可以斷定這種意外對我們的分享是沒有影響的。當我們繼續去看getHash方法,在hash.js往下翻點會找到這個方法的實現:

export function getHash (): string {
  // We can't use window.location.hash here because it's not
  // consistent across browsers - Firefox will pre-decode it!
  const href = window.location.href
  const index = href.indexOf('#')
  return index === -1 ? '' : href.slice(index + 1)
}
複製程式碼

我們可以清楚地知道,當這條地址market.lenkuntang.cn/?from=singlemessage#/home經過getHash之後會直接返回#號後面的字串,也就是 /home,所以可以得出是不會對我們分享的功能有影響的。

iOS在微信環境中瀏覽器地址不變

在Vue-router實現前端控制路由都是通過HTML5 新增的History Interface介面來控制頁面之間的跳轉的,在跳轉的同時通過修改windowloactionhash屬性反映回瀏覽器的地址,但是當遇到iOS時卻意外地發現這個hash屬性一直沒有被改變,導致每次分享出去的地址都是首頁,在網上一查發現這原來是個通病,解決的方法就是引入微信的JsSDK來手動控制分享的地址。

引入JsSDK所帶來的問題

在引入了JsSDK後,首先要對它進行配置,相關配置項如下:

wx.config({
    debug: true, // 開啟除錯模式,呼叫的所有api的返回值會在客戶端alert出來,若要檢視傳入的引數,可以在pc端開啟,引數資訊會通過log打出,僅在pc端時才會列印。
    appId: '', // 必填,公眾號的唯一標識
    timestamp: , // 必填,生成簽名的時間戳
    nonceStr: '', // 必填,生成簽名的隨機串
    signature: '',// 必填,簽名
    jsApiList: [] // 必填,需要使用的JS介面列表
});
複製程式碼

說明一下這裡的引數分別從哪裡來,appId是從微信公眾號裡獲取的,timestampnonceStr還有signature是從伺服器中返回的。jsApiList可以在所有JS介面列表中找到。

注:timestampnonceStr其實是可以在前端生成然後傳給伺服器再參與簽名的計算的,但一般在考慮到安全原因,timestamp, nonceStr這些引數應該從伺服器返回回來(因為它參與了簽名的計算)。

注意:這裡的傳入的隨機字串欄位nonceStr駝峰命名!!!

然後就是引入JsSDK中遇到最大的問題——簽名問題,要正確地實現使用JsSDK,在伺服器端首先要集齊這四種元素:

  • noncestr(隨機字串)
  • jsapi_ticket(通過微信介面獲得的ticket)
  • timestamp(時間戳)
  • url(當前網頁的URL,不包含#及其後面部分)

然後把這些元素按字典序(ASCII 碼從小到大排序)排後使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字串,再對字串進行sha1加密,欄位名和欄位值都採用原始值,不進行URL 轉義,即可得到所謂的簽名。

注意:這裡的傳入的隨機字串欄位noncestr全小寫!!!

最後附上簽名檢驗工具的地址:mp.weixin.qq.com/debug/cgi-b…

還有示例程式碼:demo.open.weixin.qq.com/jssdk/sampl…

得到簽名後再把timestampnonceStrsignature傳回給前端進行JsSDK的初始化配置。

再說計算簽名的URL

這裡再說說參與簽名的url,因為這裡傳過去的是當前見面的URL且不包括#及其後面部分,這對於使用Hash模式的單頁面應用來說是個好訊息,這樣就代表我們只需要在頁面載入時初始化一次後便可以在所有頁面上使用(對於傳統的路徑導航,因為URL變了所以要重新初始化,也就是說要在使用到的JsSDK功能的頁面中都要重新請求後臺介面拿簽名再初始化!!)。所以,一般來說我們通常會在App.vue這個檔案中作JsSDK的初始化操作,當初始化正確後便可在其它頁面上直接使用JsSDK介面的功能。

次外,由於微信存在對JsSDK的使用限定在微信公眾號裡所設定的JS介面安全域名範圍裡,所以對於本地排程用的localhost域名來說是不可行的,直接提示invalid url domain,在這裡有兩種方式可以解決這個問題,一種是通過修改host的方法來實現本地除錯,方法如下:

window系統:

進入系統盤目錄(通常是C盤): C:\Windows\System32\drivers\etc,找到hosts檔案,開啟後檔案末尾新增一條記錄127.0.0.1 market.lenkuntang.cn,這條記錄的意思是當你訪問market.lenkuntang.cn這個地址的時候會重定向到127.0.0.1這個ip地址,從而實現本地除錯的目的。

mac系統

開啟一個finder,然後按快捷鍵command+shift+G,輸入private/etc/hosts回車後就能找到對應的hosts檔案,由於是許可權問題,是無法直接在那個目錄中修改hosts檔案的,所以要把檔案複製到桌面或者其它有修改許可權的目錄,然後開啟後也是類似window一樣在檔案末尾新增一條記錄127.0.0.1 market.lenkuntang.cn,儲存後拖回原目錄確定覆蓋。

另一種是使用騰訊雲的開發者實驗室的線上Web IDE來登入到測試伺服器,然後直接在伺服器上進行修改,線上驗證。但是由於這個Web IDE目前不支援SSH金鑰方式登入,只能用賬號和密碼的方式登入。所以也是有一定的侷限性的。

附上Web IDE工具地址:cloud.tencent.com/developer/l…

點選其中一個教程,然後選擇開始上機下方的使用已有雲主機標籤,在彈出的登入介面中正確填寫你伺服器的IP地址和賬號密碼便可直接登入伺服器內進行相關操作。

登入介面

  • 登入介面

登入成功後的介面

  • 登入成功後的介面

使用微信開發者工具來本地除錯

當我們配置好了所有東西后,開啟瀏覽器我們可以在控制檯的輸出中看到JsSDK的相關資訊,但是我們卻不知道是否可以正確分享,難道我們每次都要使用手機來訪問本地服務來驗證嗎?而且在使用手機來訪問本地服務的時候,使用的是本地電腦的ip地址,這樣去拿到簽名肯定是不對,會報invalid url domain錯誤,當然也可以改手機的hosts,但是這就不是那麼容易改了,安卓的話要root,蘋果的話...算了算了。還是換種方法,這個時候我們應該使用微信開發者工具來進行除錯,微信開發者工具可以模擬微信環境,可以進行微信想著的操作,所以使用這個工具我們就可以愉快地在本地進行除錯啦。

而且,在遇到需要微信登入的頁面時,如何是用普通的瀏覽器來開啟就會跳到微信的授權登入頁,而用開發者工具來開啟則會像手機一樣彈出授權頁:

普通瀏覽器開啟

  • 普通瀏覽器開啟

微信開發者工具

  • 微信開發者工具

總結

通過這幾天對微信分享的研究,總體對微信的JsSDK的使用有了大概的認識和了解,雖然其中也遇到不少的坑和麻煩的地方,但是既然問題出現就只能儘量地去簡化問題再解決它。

相關文章