导航
导航
文章目录󰁋
  1. 一、介绍
    1. 1.1 ionic 特点
    2. 1.2 Ionic 和 Cordova(phonegap)、Angular 关系
  2. 二、环境搭建
    1. 2.1 Ionic初始化构建
    2. 2.2 Genymotion 安卓模拟器
    3. 2.3 在IOS环境下体验
    4. 2.4 在安卓下体验
    5. 2.5 在浏览器/微信下体验
    6. 2.6 ionic的常用命令
  3. 三、Ionic3.x+ 目录结构分析及创建组件
    1. 3.1 Ionic3.x 目录结构分析
    2. 3.2 创建组件
    3. 3.3 创建页面以及页面跳转
  4. 四、Ionic页面生命周期
  5. 五、API的使用
    1. 5.1 图片上传
    2. 5.2 Icon 本地存储的使用
    3. 5.3 二维码扫描
    4. 5.4 读取版本信息
  6. 六、页面之间的传参
    1. 6.1 js跳转方式
    2. 6.2 HTML传参
  7. 七、管道的使用
    1. 8.2 自定义样式
    2. 8.3 全局设置夜间模式切换
  8. 九、组件化开发-自定义组件
    1. 9.1 自定义组件
    2. 9.2 [hidden] [style] [class] 动态控制组件
  9. 十、指令
  10. 十一、常用组件使用
    1. 11.1 ModalController/LoadingController/ToastController
    2. 11.2 Refresh组件
    3. 11.3 List组件
    4. 11.4 button组件
    5. 11.5 card组件
    6. 11.6 表单组件
  11. 十二、Ionic打包上线流程
    1. 12.1 图标生成
    2. 12.2 启动图生成
    3. 12.3 打包前细节处理
    4. 12.4 打包部署
    5. 12.5 上架流程
      1. 12.5.1 IOS的打包
      2. 12.5.2 安卓版本打包
      3. 12.5.3 网站微信端发布
  12. 十三、一些问题记录
    1. 13.1 问题
    2. 13.2 技巧
  13. 十四、更多参考
    1. 14.1 项目学习
    2. 14.2 文档参考

混合App之Ionic4小结篇

折腾了一段时间Typescriptangular基础,终于可以学Ionic4

没有angular基础,看一下这篇文章 http://blog.poetries.top/2019/01/09/angular7-intro-summary

一、介绍

Ionic 是一款基于 AngularCordova 的强大的 HTML5 移动应用开发框架 , 可以快速创建一 个跨平台的移动应用。可以快速开发移动 App、移动端 WEB 页面、微信公众平台应用,混 合 app web页面。

1.1 ionic 特点

  • ionic 基于Angular语法,简单易学。
  • ionic 是一个轻量级框架。
  • ionic 完美的融合下一代移动框架,支持 Angularjs 的特性, MVC ,代码易维护。
  • ionic 提供了漂亮的设计,通过 SASS 构建应用程序,它提供了很多 UI 组件来帮助开发者开发强大的应用。
  • ionic 专注原生,让你看不出混合应用和原生的区别
  • ionic 提供了强大的命令行工具。
  • ionic 性能优越,运行速度快。

1.2 Ionic 和 Cordova(phonegap)、Angular 关系

ionic = Cordova + Angular + ionic CSS

Ionic是完全基于谷歌的 Angular 框架,在 Angular 基础上面做了一些封装,让我们可以更快 速和容易的开发移动的项目。Ionic 调用原生的功能是基于 Cordova,Cordova 提供了使用 JavaScript 调用 Native 功能,ionic 自己也封装了一套漂亮的 CSS UI 库。

二、环境搭建

2.1 Ionic初始化构建

https://ionicframework.com/getting-started#cli

1
2
# 全局安装
npm install -g ionic
  • ionic info (查看当前ionic的全部版本信息)

image.png

1
2
3
4
5
ionic start myApp tabs # 建议使用初始化

cd myApp

ionic serve

image.png

  • ionic serve 运行项目

2.2 Genymotion 安卓模拟器

使用方法 https://www.jianshu.com/p/aabc4fd01311

2.3 在IOS环境下体验

需要配备mac,安装xcode

1
2
3
4
5
# mac下需要添加sudo
sudo ionic cordova platform add ios

# 注意获取目录权限的问题
chmod -R 777 项目文件夹名

真机调试与发布需要Apple开发者账号

打开xcode选择platform下中ios文件夹,点击运行项目

image.png

2.4 在安卓下体验

1. 添加android

1
2
3
4
5
6
7
8
ionic cordova platform add android

# 注意获取目录权限的问题
chmod -R 777 项目文件夹名

# 直接使用Android studio 进行调试链接

