淺析Ionic頁面傳參方案

前言

Ionic3 中父頁面給子頁面傳遞參數很是容易,使用框架提供的 navParams 來實現就好了,可是反過來子頁面給父頁面傳遞就沒那麼容易了,由於在路由的 navController 裏面的 pop 函數並不支持傳參數,使得前面的方法沒有用了(表面上是,實際上仍然有用,詳細看正文),這個時候就須要別的手段來達到咱們的目的了,故本文主要將對 Ionic3 中子頁面向父頁面傳參問題提供筆者經常使用的幾種解決方案,如有問題或更多建議,歡迎各位同僚下方評論,共同進步!es6

簡介

在 Ionic 中如何作到頁面與頁面間的通訊?這裏給出三種方向:路由傳參,事件傳參,service 傳參。因爲三個方向各自又有不一樣的實現,且每種方案適應的場景都不同,下面就會從實現,優缺點,適應場景這幾個方面來比較一下這幾個方案,最後各位看官老爺們各取所需。ajax

1、利用路由傳遞參數

假如你還不知道Ionic3的路由傳值的話,請戳 NavController 瞭解一下。typescript

好了正文開始,這裏要用的就是navParams,前面已經說了,在頁面的pop操做裏面是不容許傳值的,那如何作到用navParams傳值。這裏給出兩種方案api

1. 傳遞「頁面「

// 本文出現navCtrl都指代navContronller
// 語法不必定正確,請根據自身代碼進行修正

<!--父頁面-->
public a = 'a';
navCtrl.push('childrenPage', {
    parent: this
})
<!--子頁面-->
parent = navParams.get('parent');
parent.a = 'change from childe';

<!--父頁面-->
console.log(a) // change from childe

// 你也能夠直接調用父頁面的方法
複製代碼

爲何能夠這樣用?

歷史棧promise

Ionic3 的路由有個歷史棧的概念,相信知道棧的同窗確定都很熟悉這個,其實就是和棧同樣的操做,app開始在根頁面的時候,這個歷史棧只有1個頁面,就是你的根頁面,以後每當使用navController的push方法到一個新的頁面的時候,歷史棧把新的頁面加入進來,pop的時候就把那個頁面從歷史棧中彈出。瀏覽器

路由app

Ionic3 的路由機制比較「神奇」,和別的常見的Vue-Router或Angular單頁面路由方式不太同樣,有點像僞路由。由於歷史棧內的頁面所有都真實的存在於當前頁面上,嘻嘻,證據以下圖: 框架

Ionic3 的僞路由
能夠看到,個人頁面實際上通過了HomePage —> parentPage —> childPage跳轉,如今在childPage了,也就是說歷史棧內有這三個頁面,而在瀏覽器內能夠看到三個頁面都真實的存在。

分析異步

而查看頁面代碼能夠知道,每一個頁面不過是被 InoicPage 裝飾器修飾過的 Angular 的組件而已,那麼就能夠經過獲取組件的實例來改變組件的狀態。結合上面路由的狀況咱們知道:即便到了子頁面,咱們的父頁面或者說父組件沒有被destroy的(可自行驗證)。因此在子頁面中,咱們是能夠經過拿到歷史頁面的實例來對其進行操做的。最後咱們再整理一下思路:這裏利用navParams來傳遞父頁面自己到子頁面中,而後子頁面獲取到父頁面來對其進行操做,經過這個方式來達到傳遞參數的目的。ionic

優勢

使用方式簡單高效,甚至能夠規避傳遞參數這一個步驟直接在子頁面對父頁面進行操做(不推薦)。

缺點

  1. 父子頁面之間耦合度較高,子頁面的操做對於父頁面來講是「隱形」的,使用過分會形成維護的困難,由於你會須要時時刻刻關注子頁面對父頁面的影響。
  2. 多層級的頁面傳參須要通過中間多個頁面的傳遞,即便他們或許用不到這個父頁面,會致使多個頁面傳遞多個不須要的參數,代碼會變得臃腫難以維護。

