1、背景android
不少開發者在面對小程序的五層頁面限制時,心裏大概是崩潰的。 使用wx.navigateTo()或<navigator>組件(open-type=navigate時)跳轉的頁面路勁最多隻有5層,這些頁面路徑能夠經過wx.navigateBack() API或者左上角返回按鈕順序返回。當頁面路徑大於5層時,使用wx.navigateTo()跳轉下一頁不會有任何動靜,頁面既不跳轉也不會報錯(這就尷尬了,若是沒有細看開發文檔,還覺得是本身代碼寫錯了呢)。[注:開發工具和ios都不會報錯,沒有驗證android的表現] 可是,有時候業務場景存在多頁面交互的狀況,遠遠不止5層頁面,這時候如何輕鬆處理頁面跳轉就成了一個不得不考慮的問題。
2、頁面棧&小程序導航API接口ios
微信小程序中的頁面導航API有3個:wx.navigateTo、wx.redirectTo、wx.navigateBack。wx.navigateTo和wx.redirectTo打開新的頁面,wx.navigateBack用於返回上一個頁面(棧裏的頁面)。 這3個頁面API的區別在於:
一、wx.navigateTo接口與頁面棧示意圖json
當棧滿了(5層)以後,再調用wx.navigateTo()跳轉任何頁面,都不會成功。 特別注意的是,調用兩次wx.navigateTo('頁面B');wx.navigateTo('頁面B');,那麼棧裏存在兩個頁面B(或者說兩個頁面B的實例),以下圖。
二、wx.redirectTo接口與頁面棧示意圖小程序
三、wx.navigateBack微信小程序
這個比較容易理解,就是棧裏的頁面一個一個出棧。當最後一個頁面(首頁A)出棧後,也就退出了小程序。 在新版庫中,給wx.navigateBack添加了一個參數delta,用於決定須要返回幾層頁面;若是delta大於等於現有頁面數(也就是棧裏的頁面數),則返回到首頁。
四、頁面棧與wx.navigateBack緩存
在此突出wx.navigateBack(OBJECT)和getCurrentPages()接口,由於本文提出的應對頁面棧5層限制的方案正是充分利用這兩個接口的結果。
3、一種能夠解決問題的方案
一、方案的提出微信
由以上的對導航接口和頁面棧的分析能夠知道兩點: (1)調用兩次wx.navigateTo('頁面B'),那麼棧裏存在兩個頁面B(或者說兩個頁面B的實例)。但其實棧裏沒有必要保留兩個頁面B實例 (2)wx.navigateBack的參數delta能夠返回棧裏的指定頁面,其中頁面信息能夠由getCurrentPages()接口獲得 據此,提出以下一種解決方案 (1)自定義頁面跳轉方式 (2)當頁面棧裏已經存在要跳轉的目標頁面A,那麼使用wx.navigateBack({delta: xx})返回到此頁面A,{xx=getCurrentPages().length - 目標頁面A在棧裏的index - 1} (3)若是頁面棧尚未要跳轉的目標頁面A a、若頁面棧已經已滿(length>=5),那麼使用wx.redirectTo(頁面A); b、不然,使用wx.navigateTo(頁面A) (4)頁面間數據採用緩存傳遞
二、方案的實現app
/* * desc: * 一、若是目標頁面已經在棧中,那麼wx.navigateBack({delta: xx})到目標頁面 * 二、若是目標頁面不在棧中, * (1)若是棧大小<5,那麼wx.navigateTo(目標頁面) * (2)不然,wx.redirectTo(目標頁面) * 三、全部頁面間的數據傳輸,經過緩存攜帶 * 四、跳轉目標頁,用goPage() * 五、在目標頁,經過inPage()接收數據。接收後,數據會被刪除 */ Nav = { MAX_VALUE: 5, //頁面棧最多5層 /* * desc: 跳轉頁面 * param: * obj: { * url: '' //頁面在app.json中的路徑,路徑前不要加'/'斜槓(也能夠加,作了兼容) * data: {} //須要攜帶的參數,統一經過緩存攜帶 * } */ goPage: function(obj) { var pages = getCurrentPages(), //頁面棧 len = pages.length, dlt = '', target = '/' + obj.url.replace(/^\//, ''), //若是有,將第一個‘/’去掉,而後再補上(開發者習慣不一樣,有些人會給url加/,有些則忘了,兼容處理 navigation_key = target.replace(/\//g, '_'); //存儲數據的下標,每一個頁面由本身的存儲key,保證了頁面間數據不會相互污染 //查找目標頁在頁面棧的位置 for (var i = 0; i < len; i++) { if (pages[i].route == target) { // dlt = i + 1; //目標頁在棧中的位置 break; } } //保存數據 //因爲navigateBack()回到指定頁面,不會從新執行onLoad事件,因此加個標兵。 //只有在isLoad = true;時,纔會接收參數並執行類onLoad事件 var nData = Object.assign({ referer: pages[len - 1].route, _is_load: true }, obj.data || {}); wx.setStorageSync(navigation_key, JSON.stringify(nData)); if (!dlt) { //頁面不在棧中 if (len < this.MAX_VALUE) { wx.navigateTo({ url: target }); } else { wx.redirectTo({ url: target }); } } else { wx.navigateBack({ delta: len - dlt }); } }, /* * desc:在目標頁接收數據 * param: * myOnLoad:回調方法,因爲經過緩存傳遞數據,頁面onLoad沒辦法接收,因此要自定義回調 */ inPage: function(myOnLoad) { var pages = getCurrentPages(); var navigation_key = pages[pages.length - 1].route.replace(/\//g, '_'); //從其餘頁面跳轉過來的,那麼isLoad確定爲true,由於goPage中設置了。若是是用戶點擊左上角後退的,那麼isLoad=false,由於下面設置了 //獲取數據 try { var raw = wx.getStorageSync(navigation_key); var options = JSON.parse(raw); if(options._is_load && myOnLoad) { //用戶點擊左上角後退時,不會執行myOnLoad,由於此時_is_load是undefined myOnLoad(options); } wx.setStorage({ //清除數據 key: navigation_key, data: '' //這以後,_is_load是undefined了 }); } catch (e) { } } };
採用以上方案後,就能夠專心寫代碼邏輯了,不須要再擔憂頁面層級無心間超過5層致使沒法跳轉的問題。
4、最完美的解決方案工具
以上方案是完美的嗎? 想一想,若是通過屢次(超過5次)跳轉,而後點擊左上角後退會出現什麼狀況?是否是中間通過的一些頁面不見了(早已經出棧了)。用戶通常的預期是,我點擊跳轉屢次,後退應該原路返回,即從哪裏來的回到哪裏去&怎麼走過來的就怎麼走回去。 但這個問題顯然是沒法解決的,由於,一旦頁面超過了5層,總有頁面得出棧,「原路返回」已經失去了條件。 其實,對於頁面5層限制的問題,官方文檔已經給出了完美的解決方案: