添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
忐忑的黑框眼镜  ·  libicui18n.so.52 ...·  1 年前    · 
活泼的石榴  ·  WPF ...·  1 年前    · 
使用Angular和ionic构建Hybrid App(三)—— 导航、生命周期和浏览器

使用Angular和ionic构建Hybrid App(三)—— 导航、生命周期和浏览器

写在前面:这篇文章的草稿在我的电脑里躺了半年。这段时间困惑非常多,这篇文章也是其中之一,就是这个系列接下来应该用哪种方式来帮助大家了解和使用ionic。很多东西文档上已经写的很清楚,大家看看文档不就行了吗?这个疑惑尽管现在还没有解开,所以还希望各位读者能留言,来谈谈大家想要看到什么。谢谢大家的支持!

ionic尽管使用了Angular作为实现的框架,它内部的导航方式却不同于Angular这种基于路由方式,而是视图栈。

什么是视图栈?其实从字面意思就能理解,无非就是我们把视图的导航以栈的形式缓存,而我们显示的永远是栈顶,在需要显示的时候压栈,在返回的时候出栈。

在ionic中,用 NavController 来管理整个应用的视图栈。它提供了两个最基本的方法来管理视图栈,push()和pop(),在我们需要显示一个页面的时候,通过调用push()方法来展示,这个方法在你需要的时候还能从上一个页面传递参数,而返回上个页面,也仅仅需要调用pop()。另外,还可以通过popToRoot()来返回到根部页面,insert()在堆栈间插入某个页面等等。

这时候可能会问,如果我需要做返回到上个页面后刷新之类的需求,应该怎么处理呢?这里就要介绍一下生命周期机制了。

生命周期,就是一个页面存在在整个业务流程中间的某些状态,比如创建视图、视图加载中、视图加载完成、视图准备销毁、视图销毁中、视图销毁完成等等,通过生命周期事件,我们可以在业务代码中控制我们需要的初始化、数据更新、数据销毁等等。例如:

import { Component } from '@angular/core';
@Component({
    template: 'Hello World'
class HelloWorld {
    ionViewDidLoad() {
        console.log("I'm alive!");
    ionViewWillLeave() {
        console.log("Looks like I'm about to leave :(");
}

ionic在页面导航中提供了一系列的生命周期事件:

ionViewDidLoad: 页面加载完毕。在ionic中,每个页面在创建后只会加载一次,因此这个事件只会触发一次,通常会在这个事件中放置一些初始化代码。
ionViewWillEnter: 页面准备展示。在ionViewDidLoad后触发。每次页面进入栈顶就会触发。
ionViewDidEnter: 页面展示完成。在ionViewWillEnter后触发。每次页面进入栈顶就会触发。
ionViewWillLeave: 页面准备退出,不代表销毁。每次页面退出栈顶就会触发。
ionViewDidLeave: 页面退出完毕,不代表销毁。每次页面退出栈顶就会触发。
ionViewWillUnload:页面将会被销毁。页面销毁前会触发,赶紧缓存一下重要的数据,离开这该死的鬼地方。

通常,我们经常会需要用到返回到上个页面后刷新,就可以在页面中注册ionViewWillEnter事件来进行页面的刷新。

我们将ionic代码编译打包成native app之后,发现在Android平台上返回键能正常的按照我们想要的逻辑进行返回。这得益于ionic提供的Platform插件中可以注册监听返回键registerBackButtonAction()。这时候,我遇到了一个问题,就是我昨天说,我希望能 All in Web

据我所知,ionic在serve阶段(就是我们开发的时候会使用ionic serve命令在浏览器中进行开发)就会生成带有service worker的PWA工程。Platform插件是通过js-bridge来和Android层交互,获取返回键的事件,那我们如何在浏览器中让Android返回键也能很好工作呢?

在Android系统的浏览器中,点击返回键触发的是history.back()事件,而在这个时候,我们可以监听到popstate事件,那么配合ionic我们应该怎么去做返回呢?我直接上 代码

import { Component, ViewChild } from "@angular/core";
import { Nav, Platform, MenuController, IonicApp, App } from "ionic-angular";
import { SplashScreen } from "@ionic-native/splash-screen";
import { StatusBar } from "@ionic-native/status-bar";
import * as moment from "moment";
import { environment } from "../environments/environment";
import { IdleService, RedirectService, SessionService } from "../core";
import { HomePage, LoginPage, PageA, PageB } from "../pages";
@Component({
    templateUrl: "app.html"
export class MyApp {
    @ViewChild(Nav) nav: Nav;
    private version = environment.deployDateTime;
    public rootPage: any = LoginPage;
    public pages: Array<{ title: string, component: any }>;
    public routeHistory: Array<any>;
    constructor(
        public menu: MenuController,
        public platform: Platform,
        private ionicApp: IonicApp,
        private idleService: IdleService,
        private redirectService: RedirectService,
        private sessionService: SessionService,
        private splashScreen: SplashScreen,
        private statusBar: StatusBar) {
        this.routeHistory = [];
        this.initializeApp();
        // used for an example of ngFor and navigation
        this.pages = [
            { title: "Home", component: HomePage },
            { title: "Page A", component: PageA },
            { title: "Page B", component: PageB }
        this.setRootPage();
        this.redirectService.redirectToLogin$.subscribe(() => { this.nav.setRoot(LoginPage); });
    public setRootPage(): void {
        const token: string = this.sessionService.accessToken();
        const tokenExpiration: Date = this.sessionService.expires();
        const currentUtcDate: Date = moment(new Date()).utc().toDate();
        if (token == null || token.length === 0 || tokenExpiration == null || tokenExpiration < currentUtcDate) {
            this.rootPage = LoginPage;
        } else {
            this.rootPage = HomePage;
            this.idleService.startIdleWatch();
    public initializeApp(): void {
        this.platform.ready().then(() => {
            this.setupBrowserBackButtonBehavior();
            this.statusBar.styleDefault();
            this.splashScreen.hide();
    public openPage(page: any): void {
        // reset the content nav to have just this page
        // we wouldn't want the back button to show in this scenario
        this.nav.setRoot(page.component);
    private pushRouteHistory(page: any): void {
        let component: any = page;
        if(page.component) {
            component = page.component;
            this.routeHistory.length === 0 ||
            this.routeHistory.length > 0 && this.routeHistory[this.routeHistory.length - 1] !== component) {
            this.routeHistory.push(component);
    private setupBrowserBackButtonBehavior(): void {
        window.onpopstate = (event) => {
            console.log("<- Back Button Pressed");
            if(this.menu.isOpen()) {
                this.menu.close();
                return;
            if(this.ionicApp) {
                let activePortal: any =
                    this.ionicApp._loadingPortal.getActive() ||
                    this.ionicApp._modalPortal.getActive() ||
                    this.ionicApp._toastPortal.getActive() ||
                    this.ionicApp._overlayPortal.getActive();
                if(activePortal) {
                    activePortal.dismiss();
                    return;
            if(this.routeHistory.length > 1) {
                this.routeHistory.pop();
                if(this.nav.canGoBack()) {
                    this.nav.pop().catch((reason) => {
                        console.log("Unable to navigate back:" + reason);
                } else {
                    this.nav.setRoot(this.routeHistory[this.routeHistory.length - 1]);
        this.nav.viewWillEnter.subscribe((page) => {
            this.pushRouteHistory(page);
        this.nav.viewDidEnter.subscribe((app) => {