推薦使用場景

涉及對父頁面的操做少且不復雜的場景,且僅推薦父 —> 子間使用,並且但願在使用時聲明一個明確的參數名稱以代表父子頁面之間的聯繫,提高代碼的可維護性。

2. 傳遞迴調

<!--這裏是網上常見版本,但筆者不太喜歡此版本-->

<!--父頁面-->
public a = 'a';
// 必須使用箭頭函數,若不使用,此法將會失效
public parentPromise = (childrenParams) => new Promis((resolve, reject) => {
    // 這裏能夠作你想利用回傳的參數想作的事
    this.a = childrenParams;
    console.log('childe change work: ', this.a);
    resolve();
});
navCtrl.push('childrenPage', {
    parentPromise: this.parentPromise
});

<!--子頁面-->
parentPromise = navParams.get('parentPromise');
parentPromise('change from childe').then(() => {
    // 此時會打印 childe change worke: change from childe
    navCtrl.pop();
})
複製代碼

預備知識

這裏代碼稍微有點繞,須要你瞭解的知識有 Promise、箭頭函數、TypeScript,假如你不太瞭解,請戳Promise箭頭函數,瞭解事後再看接下來的內容。

分析

  • parentPromise:
    • 接受一個參數(通常由子頁面傳入),並返回一個 Promise
    • Promise 內部收到參數後就能夠利用這個參數執行一些操做,結束 Promise
  • 子頁面的 parentPromise 執行順序:
    • 傳入參數,在子頁面內執行來自父頁面的操做,執行完畢後再執行 pop 回到父頁面

注意是在子頁面執行 parentPromise 內的代碼,代碼之因此可以跑成功,緣由其實和上面第一種用法的緣由是同樣的,能夠說利用的原理都是同樣,可是和第一種略微不一樣的是:前者是顯示的傳遞 this 給子頁面,控制權所有交給了子頁面,後者是利用箭頭函數將 parentPromise 的 this 綁定在父頁面上,控制權在父頁面上

優勢

  1. 相比以前的作法,此法下降了父頁面與子頁面的耦合,提高了代碼的可維護性。

缺點

  1. 多層級沒法高效的使用。
  2. 相比前一種方法,此法更難理解一點。

我想說

此段代碼是有問題的。並非結果有問題,而是代碼的實際執行行爲與代碼表現的執行預期出現的差別的問題。筆者嘗試對 parentPromise 解讀時的預期是:子頁面傳入參數 —> 子頁面返回 —> 父頁面得到參數而後作相關操做,可是實際執行行爲如以前的分析根本不是這樣的:子頁面傳入參數 —> 得到參數而後作相關操做 —> 子頁面返回,能夠發現實際執行過程當中 parentPromise 內部的 Promise 實際上是沒有太多意義的,在子頁面徹底能夠直接回調而後執行 pop 的操做。這就是筆者對這段代碼不太喜歡的緣由。 這段下面給出筆者的「改進版」供讀者評論。

