小程式現在越來越流行,但是公司的很多專案都是用網頁寫的,小程式語法不相容原生網頁,使得舊有專案遷移至小程式代價很高。
小程式之前開放了webview功能,可以說是網頁應用的一大福音了,但是微信的webview有一些坑,這篇文章就是列舉一下我在開發過程中遇到的一些問題以及我找到的一些解決方案。
遇到的問題
openid登入問題
微信webview的使用方法很簡單,只要如下設定src就可以展示具體的網站了。
1 2 3 |
<!-- wxml --> <!-- 指向微信公眾平臺首頁的web-view --> <web-view src="https://mp.weixin.qq.com/"></web-view> |
微信環境裡的很多網頁都是用頁面要實現網站的登入功能,只要把登入的資訊,比如openid或者其他資訊拼接到src裡就好了。
這裡有個問題,公眾號的賬號體系一般是以openid來判斷唯一性的,小程式是可以獲取openid的,但是小程式的openid和原公眾號之類的openid是不一樣的,需要將原先的openid賬號體系升級為unionid賬號體系。
以下是微信對unionid的介紹
獲取使用者基本資訊(UnionID機制)
在關注者與公眾號產生訊息互動後,公眾號可獲得關注者的OpenID(加密後的微訊號,每個使用者對每個公眾號的OpenID是唯一的。對於不同公眾號,同一使用者的openid不同)。公眾號可通過本介面來根據OpenID獲取使用者基本資訊,包括暱稱、頭像、性別、所在城市、語言和關注時間。
請注意,如果開發者有在多個公眾號,或在公眾號、移動應用之間統一使用者帳號的需求,需要前往微信開放平臺(open.weixin.qq.com)繫結公眾號後,才可利用UnionID機制來滿足上述需求。
UnionID機制說明:
開發者可通過OpenID來獲取使用者基本資訊。特別需要注意的是,如果開發者擁有多個移動應用、網站應用和公眾帳號,可通過獲取使用者基本資訊中的unionid來區分使用者的唯一性,因為只要是同一個微信開放平臺帳號下的移動應用、網站應用和公眾帳號,使用者的unionid是唯一的。換句話說,同一使用者,對同一個微信開放平臺下的不同應用,unionid是相同的。
做完以上步驟,就可以呼叫小程式api wx.getUserInfo() 來獲取使用者資訊了,此步驟需要進行後臺資訊解密過程,在此就不再贅述,結合小程式api文件操作就好。
獲取到unioid之後,將unionid資訊拼接到src就可以進行網頁登入操作了(前提是網頁可以用跳轉連結的方式登入,類似公眾號頁面獲取openid的形式)。
webview動態src
微信的webview有個坑的地方,不會動態的監聽src的變化,這就造成了一個問題,要通過改變src實現頁面跳轉就不可以了。
我嘗試了一些方法之後,找到了一個解決方案:
微信webview在頁面load的時候會載入一次webview,我們就利用這個特性來實現動態src問題。
- 首先把要跳轉的連結資訊設定成全域性變數,要改變src的時候,先把要src以’?‘拆分為連結和引數兩部分,存入全域性函式,再呼叫onLoad就可以實現webview重新整理了。
- 頁面跳轉時,我們也需要src的動態重新整理,所以要把連結資訊存入全域性函式;頁面跳轉時,onShow函式會被呼叫,這時候再呼叫一次onLoad就可以了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
data: { url: '', loaded: false } // 小程式js裡的onLoad函式可以寫成這樣 onLoad: function () { this.setData({ url: getApp().globalData.urlToken + '?' + getApp().globalData.urlData }) }, changUrl: function () { getApp().globalData.urlToken = 'https://www.example.com' getApp().globalData.urlToken = 'a=1&b=2' // 直接呼叫onLoad,就會實現src的重新整理 this.onLoad() }, onShow: function () { if (!this.data.loaded) { // 第一次不執行 this.setData({ loaded: true }) return } // 直接呼叫onLoad,就會實現src的重新整理 this.onLoad() } // wxml可以寫成這樣 <web-view src="{{url}}"></web-view> |
支付功能
webview裡面可以通過jssdk來實現一些小程式功能,但不能直接呼叫小程式的支付功能,這時候我們就需要轉變一下策略了:
- 在網頁裡引入微信jssdk
- 在網頁需要發起支付的地方,呼叫跳轉頁面的介面,控制小程式跳轉到小程式的支付頁面(這個要在小程式裡單獨寫的),跳轉的時候,需要把訂單的一些資訊都拼接到連結裡,訂單資訊由後臺返回,需要通過微信支付系統的統一下單介面,具體參看支付文件。
- 跳轉到小程式支付頁面後,由小程式頁面發起支付,支付完成後跳轉回webview頁面,通過之前設定的動態src,控制webview跳轉到特定的頁面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
// 網頁引入jssdk // 網頁發起支付 wx.miniProgram.navigateTo({ // payData由後臺返回,主要是需要統一下單平臺的prepay_id url: '../pay/index?data=' + encodeURIComponent(JSON.stringify(payData)) }) // 微信支付頁面 onLoad: function (option) { let page = this try { let data = JSON.parse(option.data) if (!data || !data.prepay_id) { console.error('支付引數錯誤,請稍後重試', data) } wx.requestPayment({ timeStamp: '' + data.timestamp, nonceStr: data.nonceStr, package: 'prepay_id=' + data.prepay_id, paySign: data.paySign, signType: data.signType, success: function (res) { getApp().globalData.urlToken = `paySuccess.html` // 支付成功 getApp().globalData.urlData = 'data=paySuccessData' wx.navigateTo({ url: '/page/home/index', }) }, fail: function (res) { getApp().globalData.urlToken = `payError.html` // 支付失敗 getApp().globalData.urlData = 'data=payErrorData' wx.navigateTo({ url: '/page/home/index', }) }, complete: function (res) { } }) } catch (e) { console.error('支付錯誤', e) } } |
分享功能
小程式直接分享的webview所在的頁面,如果需要加上頁面引數,那我們就需要處理一下了。
- webview內是不能直接發起分享的,需要先用wx.miniProgram.postMessage介面,把需要分享的資訊,推送給小程式;推送給小程式的資訊不是實時處理的,而是使用者點選了分享按鈕之後,小程式才回去讀取的,這就要求每個需要分享的頁面再進入的時候就發起wx.miniProgram.postMessage推送分享資訊給小程式。
- 小程式頁面通過bindmessage繫結的函式讀取post資訊,分享的資訊會是一個列表,我們取最後一個分享就好,把分享資訊處理好,存到data裡面以便下一步onShareAppMessage呼叫。
- 使用者點選分享時,會觸發onShareAppMessage函式,在裡面設定好對應的分享資訊就好了。
- onload函式有一個option引數的,可以讀取頁面載入時url裡帶的引數,這時要對原先的onload函式進行改造,實現從option裡讀取連結資訊。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
// 網頁wx.miniProgram.postMessage wx.miniProgram.postMessage({ data: { link: shareInfo.link, title: shareInfo.title, imgUrl: shareInfo.imgUrl, desc: shareInfo.desc } }) // 小程式index wxml設定 <web-view src="{{url}}" bindmessage="bindGetMsg"></web-view> // 小程式index js bindGetMsg: function (e) { if (!e.detail) { return } let list = e.detail.data if (!list || list.length === 0) { return } let info = list[list.length - 1] if (!info.link) { console.error('分享資訊錯誤', list) return } let tokens = info.link.split('?') this.setData({ shareInfo: { title: info.title, imageUrl: info.imgUrl, path: `/page/index/index?urlData=${encodeURIComponent(tokens[1])}&urlToken=${tokens[0]}` } }) }, onShareAppMessage: function (res) { if (res.from === 'button') { // 來自頁面內轉發按鈕 console.log(res.target) } let that = this return { title: that.data.shareInfo.title, path: that.data.shareInfo.path, imageUrl: that.data.shareInfo.imageUrl, success: function (res) { // 轉發成功 }, fail: function (res) { // 轉發失敗 } } }, onLoad: function (option) { if (option.urlToken) { getApp().globalData.urlToken = option.urlToken } if (option.urlData) { getApp().globalData.urlData = option.urlData } this.setData({ url: getApp().globalData.urlToken + '?' + getApp().globalData.urlData }) }, |
掃描普通二維碼跳轉特定頁面
除了分享功能之外,小程式還可以通過配置,實現掃描普通二維碼跳轉特定頁面的功能。
以下是微信對此功能的介紹
為了方便小程式開發者更便捷地推廣小程式,相容線下已有的二維碼,微信公眾平臺開放掃描普通連結二維碼跳轉小程式能力。
功能介紹
普通連結二維碼,是指開發者使用工具對網頁連結進行編碼後生成的二維碼。
線下商戶可不需更換線下二維碼,在小程式後臺完成配置後,即可在使用者掃描普通連結二維碼時開啟小程式,使用小程式的功能。
對於普通連結二維碼,目前支援使用微信“掃一掃”或微信內長按識別二維碼跳轉小程式.二維碼規則
根據二維碼跳轉規則,開發者需要填寫需要跳轉小程式的二維碼規則。要求如下:
- 二維碼規則的域名須通過ICP備案的驗證。
- 支援http、https、ftp開頭的連結(如:http://wx.qq.com、https://wx.qq.com/mp/、https://wx.qq.com/mp?id=123)。
- 一個小程式帳號可配置不多於10個二維碼字首規則。
字首佔用規則
開發者可選擇是否佔用符合二維碼匹配規則的所有子規則。如選擇佔用,則其他帳號不可申請使用滿足該字首匹配規則的其他子規則。
如:若開發者A配置二維碼規則:https://wx.qq.com/mp?id=123,並選擇“佔用所有子規則“,其他開發者將不可以配置滿足字首匹配的子規則如https://wx.qq.com/mp?id=1234。
我推薦的方式
webview實現方式
- 設定跳轉功能小程式後臺就可以設定,連結是分為四部分,路https://www.example.com/wxmin…。
https://www.example.com 域名 /wxmini/ 小程式前置規則,需要在伺服器上建一個資料夾,並且把驗證檔案放在資料夾線 home.html 需要跳轉的網頁頁面 a=1 跳轉頁面的引數 - 對onload函式再進行處理,實現普通二維碼跳轉。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
// 對index onLoad在進行處理 onLoad: function (option) { this.resetOption(option) if (option.urlToken) { getApp().globalData.urlToken = option.urlToken } if (option.urlData) { getApp().globalData.urlData = option.urlData } this.setData({ url: getApp().globalData.urlToken + '?' + getApp().globalData.urlData }) }, resetOption: function (option) { if (!option) { return } if (option.q) { option.q = decodeURIComponent(option.q) if (option.q.indexOf('https://www.example.com/wxmini/') == -1) { return } let tmp = option.q.replace('/wxmini', '') let tmps = tmp.split('?') option.urlToken = tmps[0] option.urlData = tmps[1] } else { option.urlData = decodeURIComponent(option.urlData) } } |
返回按鈕缺失問題
如果web頁面是在第一個頁面的話,這時候會有一個問題,小程式的返回按鈕就沒有了,webview無法使用微信的返回按鈕了,這時候只要在webview頁面前多加一個跳轉頁面就好了(第一個頁面也可以設定成獲取使用者許可權的頁面,不過我感覺這樣體驗不好,也不是所有頁面都要使用者獲取了許可權才可以使用)
最終的頁面層級
1 2 3 4 5 6 7 |
"pages": [ "page/index/index", // 首頁,處理onload裡的option內容,為了返回按鈕設定的 "page/home/index", // webview所在的頁面 "page/auth/index", // 獲取使用者許可權的頁面 "page/pay/index", // 支付頁面 "page/error/index" // 錯誤資訊頁面 ], |