詳解小程序中h5頁(yè)面onShow實(shí)現(xiàn)及跨頁(yè)面通信方案
小程序webview的現(xiàn)狀
h5頁(yè)面在小程序中的交互(跳轉(zhuǎn))場(chǎng)景
- h5跳轉(zhuǎn)小程序native頁(yè)面(如:調(diào)用小程序地址選擇能力,然后返回對(duì)應(yīng)的地址信息給h5頁(yè)面)
- h5跳轉(zhuǎn)己方業(yè)務(wù)線的h5頁(yè)面(內(nèi)部頁(yè)面交互,方式比較多樣)
- h5跳轉(zhuǎn)其它業(yè)務(wù)線的h5頁(yè)面(如:交易流程,相關(guān)頁(yè)面可能有其他業(yè)務(wù)線提供)
主要痛點(diǎn)
在完成相關(guān)操作后, 頁(yè)面狀態(tài)需要更新 ,目前常見(jiàn)的更新方式有如下兩種:
- 第一種:通過(guò)url傳參(如:url中加入__isonshowrefresh=1,告訴webview再次onshow時(shí)候刷新),把需要傳遞的參數(shù)拼接到url中,重新打開(kāi)url。
- 第二種:需要跳轉(zhuǎn)到新的頁(yè)面進(jìn)行數(shù)據(jù)更新(如:下單頁(yè) - 地址選擇頁(yè) - 新的下單頁(yè))
第一種方案,功能上沒(méi)有問(wèn)題,但會(huì)導(dǎo)致頁(yè)面刷新,如果頁(yè)面操作復(fù)雜,需要多次刷新
第二種方案,正向操作時(shí)體驗(yàn)比方案一好,但導(dǎo)致了另外一個(gè)問(wèn)題:操作 跳轉(zhuǎn)層級(jí)過(guò)深 ,尤其返回的時(shí)候簡(jiǎn)直讓人崩潰。
小程序中,h5頁(yè)面打開(kāi)新頁(yè)面方式
我們先來(lái)看下小程序中常見(jiàn)的h5跳h5的方式:
- 方式1:直接用location.href跳轉(zhuǎn),返回時(shí)候各機(jī)型表現(xiàn)不一致,有的會(huì)刷頁(yè)面,重新執(zhí)行js,有的會(huì)直接展示之前的緩存
- 方式2:通過(guò)路由hash跳轉(zhuǎn),返回觸發(fā)hashchange,頁(yè)面不刷新,js層面重現(xiàn)渲染
- 方式3:跳轉(zhuǎn)頁(yè)面打開(kāi)一個(gè)新的webview,相當(dāng)于每個(gè)頁(yè)面都是一個(gè)獨(dú)立的webview
我們采用的是方式3,理由如下:
- 打開(kāi)新頁(yè)面時(shí)的效果更趨近于native間的跳轉(zhuǎn)(當(dāng)然新打開(kāi)的頁(yè)面也會(huì)重新加載靜態(tài)資源,同時(shí)這也有另一個(gè)問(wèn)題,一旦你打開(kāi)10個(gè)層級(jí)后,再打開(kāi)新的webview就沒(méi)反應(yīng)了,這個(gè)是小程序10層限制)
- 返回的體驗(yàn)也更趨近于native,同時(shí)保證頁(yè)面狀態(tài)統(tǒng)一(不會(huì)出現(xiàn)有的直接展示,有的會(huì)重新執(zhí)行js)
- webview通過(guò)this.src拿到的鏈接即為當(dāng)前頁(yè)面鏈接,因?yàn)槿绻?yè)面自行通過(guò)路由和location.href跳轉(zhuǎn),頁(yè)面鏈接變更后,webview并不會(huì)知曉,這種方案,webview通過(guò)this.src拿到的鏈接始終是當(dāng)前頁(yè)面的鏈接。
由于這種方案可能會(huì)達(dá)到小程序的10層限制。所以在一些重要頁(yè)面建議加入“ 回到首頁(yè) ”的操作,通過(guò)這個(gè)操作來(lái)縮短小程序歷史棧
回到首頁(yè)方案簡(jiǎn)述
(如果不感興趣這部分可以直接略過(guò))
wx.miniProgram.reLaunch({
url: '/pages/webview/bridge?url=項(xiàng)目首頁(yè)地址'
})
先聲明,我們webview的路徑是/pages/webview/webview
/pages/webview/bridge是個(gè)中轉(zhuǎn)頁(yè),有如下特點(diǎn): 該頁(yè)面并 不是最終打開(kāi)h5頁(yè)面的webview頁(yè) ,而是一個(gè) 中轉(zhuǎn)頁(yè)。
主要用作返回處理
- 頁(yè)面邏輯: 如果是第一次展示,則跳轉(zhuǎn)/pages/webview/webview,同時(shí)把url傳過(guò)去,正常打開(kāi)h5
- 如果不是第一次展示,說(shuō)明是從webview返回過(guò)來(lái)的,直接重定向到小程序首頁(yè)
這個(gè)中轉(zhuǎn)頁(yè):主要保證reLaunch到某h5頁(yè)面后,用戶仍然可以點(diǎn)擊返回到小程序首頁(yè)。
該方案通常用于:小程序中內(nèi)嵌了多個(gè)業(yè)務(wù)線的h5頁(yè)面這種場(chǎng)景。
一個(gè)內(nèi)容發(fā)布場(chǎng)景
我們從首頁(yè)進(jìn)入發(fā)布頁(yè),完成發(fā)布后,跳轉(zhuǎn)至商品詳情頁(yè)
那么對(duì)于一個(gè)新用戶來(lái)講,整個(gè)操作過(guò)程是這樣的:
- 首頁(yè)(點(diǎn)擊發(fā)布)
- 進(jìn)入發(fā)布頁(yè)面(選擇發(fā)布商品的分類)
- 進(jìn)入商品分類頁(yè)(選擇完成后)
- 將分類id拼入url,進(jìn)入新的發(fā)布頁(yè)面(選取件地址)
- 進(jìn)入地址列表頁(yè)(如果新用戶是沒(méi)有地址的,點(diǎn)擊新增地址)
- 進(jìn)入新增地址頁(yè)(添加完成后)
- 將地址id拼如url,進(jìn)入又一個(gè)新的發(fā)布頁(yè)面(編輯完信息后點(diǎn)擊發(fā)布)
- 進(jìn)入發(fā)布成功頁(yè)(點(diǎn)擊查看商品詳情)
- 進(jìn)入商品詳情頁(yè)
這個(gè)場(chǎng)景就是同一個(gè)頁(yè)面,里面不同的內(nèi)容項(xiàng)需要跳轉(zhuǎn)不同的頁(yè)面去操作,然后再回到原來(lái)頁(yè)面更新?tīng)顟B(tài)的問(wèn)題。
假如商品詳情頁(yè)沒(méi)有“回到首頁(yè)”的入口,那么這個(gè)用戶要想回到首頁(yè)。。。需要按8次“返回” = =!
經(jīng)過(guò)這個(gè)體驗(yàn)后,我想一般的用戶是沒(méi)有勇氣再發(fā)布內(nèi)容的。
當(dāng)然也有另一種這種折中方案
就是商品提到的,在連接中加入某個(gè)標(biāo)志位,比如在url中加入__isonshowrefresh=1,webview在打開(kāi)連接時(shí)候,會(huì)去讀取這個(gè)參數(shù),如果有,則每次在onShow時(shí)候,重新加載url,通過(guò)刷新頁(yè)面進(jìn)行頁(yè)面狀態(tài)更新。
這個(gè)體驗(yàn)也不爽,就是在復(fù)雜的頁(yè)面會(huì)多次刷新。
聲明
我下面要講的這個(gè)方案并不是停留在設(shè)想階段,它已經(jīng)在線上跑了
想看效果的朋友,可以在微信小程序中搜:
“轉(zhuǎn)轉(zhuǎn)二手交易網(wǎng)”-“0元免費(fèi)領(lǐng)”-(底部)“送閑置賺星星”-進(jìn)入到發(fā)布頁(yè)后
分類(跳轉(zhuǎn)h5,選中內(nèi)容后返回,將參數(shù)傳給之前的h5)
取件地址(跳轉(zhuǎn)native原生地址選擇,選中后返回,將參數(shù)傳給之前的h5)
OK,我們進(jìn)入今天的主題
小程序中h5頁(yè)面onShow和跨頁(yè)面通信的實(shí)現(xiàn)
首先想到的就是onShow方法的實(shí)現(xiàn),之前有人提議用visibilitychange來(lái)實(shí)現(xiàn)onShow方法。
但調(diào)研過(guò)后,這種方式在ios中表現(xiàn)符合預(yù)期,但是在安卓手機(jī)里,是不能按預(yù)期觸發(fā)的。所以該方案被我否了。
于是就有了下面的方案
原理介紹
這個(gè)方案需要h5和小程序的webview都做處理。
核心思想: 利用webview的hash特性
- 小程序通過(guò)hash傳參,頁(yè)面不會(huì)更新(這個(gè)和瀏覽器一樣)
- h5可以通過(guò)hashchange捕獲最新參數(shù),進(jìn)行自定義邏輯處理
- 最后執(zhí)行window.history.go(-1)
為什么要執(zhí)行window.history.go(-1)
這一步是整個(gè)方案的精髓:
- 因?yàn)閔ash變更會(huì)導(dǎo)致webview歷史棧長(zhǎng)度+1,用戶需要多一次返回操作。但這一步明顯是多余的。
- 同時(shí)window.history.go(-1)后,會(huì)把webview在hash中添加的參數(shù)去掉,還能保證和之前的url一致。
方案延伸(跨頁(yè)面數(shù)據(jù)傳遞)
小程序里另個(gè)一常見(jiàn)的場(chǎng)景就是調(diào)用第三業(yè)務(wù)(或者己方業(yè)務(wù)),在做完某些操作后需要把選中的數(shù)據(jù)帶回之前的頁(yè)面。
如前面提到的例子:發(fā)布頁(yè),需要選擇發(fā)布類型,然后返回,發(fā)布頁(yè)發(fā)布類型局部更新
當(dāng)然有些同學(xué)會(huì)說(shuō):我可以用setInterval,監(jiān)控localStorage。在新頁(yè)面選中內(nèi)容后,設(shè)置localStorage,然后在返回不就可以了。
我這里說(shuō)的是 通用方案 。如果頁(yè)面都是由己方業(yè)務(wù)線維護(hù)的當(dāng)然可以隨便折騰。
但是一旦涉及到第三方業(yè)務(wù)線,尤其不同域名頁(yè)面的業(yè)務(wù)調(diào)用,這種通信方式就尷尬了。
那我的方案怎么處理呢,我總結(jié)了一張圖