<!--父頁面-->
public a = 'a';
// 不使用箭頭函數依舊可使用
public popWithParams(childNavCtrl: navController, childeParams: any) {
    // 回到父頁面再操做
    childNavCtrl.pop().then(function() {
        this.a = childrenParams;
        console.log('childe change work: ', this.a);
    }
};
navCtrl.push('childrenPage', {
    popWithParams: this.popWithParams
});

<!--子頁面-->
popWithParams = navParams.get('parentPromise');
popWithParams(navCtrl, 'change from childe');
複製代碼

3. 利用this和回調

<!--此法是前二者的結合,和第二種方法更像,可是用法更簡單,也更容易理解-->

<!--父頁面-->
public a = 'a';

public parentCallback = (childeParams: any) => {
    // this被綁定在父頁面
    // 僅當此處代碼爲異步時(例:數據須要ajax獲取),考慮法2傳遞Promis的用法
    this.a = childrenParams;
    console.log('childe change work: ', this.a);
};
navCtrl.push('childrenPage', {
    parentCallback: this.parentCallback
});

<!--子頁面-->
popWithParams = navParams.get('parentPromise');
parentCallback('change from childe');
navCtrl.pop()
複製代碼

優勢

使用簡單,容易理解,且父子頁面耦合也比較低控制權在父頁面。

缺點

同前二者。

2、事件傳參

1. Ionic 提供的 Events

<!--父頁面-->
constructor(public events: Events) {}

ngOnInit() {
    // 訂閱來自子頁面的事件
    events.subscribe('childParams:doWhenInit', (childParams) => {
        console.log(childParams); // params from childPage
    });  
}

<!--子頁面-->
constructor(public events: Events) {}
getParams() {
  this.events.publish('childParams:doWhenInit', 'params from childPage');
}
複製代碼

預備知識

此爲 Ionic 提供的事件支持,詳細請戳 Ionic Events

優勢

  1. 思路易理解,子頁面發佈一個事件,父頁面訂閱事件並處理。
  2. 父子頁面解耦。
  3. 多層級頁面傳遞參數方便。

缺點

  1. 事件名稱不易管理,應用複雜的狀況下,不少事件名稱須要管理,形成維護上的困難。
  2. 訂閱方即父頁面須要手動取消訂閱,否則可能會訂閱屢次。
  3. 跨頁面傳遞參數時,訂閱方即父頁面須要在事件發佈時已經生成並訂閱事件,才能收到參數。

2. RxJS 的 Subject ,BehaviorSubject

3. Angular 的 EventEmitter

這兩種方法都須要搭配 Service 結合,思路和 Ionic 提供的 Events 相似,此處很少作研究了,感興趣的同窗能夠看看相關內容。

3、Service

<!--pageParamsManagerService-->
// 可在全應用範圍內被訪問及改變
public a = 'no change';

<!--父頁面-->
import 'pageParamsManagerService' from './pageParamsManagerService'

constructor(public ppmService: pageParamsManagerService) {}

// 注意不可放在 onInit 或 ionViewDidLoad鉤子內,由於 push 到子頁面時父頁面並未銷燬, pop 回來時不會執行這兩個鉤子
ionViewWillEnter() {
    // 第一次進入:no change;
    // 子頁面pop進入:change from childePage
    console.log(this.ppmService.a); 
}

<!--子頁面-->
import 'pageParamsManagerService' from './pageParamsManagerService'

constructor(public ppmService: pageParamsManagerService) {
    // 改變公共變量
    this.ppmService.a = 'change from childePage';
}


複製代碼

預備知識

Angular 應用裏有個 Service 的概念,通常來講 Service 都是單例模式且在全應用級別做用域均可訪問(此處不作過多探討),故咱們能夠利用這個特性來達到咱們的傳參目的。

優勢

  1. 使用方便簡單。
  2. 頁面間徹底解耦。
  3. 支持多頁面間訪問,且無頁面生成時間要求,多處頁面也徹底解耦。

缺點

  1. 由於是單例模式且須要在 app.module 引入,須要作好模塊管理,這裏又是一大坑,感興趣的同窗能夠本身瞭解一下。
  2. 若管理不慎會形成意外的狀況,須要時刻注意是 Service 單例的。
  3. 若參數較少,操做很少的狀況下,僅僅爲了傳參引入一個 service 會顯得程序較爲臃腫。

結語

咱們從路由傳參、事件傳參、service 傳參三個方向給出了對應的傳參方案並分析了各自優劣和適用場景,筆者能力有限,有更多方案的同窗歡迎在評論提出,若發現問題的,也但願不吝賜教,謝謝!!

相關文章
相關標籤/搜索