# 打包成apk拖入Genymotion调试

2. 下载android studio 打开/platform/andriod文件

image.png

3. 然后连接android studio结合geny生成apk调试

image.png

2.5 在浏览器/微信下体验

1. 添加browser文件夹

1
ionic cordova platform add browser

2. 打包

ionic cordova build browser

image.png

image.png

3. 运行

1
2
3
npm run serve

# 浏览器打开 http://localhost:8100

4. 部署

www目录部署到服务器上即可

在微信下体验 注意微信title问题

2.6 ionic的常用命令

1. 基本命令

  • ionic g page myPage 创建页面
  • ionic g provider MyData 创建provider
  • ionic serve 在浏览器中看
  • ionic platform add/remove android/ios 添加删除平台
  • ionic build android/ios 快捷打包(IOS最好通过xcode打包发布)

2. 辅助命令

  • ionic info 查看关于ionic的系统消息
  • ionic emulate android/ios 模拟器中打开
  • ionic cordova plugin list 查看插件安装列表

3. 正式发布需要的命令

  • ionic cordova platforms add android 添加安卓平台
  • ionic cordova build android --release 打包成apk

三、Ionic3.x+ 目录结构分析及创建组件

3.1 Ionic3.x 目录结构分析

1. 整体目录结构

image.png

  • hooks:编译 cordova 时自定义的脚本命令,方便整合到我们的编译系统和版本控制系统中
  • node_modules :node 各类依赖包
  • resources :android/ios 资源(更换图标和启动动画)
  • src:开发工作目录,页面、样式、脚本和图片都放在这个目录下
  • www:静态文件
  • platforms:生成 android 或者 ios 安装包路径(platforms\android\build\outputs\apk:apk
    所在位置)执行 cordova platform add android 后会生成
  • plugins:插件文件夹,里面放置各种 cordova 安装的插件
  • config.xml: 打包成 app 的配置文件
  • package.json: 配置项目的元数据和管理项目所需要的依赖
  • tsconfig.json: TypeScript 项目的根目录,指定用来编译这个项目的根文件和编译选项
  • tslint.json:格式化和校验 typescript

2. src目录

