2.4 Ionic(Angular/Cordova)框架
2.4.1 概述
如何创建移动互联网的客户端核心要素移动App?一般而言,开发者需要掌握每个平台下的语言:如iOS需用Objective-C或Swift,Android需用Java。如果有一种方法可以只学习一种语言就能在多个平台下使用,那就太好了。答案是肯定的:通过使用Web通用语言和某些框架,开发者能够“一次编码部署到多个移动平台”,这就是所谓的“混合移动App”,因为它用Web技术开发,但融合了移动设备的原生能力。
混合移动应用与传统原生应用不同,它不使用设备原生开发语言,而是用Web技术(HTML、CSS和JavaScript)开发。jQuery Mobile和本节重点介绍的Ionic是当今两个最流行的混合移动App开发框架。
Ionic框架是一个用HTML、CSS和JavaScript构成的用户界面框架,专门用于混合移动App的开发。除了用户界面组件,Ionic也包含一个强大的命令行接口CLI和一套附属服务,如Ionic View和Ionic Creator。
Ionic融合了多种技术:最上层是Ionic框架自身,提供移动应用程序的用户界面层;下一层是Angular,这是一个极其强大的Web应用框架;底层是Apache Cordova,它允许Web应用程序调用设备原生能力并将App转换成原生App。这些技术融合在一起,使Ionic变成了一个创建混合移动App的强大平台。
(1)Ionic框架
Ionic框架的主要功能是提供了Web App开发中所不具备的UI组件。例如,在许多移动App中都有一个UI组件Tab Bar(底部菜单按钮),但这个组件在原生的HTML标签中不存在。Ionic框架扩展了HTML库并提供了这样的组件,由HTML、CSS和JavaScript编写,其行为和外观与原生控件一模一样。Ionic还有一个CLI工具,用于轻松创建、编译和发布Ionic应用程序。Ionic平台还扩展了几个附属功能,包括一个在线的图形用户界面构造工具,用于图形化方式设计Ionic App、打包及更新。
目前Ionic已经是第三版,该版本是一次传统意义上的常规升级,而不是像Ionic1到Ionic2那样的重大升级。Ionic2基本上是一个全新的框架,主要改进包括:全新的导航,彻底控制App的导航体验;原生支持,支持更多原生功能,能轻松调用设备提供的全部能力;强大的主题系统,轻松匹配品牌颜色和设计;为Android App提供完整的材料设计支持;支持开发运行在Windows Universal平台上的App。Ionic框架的主要目标是UI层,它通过集成Angular和Cordova的方式来提供接近原生的体验。
(2)Angular
Ionic技术栈的下一层是Angular,这是一个由谷歌支持的开源项目。自2009年发布以来,Angular已变成最流行的Web应用框架,用于建造复杂的、单页面Web App的MVW框架。Ionic团队利用Angular框架提供的强大功能,将其集成到Ionic中。例如,Ionic的定制UI组件其实就是Angular组件。随着Angular2的推出,框架也进行了大量改进。
(3)Cordova
Ionic技术栈的最后一层是Apache Cordova。Cordova起源于Nitobi Software公司于2009年所开发的一个开源项目,能够利用Web技术构建嵌入WebView的原生App。2011年,Adobe System收购了Nitobi,项目重命名为Cordova。Cordova提供了WebView和设备原生层之间的接口,弥补这两个技术之间的空隙。许多功能以插件系统的形式提供,使核心库保持较小规模。除了Android和iOS,Cordova还支持其他移动平台,如Windows Phone、Blackberry和FireOS。除了库本身,Cordova还拥有自己的命令行工具,用于搭建框架、编译和部署移动应用。Ionic CLI是基于Cordova CLI构建的。
2.4.2 安装
Ionic开发环境的安装看似复杂,其实非常简单,只需以下4个步骤。
(1)安装Node.js
Ionic是基于Node.js构建的,Node是一个能在浏览器之外运行JavaScript的平台。安装Node时,下载安装包并安装,安装后打开终端并输入node -v命令来打印Node当前安装的版本号。同时确认NPM是否更新,运行npm -v查看NPM安装版本。
(2)安装Git
Git是一个版本控制工具,Ionic CLI使用Git来管理模板。下载并安装Git,安装完成后打开git bash命令行工具,输入git --version命令测试安装成功。
(3)安装Cordova CLI
打开Git Bash,通过NPM安装Cordova CLI,输入命令npm install -g cordova。等待安装完成后,输入cordova --version验证安装,出现版本信息表示成功。
(4)安装Ionic CLI
打开Git Bash,通过NPM安装Ionic CLI,输入命令npm install -g ionic。等待安装完成后,输入ionic --version验证安装,出现版本信息表示成功。
2.4.3 新建Ionic项目
Ionic CLI提供了一个简单的命令ionic start来创建Ionic项目。这个命令会在当前目录下创建一个基本的Ionic应用。Ionic框架通过一系列初始模板来创建项目脚手架,包括blank白板、sidemenu侧菜单和tabs底部菜单标签页。运行命令ionic start myApp tabs --cordova指定使用tabs模板并集成Cordova框架。创建完成后生成myApp目录,切换到该目录cd myApp,在项目目录下使用Ionic CLI内置命令ionic serve启动一个简单Web服务器,并打开浏览器自动加载App,在浏览器中看到Tab风格的Ionic App。
2.4.4 Angular和TypeScript基础
1. Angular2是什么
本书中所述的Angular是目前通常称为Angular2的版本,以和第一代版本Angular1(AngularJS)区别。Angular2是一个全新框架,在2016年的ngConf大会上,Angular团队发布RC1版本,框架只使用Angular命名。因此,本书将Angular2称为Angular。
(1)组件
从Angular1到Angular2的一个最大改变是不再依赖scope、controller或directive,而使用基于组件的方法构建元素及其相关逻辑。一个Angular组件的例子如下:
import { Component } from '@angular/core'
@Component({
selector: 'my-first-component',
template: `<div>Hello, my name is {{name}}.<button (click)="sayMyName()">Log my name</button></div>`
})
export class MyComponent {
constructor() {
this.name = 'Inigo Montoya'
}
sayMyName() {
console.log('Hello. My name is', this.name, '. You killed my father. Prepare to die.')
}
}
这和Ionic创建自己的组件库是一样的方式。首先从Angular库中导入Component模块,然后用@Component装饰器添加元数据,指定自定义HTML选择器。模板可以是行内风格或外部引用。在装饰器之后,导出类的定义,在构造函数中初始化变量,并定义公共方法。
(2)输入
Angular基于组件模型,需要将信息传递到组件的机制,由Input模块处理。例如,组件<current-user>需要一个user参数,使用方式如下:
<current-user [user]="currentUser"></current-user>
组件定义如下:
import { Component, Input } from '@angular/core';
@Component({
selector: 'current-user',
template: '<div>{{user.name}}</div>'
})
export class UserProfile {
@Input() user;
constructor() {}
}
在类定义中,使用@Input绑定到user变量,Angular会传递currentUser变量给组件,让模板渲染user.name值。
(3)模板
模板是一个HTML片段,由Angular将指定元素和属性组装成动态内容。
- []绑定属性:当组件需要解析和绑定一个变量时,使用[]语法。例如:
<card-header [themeColor]="currentColor"></card-header>。 - ()事件处理:监听用户事件,如单击事件,将子组件中的事件用圆括号括住赋给父组件。例如:
<my-component (click)="onUserClick($event)"></my-component>。 - [()]双向数据绑定:结合属性绑定和事件绑定语法。例如:
<input [(ngModel)]="username">,使组件的this.userName与输入值保持同步。 - *星号:在指定前使用星号告诉Angular以指定方式处理模板。例如:
<my-component *ngFor="let item of items"></my-component>,ngFor将模板变成for each循环。 - {{}}插值:由一对双大括号组成,插值变量上下文是组件类本身,是一种从数据模型到模板视图的单向数据流动。例如:
<div>Hello, my name is {{name}}</div>。
(4)事件
Angular中的事件在模板中用圆括号标记,并触发组件类中的方法。例如:
<button (click)="clicked()">click</button>
组件类定义:
class MyComponent {
clicked() {
// 处理单击事件
}
}
当按钮被单击时,clicked()方法被调用。事件像普通DOM事件,能向上冒泡并向下广播。如果需要访问事件对象,可以在事件回调方法中传递$event参数。
自定义事件:如果组件需要向其他组件广播自定义事件,可以导入Output和EventEmitter模块,用@Output装饰器定义自定义事件。例如:
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'user-profile',
template: '<div>Hi, my name is</div>'
})
export class UserProfile {
@Output() userDataUpdated = new EventEmitter();
constructor() {
this.userDataUpdated.emit(this.user);
}
}
触发事件广播时,在自定义事件上调用emit方法并传入参数。在App中使用组件并绑定事件:
<user-profile (userDataUpdated)="userProfileUpdated($event)"></user-profile>
生命周期事件:Angular App和组件都有生命周期事件,允许开发者访问生命周期中的关键环节,如创建、渲染、销毁。常用事件包括:
- NgModule:通过NgModule函数重新实现App的引导方式,使用元数据对象告诉Angular如何编译和运行模块代码。
- 组件初始化事件:ngOnInit在组件初始化真正完成时触发。
- 其他事件:ngOnDestroy、ngDoCheck、ngOnChanges、ngAfterContentInit等。建议使用Ionic事件。
(5)管道符
管道符原先叫作过滤器,将一个值转换成新值。除了自定义管道,Angular提供常用管道操作,如Date、UpperCase、LowerCase、Currency、Percent等。
(6)@ViewChild
当父组件需要读写子组件的值或方法时,可以将子组件以ViewChild的形式注入父组件。例如:
import { Component, ViewChild } from '@angular/core';
import { UserProfile } from '../user-profile';
@Component({
template: '<user-profile (click)="update()"></user-profile>',
directives: [UserProfile]
})
export class MasterPage {
@ViewChild(UserProfile) userProfile: UserProfile;
constructor() {
this.userProfile.sendData();
}
}
从Angular Core导入ViewChild和UserProfile组件,在@Component装饰器中设置directives属性,构造器中使用@ViewChild装饰器将userProfile变量设置为注入的组件UserProfile,然后调用子组件的方法。
2. 核心概念
Angular官方文档列出8个核心概念:模块、组件、模板、元数据、数据绑定、指令、服务、依赖注入。从总览角度看,这些概念在应用中的位置关系显示模板与用户直接交互,模板是组件的要素之一,另一要素是组件类,用以维护组件的数据模型及功能逻辑。模板通过元数据指定,元数据还包含其他重要信息,告诉Angular如何解释一个普通类,元数据结合普通类构成组件。指令是独立构成,与模板密切关联,增强模板特性。服务是独立构成,封装单一功能逻辑单元。服务通过依赖注入机制引入组件内部,作用域可全局或局部。
最重要的概念是组件。纵观整个Angular应用,组件始终处于交互的出入口,这正是Angular基于组件设计的体现。
(1)组件、元数据及数据绑定
Angular框架基于组件设计,应用由一系列组件构成,层层嵌套形成组件树。组件由两部分组成:@Component()装饰器和TypeScript/ES6类。装饰器注入元数据,如selector和template。selector声明CSS3选择器,即组件标签名;template为模板。
组件以树的形式组织,父子组件之间存在双向数据流动。每个组件可定义输入输出属性,作为对外接口。例如,Contact组件:
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'contact',
template: '<p>张三</p>'
})
export class ContactComponent {
@Input() item: ContactModel; // 输入属性
@Output() update: EventEmitter<ContactModel>; // 输出属性
constructor() { }
modify() {
this.update.emit(newValue);
}
}
父组件ContactList使用Contact组件:
@Component({
selector: 'contact-list',
template: `
<contact [item]="items[0]" (update)="doUpdate(newValue)"></contact>
`
})
export class ContactListComponent {
items: ContactModel[];
constructor() {}
doUpdate(item: ContactModel) {
// 处理更新
}
}
属性绑定[item]数据从父组件流向子组件,事件绑定(update)数据从子组件流向父组件。数据绑定用于组件数据模型和模板视图之间的数据传递,也用于父子组件数据传递,模板充当桥梁角色。
Angular的数据流动机制由变化监测机制驱动。Angular是响应式系统,每次数据变动几乎能实时处理并更新视图。变化监测在异步事件触发后执行,通过Zones库捕获异步事件。每个组件维护一个独立的变化监测器,应用有对应的变化监测树。变化监测操作始于根组件,以深度优先原则遍历执行,性能高。
当检测到数据变动时,结合数据绑定驱动模板视图实时更新。Angular提供生命周期钩子捕获数据变动时机,如ngOnChanges。常用生命周期钩子:
- 构造函数:组件类初始化。
- ngOnInit:组件初始化阶段,处理业务逻辑。
- ngOnChanges:输入数据变动时触发。
- ngOnDestroy:组件销毁前触发,用于清理工作。
(2)模板
Angular模板基于HTML,但提供强大语法体系。数据绑定是模板最基本功能,包括属性绑定、事件绑定和插值。插值语法由双大括号{{}}组成,变量是组件类本身,单向数据流动从数据模型到模板视图。
双向数据绑定结合属性绑定和事件绑定,使用[()]语法糖,如<input [(ngModel)]="contact.name">,形成双向数据关联。
管道用于数据格式化显示,使用竖线|表示,如{{contact.telephone|phone}},将电话号码美化输出。管道支持自定义,Angular提供内置管道如number、date等。
(3)指令
指令与模板关系密切,改变样式或布局。指令分为结构指令和属性指令。结构指令添加、修改或删除DOM,改变布局,如ngIf:
<button *ngIf="canEdit">编辑</button>
当canEdit为true时按钮显示,为false时从DOM移除。注意结构指令的*号是语法糖。
属性指令改变元素外观或行为,如ngStyle:
<span [ngStyle]="setStyles()">{{contact.name}}</span>
样式由setStyles()函数计算,返回样式对象。指令支持自定义,实现UI层面逻辑复用。
(4)服务
服务是封装单一功能的单元,类似于工具库,作为组件的功能扩展。服务可以是简单字符串、JSON数据、函数或类。例如日志服务:
@Injectable()
export class LoggerService {
private level: string;
setLevel(level: string) {
this.level = level;
}
debug(msg: string) { }
warn(msg: string) { }
error(msg: string) { }
}
@Injectable()是服务类装饰器。服务专注于日志功能,组件可复用,避免重复实现。
(5)依赖注入
依赖注入是Angular的卖点,通过依赖注入机制,服务等模块可引入到组件、模块或其他服务中,开发者无须关心模块初始化。例如,组件注入日志服务:
import { LoggerService } from './logger-service';
@Component({
selector: 'contact',
template: '...',
providers: [LoggerService]
})
export class ContactListComponent {
constructor(logger: LoggerService) {
logger.debug('xxx');
}
}
@Component装饰器中的providers元数据创建注入器对象并实例化LoggerService。构造函数声明LoggerService类型参数,Angular通过类型匹配传入实例。
组件级注入器是全局注入器的子注入器。在根组件注入服务,整棵组件树都能使用并保持单例。如果组件分支需要新实例,可单独注入服务,该组件及其子组件使用新实例,实现分层注入。注入器对象可抽象为注入树,Angular从宿主组件注入器查找匹配实例,向上查找直到顶层,找不到则抛出错误。
(6)模块
模块有两层含义:物理模块(文件模块)和逻辑模块(功能单元)。逻辑模块对应用零散的组件、指令、服务按功能归类包装。模块关系示意图显示模块导入导出功能。
模块例子:
@NgModule({
imports: [SomeModule],
declarations: [SomeComponent, SomeDirective, SomePipe],
providers: [LoggerService],
exports: [SomeComponent, SomeDirective, SomePipe]
})
export class AppModule { }
@NgModule()装饰器声明模块。imports和exports属性控制模块间导入导出。模块可对外暴露构件,同时隐藏内部实现。
服务可注入到组件或模块,区别在于作用域。注入到模块的服务在全局使用,注入到组件的服务仅在该组件及子组件使用。
模块最佳实践:
- 根模块:应用启动入口。
- 特性模块:新增功能封装。
- 共享模块:公共部分封装。
- 核心模块:全局组件或服务,只导入到根模块。
Angular封装常用模块,如ApplicationModule、CommonModule、BrowserModule、FormsModule、ReactiveFormsModule、RouterModule、HttpModule等。
(7)应用启动
Angular通过引导运行根模块启动应用,引导方式有动态引导和静态引导。动态引导在浏览器编译代码,静态引导在开发时工程打包阶段编译,加载编译后代码,性能更优。
动态引导示例:
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
静态引导示例:
import { platformBrowser } from '@angular/platform-browser';
import { AppModuleNgFactory } from './app.module.ngfactory';
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
动态引导开发流程简单,适合小型项目或开发阶段;静态引导需预编译,性能提升明显,推荐使用。
3. JavaScript也在进化中——理解ES6和TypeScript
近几年来,出现许多改进JavaScript的语言,如CoffeeScript、AtScript、Dart、ES6、TypeScript等,它们通过转译成标准JavaScript使用。ES6是JavaScript下一代官方版本,提供类型检查和面向对象特性。TypeScript是Angular和Ionic App开发的主要语言,扩展JavaScript语法,添加ES6规范和基于类的面向对象编程特性。TypeScript由微软和谷歌支持,可能成为前端开发主流。
(1)搭建TypeScript开发环境
安装TypeScript转译器:
npm install -g typescript
版本检查:
tsc --version
简单使用:创建Hello.ts文件,运行tsc Hello.ts编译生成Hello.js。
(2)字符串新特性
多行字符串:使用双撇号声明字符串,自动换行。
字符串模板:通过${表达式}进行字符串插值。
(3)变量、参数和类型新特性
let关键字指定变量作用域限于最近块。
参数类型:参数名称后使用冒号指定类型,函数返回值也可类型化。
参数默认值:参数声明后用等号指定默认值。
可选参数:参数声明后用问号标明可选。
类型化数组:在类型声明后添加[]后缀。
特殊类型:any、null、undefined、void。
(4)函数新特性
Rest and Spread操作符:声明任意数量方法参数,使用三个点“...”。
generator函数:控制函数执行过程,手工暂停和恢复,使用function*和yield语句。
Promise对象:用于延迟操作和异步操作,处理单个值同步。
Observable对象:用RxJS库实现,处理多个值同步。
(5)表达式和循环
箭头表达式:消除传统匿名函数的this指针问题。
forEach()遍历数组,for in遍历索引,for of遍历元素值。
(6)面向对象特性
TypeScript类:类是TypeScript核心。
定义类:
class Person {
constructor(public name: string) {
this.name = name;
}
eat() {
console.log("im eating");
}
}
class Employee extends Person {
code: string;
constructor(name: string, code: string) {
super(name);
this.code = code;
}
work() {
super.eat();
this.doWork();
}
private doWork() {
console.log("im working");
}
}
泛型:参数化类型,限制集合内容。
接口:建立代码约定,其他开发者调用方法或创建类时必须遵循。
模块:将代码分割为可重用单元,使用export和import关键字。
注解:为程序元素加说明,如Angular中的@Component。
类型定义文件:帮助在TypeScript中使用已有的JavaScript工具包。
2.4.5 Apache Cordova(PhoneGap)
(1)概述
Apache Cordova是一个开源框架,允许移动App开发者用HTML、CSS和JavaScript创建针对各种移动设备的本地应用。Cordova将Web应用渲染到原生Web View中,Web View是原生App组件,用于显示Web内容。Web App在这个容器中运行,像在移动浏览器中一样,可以打开外部HTML页、执行JavaScript、播放媒体、与远程服务器交互。这种移动App类型称作混合App。
Cordova架构显示Web View作为容器。Cordova还提供JavaScript API访问设备特性,如联系人数据库,通过一系列插件暴露给开发者。插件提供Web App和设备原生能力之间的桥接层。Cordova项目维护核心插件集合,更多功能通过第三方插件提供。
Cordova优势:构建混合移动应用程序,跨平台如iOS、Android、Windows Phone等;开发比原生应用更快,节省时间;使用JavaScript,无需学习平台特定语言;有大量社区插件。
(2)深入了解Cordova
Cordova CLI使配置简单,搭建项目脚手架并配置支持指定移动平台,集成和管理插件。每个Cordova App通过config.xml文件配置,控制App的方方面面,从图标、插件到平台相关设置。它基于W3C的Package Web App规范。Ionic CLI生成基本的config.xml文件。
config.xml主要元素:widget(应用程序反向域值)、name(应用程序名称)、description(说明)、author(作者)、content(起始页)、plugin(安装的插件)、access(控制对外部域的访问)、allow-intent(控制意图访问)、platform(构建平台)。
Cordova强大之处是插件库。插件是一些代码,允许JavaScript和原生组件交互,使App能调用Web App不具备的原生设备能力。有核心插件和第三方插件。从PhoneGap 3.0开始,所有插件抽离成单独元素,可单独升级。
Cordova缺少用户界面组件,只提供基本HTML控件,不提供原生SDK组件。如需tab bar组件,需自己编写或使用第三方框架如Ionic。Cordova使用的Web View来自设备内置,编译App时不会放进Web View。在旧移动设备上,可能需要替换Web View为最新版本,如谷歌的Chromium,以获得最新浏览器特性,但会增加App大小。
2.4.6 理解Ionic
1. Ionic页面的构成
每个Ionic页面由三种基本文件组成:HTML文件定义显示组件;SASS文件定义可视化样式;TypeScript文件提供自定义功能。
(1)HTML文件构成
Ionic组件页面不需要等标签,HTML文件在index.html中定义。只需定义展示给用户的组件,混合传统HTML标签和自定义标签,自定义标签定义Ionic组件,如、、。Ionic组件可接受属性定义设置,如ID值或CSS类,也有专门属性。
(2)SASS/SCSS文件
Ionic App界面用CSS定义,但用SASS创建。SASS比CSS有好处,包括变量声明,如$company-brand: #ff11dd,然后引用变量。所有Ionic组件可使用SASS变量设置样式。SASS支持嵌套CSS语法,编写更简洁。页面.scss文件定义和页面相关的CSS,App级别主题样式在app.core.scss文件中定义。
(3)理解TypeScript
TypeScript文件编写页面交互逻辑相关的Angular/TypeScript代码,定义需要导入的代码模块,如组件或Angular模块。
2. Ionic框架的资源优势
Ionic基于Angular和Cordova,Angular负责界面交互和业务逻辑处理,Cordova搭建JavaScript与设备功能的桥梁,调用API使用硬件设备功能如摄像头、麦克风。Ionic还开发了大量适合移动设备使用的界面控件和CLI工具。
- 预置大量资源,包括图标、控件、特定样式等,数百个内置图标即用。
- 针对移动设备特征优化内置控件,如导航、内容块、滚动、侧栏菜单、键盘等。
- 针对表格、按钮、列表和一般布局的内置CSS模块,产生层次分明布局和类似原生应用的视觉感。
- 沿用Angular优秀设计,通过框架复用减少代码量,专注于业务逻辑处理和数据分流格式化。
- Ionic配套工具CLI使创建、转化、安装调试变得简便。
3. Ionic项目剖析
(1)第一次产生Ionic2应用程序的项目结构
包括config.xml、hooks、ionic.config.json、node_modules、package.json、platforms、plugins、resources、src、tsconfig.json、tslint.json。大部分时间花在src文件夹,包含应用程序逻辑。
(2)src/index.html
项目起始入口,包含标签,ionic查找这个标签运行App。本地部署时打包Cordova。代码编译打包的最终文件,连接ionic、Angular和App。
(3)简要描述项目结构
- config.xml:配置应用程序名称和包名,用于安装到实际设备。
- src:应用程序源代码。
- node_modules:包含npm包。
- package.json:包构建ionic应用程序必需内容。
- platforms:平台具体构建、构建工具和包/库存储。
- plugins:Cordova插件,允许应用调用移动设备本地功能。
- resources:包含平台特定资源,如图标和启动屏幕。
(4)src/app/app.module.ts
App入口点,定义根模块控制应用,类似Ionic和Angular1的ng-app。设置根组件到MyApp,是App加载的第一个组件。在app.component.ts中,设置src/app/app.html模板。
(5)src/app/app.html
模板中建立ion-menu作为菜单,ion-nav组件作为主要内容区域。ion-menu的[content]属性绑定到本地变量ion-nav中的content。操作按钮执行*ngFor指令,遍历页面集合生成模板。对应ts文件是src/app/app.component.ts。
(6)src/app/app.component.ts
定义MyApp类,@ViewChild(Nav) nav: Nav; rootPage设置为UsersPage;pages数组定义页面列表;构造函数初始化App并设置页面;openPage方法关闭菜单并导航到新页面。