我們來(lái)解讀一下這張圖:
- webview1打開(kāi)發(fā)布頁(yè)面,h5綁定hashchange事件(因?yàn)閣ebview通過(guò)hash傳值時(shí)會(huì)觸發(fā)該事件)
- 將自定義的onShow方法緩存。在hashchange觸發(fā)時(shí),尋找指定參數(shù),如果存在則觸發(fā)
- 用戶點(diǎn)擊跳轉(zhuǎn)到類型選擇頁(yè)
- 這時(shí)會(huì)打開(kāi)一個(gè)新的webview2頁(yè)面實(shí)例,打開(kāi)類型選擇頁(yè)
- 用戶操作完成,調(diào)用wx.miniProgram.postMessage把數(shù)據(jù)發(fā)送給webview,并返回
- webview由于綁定了bindmessage事件,在返回時(shí)會(huì)接收到h5發(fā)送的數(shù)據(jù)
- 同時(shí)將接收到的數(shù)據(jù)緩存在一個(gè)全局的store中,webview2銷毀,小程序執(zhí)行返回
- 從webview2返回到webview1,這時(shí)webview1的onShow鉤子會(huì)觸發(fā)
- webview1讀取全局的store,將要發(fā)送的參數(shù)取出,拼接h5鏈接的hash部分,并重新打開(kāi)該鏈接
- 雖然重新打開(kāi)鏈接,由于僅僅是hash部分的變化,所以頁(yè)面不會(huì)刷新
- 但會(huì)觸發(fā)h5頁(yè)面的hashchange,此時(shí)調(diào)用用戶自定義的onShow方法,讀取hash參數(shù),進(jìn)行頁(yè)面更新
- h5頁(yè)面在執(zhí)行完onShow方法后,調(diào)用window.history.go(-1),恢復(fù)歷史棧
整個(gè)過(guò)程就是這樣
代碼示意:
小程序
小程序webview要先做幾方面考慮:
- 出于平滑接入的考慮,不能上來(lái)搞一刀切,要保證現(xiàn)有頁(yè)面再不做任何修改的情況下繼續(xù)訪問(wèn)。
- 新能力要通過(guò)額外參數(shù)區(qū)分,如:檢測(cè)url中的query部分,帶有__isonshowpro=1再進(jìn)行通過(guò)hash方式傳參。
- 改造原有邏輯,讓__isonshowpro=1時(shí),hash處理邏輯優(yōu)先級(jí)最高
- 參數(shù)定義,在前面加入了兩個(gè)下劃線,目的是為了分區(qū)url中正常的參數(shù)
小程序端webview.wpy
<web-view wx:if="{{url}}" src="{{url}}" binderror="onError" bindload="onLoaded" bindmessage="onPostMessage"></web-view>
// 鏈接處理工具方法
import util from '@/lib/util';
// 全局?jǐn)?shù)據(jù)存儲(chǔ)操作類
import routeParams from '@/lib/routeParams';
const urlReg = /^(https?\:\/\/[^?#]+)(\?[^#]*)?(#[^\?&]+)?(.+)?$/;
let messageData = {};
export default class extends wepy.page {
data = {
// 頁(yè)面展示次數(shù)
pageShowCount: 0,
// 頁(yè)面url中query部分的參數(shù)對(duì)象
mQuery: {},
...
}
onShow(){
++this.pageShowCount;
// 獲取其他頁(yè)面經(jīng)過(guò)操作后,需要傳遞給h5的參數(shù)
let data = routeParams.getBackFromData() || {};
// webview頁(yè)面狀態(tài)更新
if(this.pageShowCount > 1 && this.mQuery.__isonshowpro && this.mQuery.__isonshowpro === '1' || data.refresh){
// 獲取需要傳遞給h5頁(yè)面的參數(shù)
let refreshParam = data.refreshParam;
...
// 如果連接中帶有需要處理onShow邏輯的參數(shù)(通過(guò)url的hash和h5交互,而不是刷頁(yè)面)
if (this.pageShowCount > 1 && this.mQuery.__isonshowpro === '1') {
let [whole, mainUrl, queryStr, hashStr, hashQueryStr] = urlReg.exec(this.url);
// 在url的hash中加入新的參數(shù)
hashStr = (hashStr || '#').substring(1);
if (refreshParam) {
delete refreshParam.refresh;
}
const messageData = this.getNavigateMessageData();
// 將需要更新的參數(shù)傳給頁(yè)面hash
hashStr = util.addQuery(hashStr, Object.assign({
// onshow標(biāo)志位
__isonshow: 1,
// wa主動(dòng)觸發(fā)hashchange標(biāo)志位
// 其實(shí)目前通過(guò)__isonshow就可以判斷是wa主動(dòng)觸發(fā)hashchange
// 設(shè)置該字段是為了明確功能,且以后擴(kuò)展用
__wachangehash: 1,
// 時(shí)間戳刷新
__hashtimestamp: Date.now()
}, messageData, refreshParam));
this.url = mainUrl + queryStr + '#' + hashStr;
console.log('【webview-hashchange-url】', this.url);
// 這里要加個(gè)延遲,否則在webview返回到webview時(shí),無(wú)法觸發(fā)hashchange,應(yīng)該是小程序bug
setTimeout(()=> {
this.$apply();
}, 50);
// 通過(guò)修改query參數(shù),刷新webview
} else {
...
}
...
}
}
/**
* 獲取需要發(fā)送的消息數(shù)據(jù)
*/
getNavigateMessageData(){
let rst = {};
for(let i in messageData){
/* message結(jié)構(gòu):
message: {
key: 'xx', // 消息名稱
content: 'xx', // 消息內(nèi)容
trigger: { // 觸發(fā)條件
type: '', // 觸發(fā)類型
- immediately 在下一次onshow或者打開(kāi)頁(yè)面中立刻觸發(fā),
- url 在找到指定h5鏈接時(shí)觸發(fā)
content: '' // 條件內(nèi)容
- type=immediately 時(shí)為空
- type=url 時(shí)候?yàn)閔5鏈接地址
}
}
*/
const message = messageData[i];
const trigger = message.trigger || {};
// 立刻發(fā)送、路徑觸發(fā)
if(trigger.type === 'immediately' || trigger.type === 'url' && this.url.indexOf(trigger.content) > -1){
// 將key和content集合到一個(gè)對(duì)象中,便于hash直接設(shè)置
rst[message.key] = message.content;
// 消息通知后,從緩存中刪除
delete messageData[message.key];
}
}
console.log('【webview-get-message】', rst);
console.log('【webview-message-cache】', messageData);
return rst;
}
/**
* 存儲(chǔ)消息數(shù)據(jù)
*/
storeNavigateMessageData(message){
if(message && message.key){
console.log('【webview-store-message】', message)
// 通過(guò)key設(shè)置每一條消息名稱
messageData[message.key] = message;
console.log('【webview-message-cache】', messageData);
}
}
methods = {
// 接收發(fā)送過(guò)來(lái)的消息
onPostMessage(e){
if(!e.detail.data)return;
const detailData = e.detail.data;
// 獲取消息數(shù)據(jù)
let messageData = getValueFromMixedArray(detailData, 'messageData', true);
if (messageData) {
// 存儲(chǔ)
this.storeNavigateMessageData(messageData);
}
...
}
}
...
}
上面東西看著挺多,總結(jié)下來(lái)就是幾點(diǎn):
- 綁定bindmessage事件
- 接收到頁(yè)面?zhèn)鱽?lái)的消息之后,需要按照一定規(guī)則存起來(lái)(我是按照key存儲(chǔ)的)
- webview在觸發(fā)onShow鉤子時(shí)候,按照之前傳過(guò)來(lái)的觸發(fā)條件(condition),取出需要發(fā)送的消息數(shù)據(jù)
- 將數(shù)據(jù)拼接到url的hash部分,并加入特有的標(biāo)志位,重新加載url
h5端
h5端在做修改時(shí)也要考慮幾點(diǎn):
最好能把這些交互邏輯封裝起來(lái)
讓業(yè)務(wù)方比較簡(jiǎn)單方便的調(diào)用
這里我新定義了2個(gè)方法
onShow(callback)
- 描述:這個(gè)和小程序onShow鉤子一樣,只不過(guò)是給h5調(diào)用的
- 參數(shù):callback 回調(diào)方法
例子:發(fā)布頁(yè)面,需要選擇分類,返回時(shí)需要更新分類信息
import { isZZWA, onShow } from '@/lib/sdk'
import URL from '@/lib/url'
...
created () {
if (isZZWA()) {
onShow(() => {
// 地址信息
const addressInfo = URL.getHashParam('zzwaAddress')
console.log('addressInfo:', decodeURIComponent(addressInfo))
...
// 分類信息
const selecteCateInfo = URL.getHashParam('selecteCateInfo')
console.log('selecteCateInfo:', selecteCateInfo)
...
} else {
...
}
}
...
serviceDone(data, condition)
描述:業(yè)務(wù)結(jié)束,需要將數(shù)據(jù)傳遞給指定頁(yè)面
參數(shù):
data Object 需要傳遞的數(shù)據(jù) {key: 'xx', content: 'xx'}
condition String|Number 觸發(fā)條件
- String 指定url的路徑,當(dāng)webview打開(kāi)指定的url觸發(fā)onshow時(shí),會(huì)發(fā)送該消息
- Number 返回到指定的測(cè)試,類似history.go(-1),如: -1,-2
例子:類型選擇頁(yè)
import { isZZWA, serviceDone } from '@/lib/sdk'
// 類型選擇點(diǎn)擊
typeChooseClick (param, type) {
...
if (isZZWA()) {
// 需要返回的數(shù)據(jù)
const data = {
key: 'selecteCateInfo',
content: JSON.stringify({...})
}
// 通過(guò)postMessage發(fā)送給小程序,-1表示返回上一頁(yè)面
serviceDone(data, -1)
} else {
...
}
}
ok,我們來(lái)看看h5端的sdk是怎么實(shí)現(xiàn)的
import util from './util';
class WASDK {
/**
* Create a instance.
* @ignore
*/
constructor(){
// hashchang事件處理
if('onhashchange' in window && window.addEventListener && !WASDK.hashInfo.isInit){
// 更新標(biāo)志位
WASDK.hashInfo.isInit = true;
// 綁定hashchange
window.addEventListener('hashchange', ()=>{
// 如果小程序webview修改的hash,才進(jìn)行處理
if (util.getHash(window.location.href, '__wachangehash') === '1') {
// 這塊有個(gè)坑:
// ios小程序webview在修改完url的hash之后,頁(yè)面hashchange和更新都可以正常觸發(fā)
// 但是:h5調(diào)用部分小程序能力會(huì)失敗(如:ios在設(shè)置完hash后,調(diào)用wx.uploadImg會(huì)失敗,需要重新設(shè)置wx.config)
// 因?yàn)閕os小程序的邏輯是,url只要發(fā)生變化,wx.config中的appId就找不到了
// 所以需要重新進(jìn)行wx.config配置
// 這一步是獲取之前設(shè)置wx.config的參數(shù)(需要從服務(wù)端拿,因?yàn)橹耙呀?jīng)獲取過(guò)了,這里從緩存直接?。?
const jsticket = window.native && window.native.adapter && window.native.adapter.jsticket || null;
const ua = navigator.userAgent;
// 非安卓系統(tǒng)要重新設(shè)置wx.config
if (jsticket && !(ua.indexOf('Android') > -1 || ua.indexOf('Adr') > -1)) {
window.wx.config({
debug: false,
appId: jsticket.appId,
timestamp: jsticket.timestamp,
nonceStr: jsticket.noncestr,
signature: jsticket.signature,
jsApiList: ['onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ',
'onMenuShareQZone', 'onMenuShareWeibo', 'scanQRCode', 'chooseImage', 'uploadImage', 'previewImage', 'getLocation', 'openLocation']
})
}
// 觸發(fā)緩存數(shù)組的回調(diào)
WASDK.hashInfo.callbackArr.forEach(callback=>{
callback();
})
// 執(zhí)行返回操作(這一步是重點(diǎn)?。。?
// 因?yàn)閣ebview設(shè)置完hash參數(shù)后,會(huì)使webview歷史棧+1
// 而實(shí)際并不需要這次多余的歷史記錄,所以需要執(zhí)行返回操作把它去掉
// 即便是返回操作,也僅僅是hash層面的變更,所以不會(huì)觸發(fā)頁(yè)面刷新
// 用setTimeout表示在下一次事件循環(huán)進(jìn)行返回操作。如果后面有對(duì)dom操作可以在當(dāng)前次事件循環(huán)完成
setTimeout(()=>{
window.history.go(-1);
}, 0);
}
}, false)
}
}
/**
* hash相關(guān)信息
*/
static hashInfo = {
// 是否已經(jīng)初始化
isInit: false,
// hash回調(diào)香瓜數(shù)組
callbackArr: []
}
/**
* 頁(yè)面再次展示時(shí)鉤子方法
* @param {Function} callback - 必填, callback回調(diào)方法, 回傳參數(shù)為hash部分問(wèn)號(hào)后面的參數(shù)解析對(duì)象
*/
@execLog
onShow(callback){
if (typeof callback === 'function') {
// 對(duì)回調(diào)方法進(jìn)行onshow邏輯包裝,并推入緩存數(shù)組
WASDK.hashInfo.callbackArr.push(function(){
// 檢查是否是指定參數(shù)發(fā)生變化
if(util.getHash(window.location.href, '__isonshow') === '1'){
// 觸發(fā)onShow回調(diào)
callback();
}
})
} else {
util.console.error(`參數(shù)錯(cuò)誤,調(diào)用onShow請(qǐng)傳入正確callback回調(diào)`);
}
}
/**
* 業(yè)務(wù)處理完成并發(fā)送消息
* @param {Object} obj - 必填項(xiàng),消息對(duì)象
* @param {String} obj.key - 必填項(xiàng),消息名稱
* @param {String} obj.content - 可選項(xiàng),消息內(nèi)容,默認(rèn)空串,如果是內(nèi)容對(duì)象,請(qǐng)轉(zhuǎn)換成字符串
* @param {String|Number} condition - 可選項(xiàng),默認(rèn)僅進(jìn)行postMessage
* String - 可以傳指定url的路徑,當(dāng)小程序webview打開(kāi)指定的url或者onshow時(shí),會(huì)觸發(fā)該消息
* 也可傳小程序path,這個(gè)為以后預(yù)留
* Number - 返回到指定的測(cè)試,類似history.go(-1),如: -1,-2
*/
@execLog
serviceDone(obj, condition){
if(obj && obj.key){
// 消息體
const message = {
// 消息名稱
key: obj.key,
// 消息體
content: obj.content || '',
// 觸發(fā)條件
trigger: {
// 類型 'immediately'在下一次onshow中立刻觸發(fā), 'url',在找到指定h5鏈接時(shí)觸發(fā),'path'在打開(kāi)指定小程序路徑時(shí)觸發(fā)
type: 'immediately',
// 條件內(nèi)容,immediately是為空,url是為h5鏈接地址,path是為小程序路徑
content: ''
}
};
// 解析觸發(fā)條件
condition = condition || 0;
// 如果是路徑
if(typeof condition === 'string' && (condition.indexOf('http') > -1 || condition.indexOf('pages/') > -1)){
// 設(shè)置消息觸發(fā)條件
message.trigger = {
type: condition.indexOf('http') > -1 ? 'url' : 'path',
content: condition
}
}
// 發(fā)送消息
wx.miniProgram.postMessage({
data: {
messageData: message
}
});
// 如果不是url或者path觸發(fā),則對(duì)conditon是否需要返回進(jìn)行判斷
if(message.trigger.type === 'immediately'){
// 查看是否需要返回指定的層級(jí),兼容傳入'-1'字符串這種類型的場(chǎng)景
try{
condition = parseInt(condition, 10);
}catch(e){}
// 保證返回級(jí)數(shù)的正確性
if(condition && typeof condition === 'number' && !isNaN(condition)){
this.handler.navigateBack({delta: Math.abs(condition)});
}
}
}else{
util.console.error(`參數(shù)錯(cuò)誤,調(diào)用serviceDone方法,傳入的對(duì)象中不包含key值`);
}
}
...
}
window.native = new Native();
export default native;
這個(gè)看著也挺多,總結(jié)下來(lái)是兩點(diǎn):
onShow方法的實(shí)現(xiàn)
綁定一個(gè)hashchange事件(這里做了防止重復(fù)綁定事件的處理)
將傳入的onShow自定義事件緩存在一個(gè)數(shù)組中,hashchange觸發(fā)時(shí),根據(jù)特有的標(biāo)志位__isonshow和__wachangehash確定是否觸發(fā)
serviceDone方法的實(shí)現(xiàn)
- 處理傳過(guò)來(lái)的數(shù)據(jù)
- 處理該數(shù)據(jù)的觸發(fā)條件:immediately表示最近的一次onShow觸發(fā),或者自己指定url
- 通過(guò)wx.miniProgram.postMessage發(fā)送數(shù)據(jù)
ok,整個(gè)方案就介紹完了
結(jié)語(yǔ)
最早的方案并不完全是這樣的,但原理是一樣的。在我實(shí)現(xiàn)的過(guò)程中發(fā)現(xiàn)原始方案有很多問(wèn)題
于是我又做了大量的改造和細(xì)節(jié)優(yōu)化,于是形成了上面的最終方案。
這個(gè)方案屬于侵入式改造方案,需要各業(yè)務(wù)方改造自己的代碼。雖然有一定改造成本,但用戶體驗(yàn)的收益非常明顯。
ps:我們的QA在測(cè)試時(shí)都說(shuō)“這用起來(lái)就爽多了”
注意:
采用這個(gè)方案需要注意幾點(diǎn):
- 如果采用這種方式通信,需要在當(dāng)前頁(yè)面url的query部分加入__isonshowpro=1,否則是不會(huì)通過(guò)hash通信的
- 同時(shí)要保證頁(yè)面確實(shí)調(diào)用了onShow方法,否則頁(yè)面也是不會(huì)刷新的
- 如果第三方業(yè)務(wù)需要傳值,需要統(tǒng)一采用serviceDone方法通信
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 微信小程序?qū)崿F(xiàn)傳遞多個(gè)參數(shù)與事件處理
- 微信小程序?qū)W習(xí)筆記之跳轉(zhuǎn)頁(yè)面、傳遞參數(shù)獲得數(shù)據(jù)操作圖文詳解
- 微信小程序頁(yè)面間值傳遞的兩種方法
- 微信小程序使用navigateTo數(shù)據(jù)傳遞的實(shí)例
- 微信小程序教程系列之頁(yè)面跳轉(zhuǎn)和參數(shù)傳遞(6)
- 微信小程序在其他頁(yè)面監(jiān)聽(tīng)globalData中值的變化
- 微信小程序Page中data數(shù)據(jù)操作和函數(shù)調(diào)用方法
- 微信小程序獲取用戶信息的兩種方法wx.getUserInfo與open-data實(shí)例分析
- 微信小程序 setData使用方法及常用錯(cuò)誤解決辦法
- 微信小程序跨頁(yè)面?zhèn)鬟fdata數(shù)據(jù)方法解析
相關(guān)文章
js實(shí)現(xiàn)計(jì)算器和計(jì)時(shí)器功能
這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)計(jì)算器和計(jì)時(shí)器功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07
JavaScript原生數(shù)組函數(shù)實(shí)例匯總
這篇文章主要介紹了JavaScript原生數(shù)組函數(shù)實(shí)例匯總,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10
在JavaScript中查找字符串中最長(zhǎng)單詞的三種方法(推薦)
這篇文章主要介紹了在JavaScript中查找字符串中最長(zhǎng)單詞的三種方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
在瀏覽器中獲取當(dāng)前執(zhí)行的腳本文件名的代碼
同事提了一個(gè)問(wèn)題,如何在瀏覽器中動(dòng)態(tài)插入的 JavaScript 文件中,獲取當(dāng)前文件名?2011-07-07
JavaScript提取元素中第一個(gè)子元素的實(shí)現(xiàn)方法
本文主要介紹了JavaScript提取元素中第一個(gè)子元素的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
js實(shí)現(xiàn)滾動(dòng)條自動(dòng)滾動(dòng)
這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)滾動(dòng)條自動(dòng)滾動(dòng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-12-12
JavaScript實(shí)現(xiàn)選中文字提示新浪微博分享效果
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)選中文字提示新浪微博分享效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06