image.png

  • app:应用根目录
  • assets:资源目录(静态文件(图片,js 框架)
  • pages:页面文件,放置编写的页面文件,包括:htmlscssts
  • theme:主题文件,里面有一个 scss 文件,设置主题信息。

3. Ionic3.x src 下面文件分析

image.png

4. app.module.ts 分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//这个根模块会告诉 ionic 如何组装该应用

import { NgModule, ErrorHandler } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { IonicStorageModule } from '@ionic/storage';
import { MyApp } from './app.component';

// 其他组件
import { HomePage } from '../pages/home/home';
import { DiscoveryPage } from '../pages/discovery/discovery';
import { ChatPage } from '../pages/chat/chat';
import { NotificationPage } from '../pages/notification/notification';
import { MorePage } from '../pages/more/more';
import { LoginPage } from '../pages/login/login';
import { TabsPage } from '../pages/tabs/tabs';

import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { ApiProvider } from '../providers/api/api';

@NgModule({
declarations: [
MyApp,
HomePage,
DiscoveryPage,
ChatPage,
NotificationPage,
MorePage,
LoginPage,
TabsPage
],
imports: [
BrowserModule,
HttpClientModule,
IonicModule.forRoot(MyApp),
IonicStorageModule.forRoot()
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
HomePage,
DiscoveryPage,
ChatPage,
NotificationPage,
MorePage,
LoginPage,
TabsPage
],
providers: [
StatusBar,
SplashScreen,
{provide: ErrorHandler, useClass: IonicErrorHandler},
ApiProvider
]
})
export class AppModule {}

3.2 创建组件

  1. cd 到我们的项目目录
  2. 通过 ionic g component 组件名称创建组件

输入ionic g后,可以创建的组件如下

image.png

  1. 创建完成组件以后会在 src 目录下面多一个 components 的目录,这个目录里面有我们用命令创建的所有的组件

image.png

  1. 如果我们要使用这些组件必须在 app.module.ts 里面注册我们的模块,注册完成后就可以在 pages 里面的其页面里面使用这些组件

image.png

image.png

3.3 创建页面以及页面跳转

1. 创建页面

1
ionic g page news

2. 跳转

1
<button ion-button (click)="pushButton">执行button跳转</button>

image.png

四、Ionic页面生命周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 页面被加载完成后调用的函数,切换页面时并不会进行重新加载,因为有cache的存在
onPageLoaded() {
console.log('page 1: page loaded.');
}

// 页面即将进入的时候
onPageWillEnter() {
// 在这里可以做页面初始化的一些事情
console.log('page 1: page will enter.');
}

// 页面已经进入的时候
onPageDidEnter() {
console.log('page 1: page did enter.');
}

// 页面即将离开的时候
onPageWillLeave() {
console.log('page 1: page will leave.');
}

// 页面已经离开的时候
onPageDidLeave() {
console.log('page 1: page did leave.');
}

// 从 DOM 中移除的时候执行的生命周期
onPageWillUnload() {

}

// 从 DOM 中移除执行完成的时候
onPageDidUnload() {

}

五、API的使用

5.1 图片上传

文档 https://ionicframework.com/docs/native

1
2
3
4
5
6
7
npm i --save @ionic-native/camera @ionic-native/file @ionic-native/file-path @ionic-native/transfer

# 插件 mac下需要sudo
sudo ionic cordova plugin add cordova-plugin-camera
sudo ionic cordova plugin add cordova-plugin-file
sudo ionic cordova plugin add cordova-plugin-file-transfer
sudo ionic cordova plugin add cordova-plugin-filepath

5.2 Icon 本地存储的使用

https://ionicframework.com/docs/storage

1. 安装插件

1
2
3
ionic cordova plugin add cordova-sqlite-storage

npm install --save @ionic/storage

2. src/app/app.module.ts导入配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { IonicStorageModule } from '@ionic/storage';

@NgModule({
declarations: [
// ...
],
imports: [
BrowserModule,
IonicModule.forRoot(MyApp),
IonicStorageModule.forRoot()
],
bootstrap: [IonicApp],
entryComponents: [
// ...
],
providers: [
// ...
]
})
export class AppModule {}

3. 在你的页面中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { Storage } from '@ionic/storage';

export class MyApp {
constructor(private storage: Storage) { }

...

// set a key/value
storage.set('name', 'Max');

// Or to get a key/value pair
storage.get('age').then((val) => {
console.log('Your age is', val);
});
}

5.3 二维码扫描

https://ionicframework.com/docs/native/qr-scanner

1. 安装插件

1
2
3
# 安装插件
$ ionic cordova plugin add cordova-plugin-qrscanner
$ npm install --save @ionic-native/qr-scanner

2. 在src/app/app.module.ts中导入

1
2
3
4
5
6
7
8
import { QRScanner, QRScannerStatus } from '@ionic-native/qr-scanner';

providers: [
StatusBar,
SplashScreen,
{provide: ErrorHandler, useClass: IonicErrorHandler},
QRScanner // 导入插件
]

3. 在页面中使用

1
2
# 新建扫描页面
ionic g page scan
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/** scan.scss **/
page-scan {
html,
body,
ion-app,
ion-content,
ion-page,
.nav-decor {
background-color: transparent !important;
}
.line {
position: absolute;
z-index: 999999;
top: 15px;
height: 2px;
width: 100%;
background-color: #009900; //动画
animation: scan 1s infinite alternate;
-webkit-animation: scan 1s infinite alternate;
}
@keyframes scan {
from {
top: 20%;
}
to {
top: 80%;
}
}
}
1
2
3
4
5
6
7
8
<!--scan.html-->
<ion-header>
<ion-navbar>
<ion-title></ion-title>
</ion-navbar>
</ion-header>

<div class="line"></div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// scan.ts
import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams, AlertController } from 'ionic-angular';
import { QRScanner, QRScannerStatus } from '@ionic-native/qr-scanner';

@Component({
selector: 'page-scan',
templateUrl: 'scan.html',
})
export class ScanPage {

constructor(public navCtrl: NavController,
public navParams: NavParams,
public alertCtrl: AlertController,
public qrScanner: QRScanner) {
}

ionViewDidLoad() {
console.log('ionViewDidLoad ScanPage');
}
ionViewDidEnter() {
this.scanQRCode();
}

scanQRCode() {
this.qrScanner.prepare()
.then((status: QRScannerStatus) => {
if (status.authorized) {
window.document.querySelector('body').classList.add('transparent-body');
let scanSub = this.qrScanner.scan().subscribe((text: string) => {
let alert = this.alertCtrl.create({
title: '二维码内容',
subTitle: text,
buttons: ['OK']
});
alert.present();
scanSub.unsubscribe();
});

this.qrScanner.show();

}
else if (status.denied) {
//提醒用户权限没有开
}
else {

}
})
.catch((e: any) => console.error('Error :', e));
}

}

4. 调用scan页面

1
2
3
4
<button ion-item (click)="gotoScanQRCode()">
<ion-icon name="qr-scanner" item-start color="dark"></ion-icon>
<ion-label>扫描二维码</ion-label>
</button>
1
2
3
4
5
6
7
8
9
10
11
import { ScanPage } from '../scan/scan'; // 引入新建的扫描页面

