浅析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 传参三个方向给出了对应的传参方案并分析了各自优劣和适用场景,笔者能力有限,有更多方案的同窗欢迎在评论提出,若发现问题的,也但愿不吝赐教,谢谢!!

相关文章
相关标签/搜索