constructor(public navCtrl: NavController,
public navParams: NavParams) {
super()
}

// 跳转到二维码扫描界面,加上'animate': false参数是为了相机能够在整个屏幕显示,否则相机出不来
gotoScanQRCode() {
this.navCtrl.push(ScanPage, null, {'animate': false})
}

5.4 读取版本信息

https://ionicframework.com/docs/native/app-version

1. 安装插件

1
2
$ ionic cordova plugin add cordova-plugin-app-version
$ npm install --save @ionic-native/app-version

导入app.module.ts

1
2
3
4
import { AppVersion } from '@ionic-native/app-version';
providers: [
AppVersion
]

2. 新建一个页面展示version

1
ionic g page versions

3. 在app.modules.ts中导入

4. version页面配置

在浏览器中不可以调试,需要真机调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<ion-header>

<ion-navbar>
<ion-title>版本信息</ion-title>
</ion-navbar>

</ion-header>

<ion-content>
<ion-list>
<ion-item>
AppName: {{appName}}
</ion-item>
<ion-item>
PackageName: {{packageName}}
</ion-item>
<ion-item>
VersionCode: {{versionCode}}
</ion-item>
<ion-item>
VersionNumber: {{versionNumber}}
</ion-item>
</ion-list>

</ion-content>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { AppVersion } from '@ionic-native/app-version';

@Component({
selector: 'page-versions',
templateUrl: 'versions.html',
})
export class VersionsPage {

appName: string;
packageName: string;
versionCode: string;
versionNumber: string;

constructor(public navCtrl: NavController,
private appVersion: AppVersion,
public navParams: NavParams) {
}

ionViewDidLoad() {
this.appVersion.getAppName().then(v => {
this.appName = v;
});

this.appVersion.getPackageName().then(v => {
this.packageName = v;
});

this.appVersion.getVersionCode().then(v => {
this.versionCode = v;
});

this.appVersion.getVersionNumber().then(v => {
this.versionNumber = v;
});
}
}

5. 其他组件中使用

1
2
3
4
<button ion-item (click)="gotoVersions()">
<ion-icon name="help-circle" item-start color="dark"></ion-icon>
<ion-label>关于</ion-label>
</button>
1
2
3
gotoVersions(){
this.navCtrl.push(newPage)
}

六、页面之间的传参

6.1 js跳转方式

路由跳转通过NavController

1. 导入NavController

1
import { NavController} from 'ionic-angular';

2. 注入

1
constructor(public navCtrl: NavController) {}

3. 传参

1
2
// 参数可以说任意 这里对象形式
this.navCtrl.push(DetailsPage, {id: questionId})

navCtrl传参和ModalCtr传参一样

1
2
3
this.ModalCtrl.create(AnswerPage, {
id: this.id
})

4. 接收参数

1
2
3
4
5
6
7
8
9
10
public id:string;

constructor(public navCtrl: NavController, public navParams: NavParams) {
this.id = navParams.get('id') // 接收传递过来的参数
}

// 或者这样写
ionViewDidLoad(){
this.id = this.navParams.get('id')
}

6.2 HTML传参

1. 传参

通过[navPush]打开新页面,[navParams]传递参数。navPush文档

1
2
3
4
5
6
7
8
9
10
11
import { ChatdetailsPage } from '../chatdetails/chatdetails' // 1.导入页面

public ChatdetailsPage: any; // 2. 声明类型

constructor(){
userinfo = {
userId: '1234',
username: 'poetries'
}
this.ChatdetailsPage = ChatdetailsPage; // 3.
}
1
2
3
4
5
6
7
8
<!--4. 使用-->
<ion-item [navPush]="ChatdetailsPage" [navParams]="userinfo">
<ion-avatar>
<img src="https://blog.poetries.top/images/avatar.jpg">
</ion-avatar>
<h2>poetries</h2>
<p>聊天组件开发</p>
</ion-item>

2. 获取传递的参数

1
2
3
4
constructor(public navCtrl: NavController, public navParams: NavParams) {
this.username = navParams.get('username')
this.userid = navParams.get('userid')
}

七、管道的使用

1. 新建管道

1
ionic g pipe realativetime # 管道名称

2. 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { Pipe, PipeTransform } from '@angular/core';
import * as moment from 'moment'

/**
* Generated class for the RelativetimePipe pipe.
*
* See https://angular.io/api/core/Pipe for more info on Angular Pipes.
*/
@Pipe({
name: 'relativetime',
})
export class RelativetimePipe implements PipeTransform {
/**
* Takes a value and makes it lowercase.
*/
transform(value: string, ...args) {
return moment(value).toNow() // 将过去时间变成距离现在多久
}
}

3. src/App/modules.ts中全局导入

1
2
3
4
5
6
7
8
import { RelativetimePipe } from '../pipes/relativetime/relativetime'


@NgModule({
declarations: [
RelativetimePipe
]
...

4. 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!--relativetime管道名称-->
<div class="msg-info">
<p>{{msg.username}}&nbsp;{{msg.time | relativetime}}</p>
</div>
```

# 八、Theme主题全局样式

## 8.1 全局图标颜色配置

**1. `theme/variables.scss`中定义**

```scss
$colors: (
primary: #488aff, /**蓝色。优先匹配**/
secondary: #32db64, /**第二匹配**/
danger: #f53d3d, /**第三匹配**/
light: #f4f4f4,
dark: #222
);

2. 使用

1
2
<!--在color上使用primary-->
<ion-icon name="paper" item-start color="primary"></ion-icon>

image.png

8.2 自定义样式

定义两套样式

theme中新建light.scss。在variables.scss中导入

1
2
// 自定义样式
@import "light";

8.3 全局设置夜间模式切换

1. 在theme中新建theme.dark.scss。在variables.scss中导入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/** 根据dark-theme 作用域切换主题 **/
.dark-theme {
ion-content,.card,.floatMenu {
background-color: #1e2446 !important;
color: #fff !important;
}

.toolbar-title {
color: #fff !important;
}

.header .toolbar-background {
border-color: #140414 !important;
background-color: #3a3c4b !important;
}

.list,.label,.item{
background-color: #3a3c4b !important;
color:#FFFFFF !important;
}
.item{
border-bottom: 0.55px solid #2e2749 !important;
}
.item-inner,{
border: none !important;
}
.item-block{
border-bottom: 0.55px solid #2e2749 !important;
}
}

2. 在theme中新建theme.light.scss。在variables.scss中导入

1
2
3
4
5
6
7
8
9
.light-theme {
ion-content {
background-color: #e3e4e7
}

.toolbar-background {
background-color: #fff;
}
}

3. 在variables.scss中导入

1
2
3
4
/** 这里导入的是两套主题样式 **/

@import "theme.light";
@import "theme.dark";
1
2
3
4
5
6
7
8
9
10
<ion-list class="marginTop">
<ion-list-header>
设置
</ion-list-header>
<ion-item>
<ion-icon name="cloudy-night" item-start color="purple"></ion-icon>
<ion-label>夜间模式</ion-label>
<ion-toggle color="purple" (ionChange)="toggleChangeTheme()"></ion-toggle>
</ion-item>
</ion-list>

4. 新建一个provider来控制app主题的切换

1
ionic provider settings
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// settings.ts
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/Rx'

@Injectable()
export class SettingsProvider {

private theme: BehaviorSubject<string>
constructor(public http: HttpClient) {
this.theme = new BehaviorSubject('light-theme')
}
setActiveTheme(val) {
this.theme.next(val)
}
getActiveTheme() {
return this.theme.asObservable()
}
}

5. 在app.component.ts中设置

image.png

然后在src/app/app.html中设置selectedTheme

1
<ion-nav [root]="rootPage" [class]="selectedTheme"></ion-nav>

6. 在对应组件页面中设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 1. 引入settings providers
import { SettingsProvider } from '../../providers/settings/settings';

export class UserCenterPage {

// 2. 定义变量
public selectedTheme: string;

constructor(public navCtrl: NavController,
public navParams: NavParams,
// 3. 注入
public settings: SettingsProvider
public ModalCtrl: ModalController ) {
super()

// 4. 获取主题
this.settings.getActiveTheme().subscribe(val => this.selectedTheme = val)
}
// 5.切换主题
toggleChangeTheme() {
if(this.selectedTheme == 'dark-theme') {
this.settings.setActiveTheme('light-theme')
}else{
this.settings.setActiveTheme('dark-theme')
}
}
}

7. 在整个app启动的时候设置夜间或者白天模式

image.png

九、组件化开发-自定义组件

9.1 自定义组件

1. 新建组件

1
2
3
# 新建组件

ionic g component emojipicker# 组件名称

image.png

2. 在src/app/app.module.ts中导入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { ComponentsModule } from '../components/components.module'

@NgModule({
declarations: [

],
imports: [
BrowserModule,
HttpClientModule,
ComponentsModule, // 导入自定义组件
IonicStorageModule.forRoot()
],
bootstrap: [IonicApp],
entryComponents: [
],
providers: [
StatusBar,
SplashScreen,
{provide: ErrorHandler, useClass: IonicErrorHandler}
]
})
export class AppModule {}

3. 使用

在页面中使用即可

image.png

4. 组件通过@input()接收外部参数

4.1 定义组件

1
2
3
4
5
6
7
8
9
import { Component, Input } from '@angular/core';

// datatype 外部传递进来,dataSourceType 本地接收之后的参数命名
@Input('dataType') dataSourceType;

//这里没有 ionViewDidLoad 生命周期的函数
ngAfterContentInit(){
console.log(this.dataSourceType)
}

4.2 使用组件

1
2
<!--dataType传递给question-list中的Input 接收-->
<question-list dataType="{{dataType}}"></question-list>

9.2 [hidden] [style] [class] 动态控制组件

1. 动态显示隐藏hidden

1
2
3
4
<ion-content padding>
<img src="{{pathForImage(lastImage)}}" class="img" [hidden]="lastImage === null" />
<h3 [hidden]="lastImage !== null">请从图片库选择一个图片</h3>
</ion-content>

2. 动态绑定style

1
2
<ion-footer no-border [style.height]="isOpenEmojiPicker ? '255px': '55px' ">
</ion-footer>

3. 动态绑定class属性

1
2
3
4
5
6
<div class="message right" 
*ngFor="let msg of messageList"
[class.right] = "msg.userId === userId"
[class.left] = "msg.userId === chatUserId"
>
</div>

十、指令

采用了angular语法 建议参考这篇文章

十一、常用组件使用

组件文档 https://ionicframework.com/docs/components

11.1 ModalController/LoadingController/ToastController

1. 导入

1
import {ModalController, LoadingController,  ToastController, ViewController} from 'ionic-angular';

2. 注入

1
2
3
4
5
6
7
8
constructor(
public loadingCtr: LoadingController,
public viewCtr: ViewController,
public toastCtrl: ToastController,
public ModalCtrl: ModalController
) {

}

3. 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// loading使用
let loader = loadingCtr.create({
content: message, // 加载的内容
dismissOnPageChange: true
})
loader.present() // // 触发生效
loading.dismiss() // 关闭loading

// toastCtrl使用
let toast = toastCtrl.create({
message, // 提示的信息
duration: 3000, // 间隔时间
position: 'bottom' // top buttom left right
})
toast.present() // 触发生效

// ModalController

let modal = this.ModalCtrl.create(QuestionPage) // 弹出的页面
modal.present() // 生效
// 关闭后进行父页面刷新
modal.onDidDismiss(()=>{
// 刷新页面
this.loadPage()
})


// viewCtr
// 关闭当前页面
this.viewCtr.dismiss()

4. 封装loading, toast

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// common/baseui
import { Loading, LoadingController, ToastController, Toast} from 'ionic-angular';

export abstract class BaseUI {
constructor() {

}

protected showLoading(loadingCtr: LoadingController, message: string): Loading {
let loader = loadingCtr.create({
content: message,
dismissOnPageChange: true
})
loader.present()
return loader
}
protected showToast(toastCtrl: ToastController, message: string):Toast {
let toast = toastCtrl.create({
message,
duration: 3000,
position: 'bottom'
})
toast.present()
return toast
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 使用
import { Component } from '@angular/core';
import { ModalController, LoadingController, ToastController} from 'ionic-angular';
import { ApiProvider } from '../../providers/api/api'
import { BaseUI } from '../../common/baseui' // 1.导入封装的组件

@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage extends BaseUI { // 2. 继承BaseUI

feeds: string[];
errorMessage: string;

constructor(
public loadingCtr: LoadingController, // 3. 构造
public api: ApiProvider,
public toastCtrl: ToastController, // 3. 构造
public ModalCtrl: ModalController
) {
super() // 调用子类方法
}

getFeeds() {
let loading = super.showLoading(this.loadingCtr, '加载中...') // 4. 使用loading
this.api.getFeeds().subscribe(data=>{

if(data['UserId']) {
this.feeds = data
loading.dismiss() // 关闭loading
}else{
// 5. 使用toast
super.showToast(this.toastCtrl, data['StatusContent'])
}
},err=>this.errorMessage = <any>err)
}
}

11.2 Refresh组件

刷新组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<ion-content>
<!--刷新组件-->
<ion-refresher (ionRefresh)="doRefresh($event)">
<ion-refresher-content
pullingIcon="arrow-down"
pullingText="下拉刷新"
refreshingSpinner="circles"
refreshingText="数据加载中..."
>
</ion-refresher-content>
</ion-refresher>
<ion-card *ngFor="let q of questions" (click)="gotoDetails(q.IdentityId)">
<ion-item>
<ion-avatar item-start>
<img src="{{q.HeadFace}}">
</ion-avatar>
<p>
{{q.UserNickName}}发布了该问题
<ion-icon class="more-button" name="more"></ion-icon>
</p>
</ion-item>
<h2>{{q.ContentTitle}}</h2>
<ion-card-content>
<p>{{q.ContentSummary}}</p>
</ion-card-content>
<ion-row>
<ion-col col-8 center text-left>
<ion-note>
{{q.LikeCount}}&nbsp;赞同&nbsp;&nbsp;.&nbsp;&nbsp;{{q.CommentCount}}&nbsp;评论&nbsp;&nbsp;.&nbsp;&nbsp;关注
</ion-note>
</ion-col>
<ion-col col-4></ion-col>
</ion-row>
</ion-card>
</ion-content>
1
2
3
4
doRefresh(refresher) {
this.getQuestions() // 再次请求数据
refresher.complete() // 停止刷新
}

11.3 List组件

https://ionicframework.com/docs/components/#lists

11.4 button组件

https://ionicframework.com/docs/components/#buttons

11.5 card组件

https://ionicframework.com/docs/components/#cards

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<ion-card *ngFor="let f of feeds" (click)="gotoDetails(f.IdentityId)">
<ion-item>
<ion-avatar item-start>
<img src="{{f.HeadFace}}">
</ion-avatar>
<p>
{{f.UserNickName}}回答了该问题
<ion-icon class="more-button" name="more"></ion-icon>
</p>
</ion-item>
<h2>{{f.ContentTitle}}</h2>
<ion-card-content>
<p>{{f.ContentSummary}}</p>
</ion-card-content>
<ion-row>
<ion-col col-8 center text-left>
<ion-note>
{{f.LikeCount}}&nbsp;赞同&nbsp;&nbsp;.&nbsp;&nbsp;{{f.CommentCount}}&nbsp;评论&nbsp;&nbsp;.&nbsp;&nbsp;关注
</ion-note>
</ion-col>
<ion-col col-4></ion-col>
</ion-row>
</ion-card>

11.6 表单组件

https://ionicframework.com/docs/components/#inputs

十二、Ionic打包上线流程

12.1 图标生成

替换resource/icon.png图标为1024*1024

生成图标

第一次生成,需要注册账号才可生成 https://dashboard.ionicframework.com/signup

生成图标的过程可能需要翻墙

image.png

1
2
# 在项目根目录执行 不需要进到resources文件夹
ionic cordova resources

image.png

12.2 启动图生成

替换resource/splash.png图标为1024*1024

1
2
# 在项目根目录执行 不需要进到resources文件夹
ionic cordova resources

12.3 打包前细节处理

  1. 修改index.html中的title
  2. 修改config.xml 中的信息
  • widget id="io.ionic.starter" version="0.0.1" 修改id(包名)以及version
  • <name>MyApp</name> 修改app名字
  • <description>An awesome Ionic/Cordova app.</description> 修改app描述
  • <author email="hi@ionicframework" href="http://ionicframework.com/">Ionic Framework Team</author> 修改email

image.png

12.4 打包部署

1
2
# 将所有的平台打包
sudo ionic build

项目文件夹需要执行权限

1
2
# 这样才可以在xcode中打开调试
sudo chomd 777 your_project_name

打开xcode进行ios下的调试

12.5 上架流程

12.5.1 IOS的打包

image.png

app store上架,需要注册开发者账号

image.png

12.5.2 安卓版本打包

最后生成apk文件。

打包方式

  • 通过Android studio生成
  • 通过命令生成

1. 需要安装jdk环境,并设置环境变量

1
2
# mac上安装
brew install jdk java

2. 配置Gradle

1
2
3
# 打开 GradleBuilder.js

platforms/android/cordova/lib/builders/GradleBuilder.js

找到distributionUrl

1
var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'https\\://services.gradle.org/distributions/gradle-4.1-all.zip';

image.png

1
2
3
4
5
// 注释
// var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'https\\://services.gradle.org/distributions/gradle-4.1-all.zip';

// 新增
var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || '../gradle-4.1-all.zip';

3. 配置Gradle的环境变量

第一步

把上一步下载的Gradle把放到任意目录,这里我放的路径/Users/poetry/cordova/gradle-4.1

第二步: 新增环境变量

mac下编辑.bash_profile配置文件, sudo vi ~/.bash_profile。新增一个环境变量,这里的$HOME就是/Users/poetry路径

image.png

第三步: 使环境变量生效

1
source ~/.bash_profile
  • 如果你的终端使用iTerm,需要加载一条命令
1
2
3
4
sudo vi ~/.zshrc

# 新增
source .bash_profile

image.png

第四步:新开一个终端

1
2
3
# 执行下面命令看环境变量

echo $PATH

image.png

第五步:测试

1
gradle -v

image.png

4. debug测试版打包

查看当前打包环境ionic info

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Ionic:

ionic (Ionic CLI) : 4.7.1
Ionic Framework : ionic-angular 3.9.2
@ionic/app-scripts : 3.2.1

Cordova:

cordova (Cordova CLI) : 8.1.2 (cordova-lib@8.1.1)
Cordova Platforms : android 7.1.4, browser 5.0.4, ios 4.5.5
Cordova Plugins : cordova-plugin-ionic-webview 1.2.1, (and 11 other plugins)

System:

Android SDK Tools : 26.1.1
NodeJS : v9.10.0
npm : 5.6.0
OS : macOS Mojave
Xcode : Xcode 10.1 Build version 10B61

注意:需要前面几步环境配置好了,才可以正常执行打包

1
2
# 执行这条命令默认打包的是debug版本
cordova build android

打包后输出apk路径 platforms/android/app/build/outputs/apk/debug/

image.png

生成的apk。此时可以把apk,拖入Genymotion模拟器调试

image.png

5. 正式版本打包

5.1 未签名版本

此时打包的apk是没有签名的版本,不可以在手机上安装

1
2
# 生成未签名版
ionic cordova build android --release

image.png

打包后输出apk路径 platforms/android/app/build/outputs/apk/release

在手机安装示意图,签名版不能安装

image.png

5.2 签名版本apk打包

签名步骤

1. 创建私钥,项目根目录下执行命令(记住设置的别名)

1
keytool -genkey -v -keystore [自定义秘钥文件名,如 my-app].jks -keyalg RSA -keysize 2048 -validity 36500 -alias [自定义app别名,如 my-alias]
  • -genkey 意味着执行的是生成数字证书操作
  • v 表示将生成证书的详细信息打印出来,显示在dos窗口中
  • -keyalg RSA 表示生成密钥文件所采用的算法为RSA
  • -validity 36500 表示该数字证书的有效期为36500

2. 接下来会让设置秘钥库口令(记住秘钥):

秘钥库就是你的密码

image.png

3. 设置秘钥库口令后会让输入一些APP信息

image.png

4. 按照提示依次输入后会在你的项目根目录生成秘钥文件 my-app.jks

把之前生成好的platforms/android/app/build/outputs/apk/release/app-release-unsigned.apk复制到项目根目录,这样和my-app.jks同一目录签名

1
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore [上步生成的xxx.jks] app-release-unsigned.apk [步骤1命令中设置的app别名,如 my-alias]
1
2
# 这里是上面的示例:执行签名命令
keytool -genkey -v -keystore my-app.jks -keyalg RSA -keysize 2048 -validity 36500 -alias my-app

5. 验证应用是否签名成功

1
jarsigner -verify -verbose -certs 你的apk名
1
2
# 示例
jarsigner -verify -verbose -certs app-release-unsigned.apk

5. 优化 apk 文件

5.1 首先需要配置环境变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 这里以mac下的iTerm2来配置
vi ~/.bash_profile

# 增加一行
export PATH=$PATH:$ANDROID_HOME/build-tools/27.0.3

# 这里其实是安卓的路径,我们需要给zipalign配置环境变量
/Users/poetry/Library/Android/sdk/build-tools/27.0.3/zipalign

# 使得bash_profile生效
source ~/.bash_profile

# 因为使用到iTerm2需要在.zshrc加入.bash_profile
vi ~/.zshrc

# 加入bash_profile
source .bash_profile

# 最后新建窗口echo $PATH就看到环境变量配置了

# 查看是否生效
zipalign -v

echo $PATH

5.2 在项目根目录执行

1
2
# 自定义最终生成的apk的名字,如 ionicQa.apk
zipalign -v 4 app-release-unsigned.apk ionicQa.apk

6. 遇到的问题

6.1 无法打开 jar 文件

将 秘钥文件 xxx.jksandroid-release-unsigned.apk 放在同一目录下,放到项目根目录就好了

此时就构建好了应用,这里是构建的应用

12.5.3 网站微信端发布

打包成一个静态站点方便部署

1
sudo ionic build

静态网站部署的站点资源路径 platform/browser/www

十三、一些问题记录

13.1 问题

1. 【ionic3】刷新页面,ws中断

解决办法升级@ionic/app-scripts

1
npm install @ionic/app-scripts@latest --save-dev

13.2 技巧

读取可空对象

question?.ContentTitle question可能返回空

十四、更多参考

14.1 项目学习

https://github.com/poetries/ionic-qa-app

14.2 文档参考

支持一下
扫一扫,支持poetries
  • 微信扫一扫
  • 支付宝扫一扫