Ionic 从入门到实践开发

Ionic 准备

Cordova
Angular

npm install -g cordova @ionic/cli

vscode 扩展

Ionic Snippets
Angular Snippets

Ionic 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
npm install -g @ionic/cli

ionic start myApp tabs
cd myApp
ionic serve

ionic g page "User Detail"
ionic generate component contact/form
ionic generate directive ripple --skip-import
ionic generate service api/user

ionic capacitor add ios
ionic cordova prepare ios

ionic capacitor add android
ionic cordova prepare android

Ionic 目录结构

src/
|- app/
|- app-routing.module.ts
|- app.component.html
|- app.component.spec.ts
|- app.component.ts
|- app.module.ts
www/:静态文件,ionic build –prod 生成的单页面静态资源文件
|- assets/
|- environments/
|- theme/
|- global.scss
|- index.html
|- main.ts
|- polyfills.ts
|- test.ts
|- zone-flags.ts

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
// 根模块
import { NgModule } from '@angular/core';
// Angula、Ionic的核心文件
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
// Ionic 打包成 app 以后配置启动画面以及导航条的服务
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';

// 路由配置文件
import { AppRoutingModule } from './app-routing.module';
// 引入根组件
import { AppComponent } from './app.component';

@NgModule({
declarations: [AppComponent], // 声明组件
entryComponents: [],// 配置不会在模板中使用的组件
imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule],// 引入依赖模块
providers: [ // 配置服务
StatusBar,
SplashScreen,
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
],
bootstrap: [AppComponent]
})
export class AppModule {}

路由文件

src/
|- app/
|- app-routing.module.ts:主路由配置
|- tabs/
|- tabs-routing.module.ts:页面路由配置

Ionic router 跳转及传参

1
2
<!-- 声明式导航 -->
<ion-button [routerLink]="['/button']">Button</ion-button>

编程式

1
2
3
4
5
6
7
8
9
<!-- 返回按钮 -->
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title>button</ion-title>
</ion-toolbar>
</ion-header>

自定义模块

ionic generate component contact/form
ionic g module contact/form

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// form.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
// 引入 ionic 组件
import { IonicModule } from '@ionic/angular';

@NgModule({
declarations: [],
imports: [
CommonModule,
// 注册 ionic 组件
IonicModule
]
})
export class FormModule { }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// contact.module.ts
import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { FormsModule } from "@angular/forms";

import { IonicModule } from "@ionic/angular";

import { ContactPageRoutingModule } from "./contact-routing.module";

import { ContactPage } from "./contact.page";
// 引入组件
import { FormComponent } from "./form/form.component";

@NgModule({
imports: [CommonModule, FormsModule, IonicModule, ContactPageRoutingModule],
// 注册组件
declarations: [ContactPage, FormComponent],
})
export class ContactPageModule {}
1
2
3
4
5
6
7
8
9
10
11
<!--  contact.page.html  -->
<ion-header>
<ion-toolbar>
<!-- 使用组件 -->
<ion-title>contact</ion-title>
</ion-toolbar>
</ion-header>

<ion-content>
<app-form></app-form>
</ion-content>

Ionic UI 组件

button
ion-router-outlet

Toolbar

ion-toolbar 导航栏
ion-header 头部
ion-footer 底部
ion-title 导航栏标题
ion-buttons 导航栏按钮
ion-back-button 返回按钮

ion-list

ion-list
ion-list-header
ion-virtual-scroll

Media

ion-avatar
ion-icon
ion-img
ion-thumbnail

内置颜色


Ionic Vue

其中包含部分 vue3,最好先看 vue3。

1
2
3
4
5
npm install -g @ionic/cli@latest
ionic start myApp blank --type vue
ionic serve
ionic capacitor add // 添加平台
cordova-res -skip-config -copy //生成应用图标和启动画面

nrm

nrm 管理 npm 源

1
2
3
cnpm i -g nrm
nrm ls // 查看源
nrm use taobao // 使用源

路由跳转

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-back-button></ion-back-button>
<!-- 声明式导航 -->
<ion-button router-link="/button?id=12">Button</ion-button>
<router-link to="/newsContent?id=2">Get 传值</router-link>

this.$router.push('/non-existing')
this.$route.query

// setup 中使用
import { useRoute,useRoutes,useIonRouter } from 'vue-router'
setup(){
const route = useRoute()
const router = useRouter()
console.log(route.query)

判断是否可以返回根页面
const ionRouter=useIonRouter();
if(ionRouter.canGoBack()){
//Performsomeactionhere
}

return {
goBack(){
router.back()
},
goRoot(){
router.replace("/tabs/tab1")
}
}
}

// 返回上一页
router.back();
router.go(-1)

组件

内置主题颜色:primary、secondary、tertiary、success、warning、danger、dark、medium、light 等

详细参考:theme\variables

ctrl + 鼠标左键点击引入的组件 可查看内部属性

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
 按钮
<ion-button color="primary">primary</ion-button>
<ion-button expand="block">圆角</ion-button>
<ion-button expand="full">方角</ion-button>
<ion-button>

<ion-icon slot="start" :icon="homeOutline"></ion-icon>
图标按钮
</ion-button>


顶部导航栏
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title>Back Button</ion-title>
</ion-toolbar>

列表
<ion-list>
<ion-item lines="full">
<ion-label>Pokémon Yellow</ion-label>
</ion-item>
<ion-item detail>
<ion-label>Mega Man X</ion-label>
</ion-item>
</ion-list>

列表分组
<ion-list>
<ion-item-group>
<ion-item-divider>
<ion-icon slot="start" :icon="add"></ion-icon>
<ion-label>A</ion-label>
</ion-item-divider>
// 头像
<ion-item>
<ion-avatar>
<img src="/assets/icon/favicon.png" />
</ion-avatar>
<ion-label>
<h2>姓名:</h2>
<p>身份:</p>
</ion-label>
</ion-item>
// 缩略图
<ion-item>
<ion-thumbnail slot="start">
<img src="/assets/icon/favicon.png" />
</ion-thumbnail>
<ion-label>Argentina</ion-label>
</ion-item>
</ion-item-group>

// 滑动按钮
<ion-item-sliding>
<ion-item-options side="start">
<ion-item-option @click="favorite(item)">Favorite</ion-item-option>
<ion-item-option color="danger" @click="share(item)">Share</ion-item-option>
</ion-item-options>
<ion-item>
<ion-label>Item Options</ion-label>
</ion-item>
<ion-item-options side="end">
<ion-item-option @click="unread(item)">Unread</ion-item-option>
</ion-item-options>
</ion-item-sliding>
</ion-list>

<script>
// 图标引入
import {add,homeOutline} from "ionicons/icons";
export default {
setup() {
return{
add,
homeOutline
}
}
}
</script>

// 表单
<ion-item>
<ion-label position="fixed">姓名</ion-label>
<ion-input v-model="userInfo.name" placeholder="请输入姓名" />
</ion-item>
// 开关
<ion-item>
<ion-label>Pepperoni</ion-label>
<ion-toggle value="pepperoni"> </ion-toggle>
</ion-item>
// 单选
<ion-list>
<ion-radio-group name="auto" value="tesla">
<ion-list-header>
<ion-label>Auto Manufacturers</ion-label>
</ion-list-header>
<ion-item>
<ion-label>Tesla</ion-label>
<ion-radio value="tesla"></ion-radio>
</ion-item>
<ion-item>
<ion-label>Ford</ion-label>
<ion-radio value="ford"></ion-radio>
</ion-item>
</ion-radio-group>
</ion-list>
// 多选
<ion-list>
<ion-item v-for="entry in form">
<ion-label>{{entry.val}}</ion-label>
<ion-checkbox
slot="end"
@update:modelValue="entry.isChecked = $event"
:modelValue="entry.isChecked">
</ion-checkbox>
</ion-item>
</ion-list>
// 选择器
<ion-item>
<ion-label>Gender</ion-label>
<ion-select placeholder="Select One">
<ion-select-option value="f">Female</ion-select-option>
<ion-select-option value="m">Male</ion-select-option>
</ion-select>
</ion-item>
// 多行文本框
<ion-textarea></ion-textarea>
// 轮播图
<ion-slides pager="true" :options="slideOpts" @ionSlideTouchEnd=“ionSlideTouchEnd”>
<ion-slide>
<h1>Slide 1</h1>
</ion-slide>
<ion-slide>
<h1>Slide 2</h1>
</ion-slide>
<ion-slide>
<h1>Slide 3</h1>
</ion-slide>
</ion-slides>
<script>
setup() {
// Optional parameters to pass to the swiper instance. See http://idangero.us/swiper/api/ for valid options.
const slideOpts = {
initialSlide: 1,
speed: 400,
loop: true,
autoplay: {
delay: 2000
}
};
return { slideOpts }
},
methods:{
ionSlideTouchEnd(e){
e.srcElement.startAutoplay()
}
}
</script>
// 搜索
<ion-searchbar placeholder="请输入搜索关键字" color="tertiary" type="number" animate :debounce="250" ></ion-searchbar>
<ion-toolbar>
<ion-buttons slot="primary">
<ion-button @click="clickedSearch()">
<ion-icon slot="icon-only" :icon="search"></ion-icon>
</ion-button>
</ion-buttons>
<ion-searchbar placeholder="Search Favorites"></ion-searchbar>
</ion-toolbar>
// 切换
<ion-segment @ionChange="segmentChanged($event)">
<ion-segment-button value="friends">
<ion-label>Friends</ion-label>
</ion-segment-button>
<ion-segment-button value="enemies">
<ion-label>Enemies</ion-label>
</ion-segment-button>
</ion-segment>
<div>
<div v-if="tab === 'friends'">
Friends
</div>
<div v-if="tab === 'enemies'">
Enemies
</div>
</div>
// 日期选择
<ion-datetime display-format="MM/YYYY" picker-format="MMMM YYYY" cancel-text=“取消” done-text="确认"></ion-datetime>
// 侧边栏
<ion-menu side="start" swipe-gesture="false" menu-id="first" content-id="main">
<ion-header>
<ion-toolbar color="primary">
<ion-title>Start Menu</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-menu-toggle>
<ion-item router-direction="root" router-link="/button"></ion-item>
</ion-menu-toggle>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
</ion-list>
</ion-content>
</ion-menu>
<ion-router-outlet id="main"></ion-router-outlet>
<script>
import { menuController } from '@ionic/vue'
export default defineComponent({
methods:{
openSide(){
menuController.enable(true,'first');
menuController.open('first');
// 关闭
menuController.close('first');
}
}
})
</script>
// 修改 ionic vue 内置组件里面的样式
<style>
.list .ion-select::part(placeholder){
color: red;
opacity: 1;
}
</style>
// 操作单
<script>
import {actionSheetController} from '@ionic/vue'
export default defineComponent({
methods:{
async presentActionSheet() {
const actionSheet = await actionSheetController
.create({
header: 'Albums',
cssClass: 'my-custom-class',
buttons: [
{
text: 'Delete',
role: 'destructive',
icon: trash,
handler: () => {
console.log('Delete clicked')
},
},
{
text: 'Share',
icon: share,
handler: () => {
console.log('Share clicked')
},
},
{
text: 'Play (open modal)',
icon: caretForwardCircle,
handler: () => {
console.log('Play clicked')
},
},
{
text: 'Favorite',
icon: heart,
handler: () => {
console.log('Favorite clicked')
},
},
{
text: 'Cancel',
icon: close,
role: 'cancel',
handler: () => {
console.log('Cancel clicked')
},
},
],
});
await actionSheet.present();

const { role } = await actionSheet.onDidDismiss();
console.log('onDidDismiss resolved with role', role);
},
}
})
</script>
<style>
// 更改自定义样式
.my-custom-class .action-sheet-group {
background: #e5e5e5;
}
</style>

<ion-action-sheet
:is-open="isOpenRef"
header="Albums"
css-class="my-custom-class"
:buttons="buttons"
@didDismiss="setOpen(false)"
>
</ion-action-sheet>
<script>
setup() {
const isOpenRef = ref(false);
const setOpen = (state: boolean) => isOpenRef.value = state;
const buttons = [
{
text: 'Delete',
role: 'destructive',
icon: trash,
handler: () => {
console.log('Delete clicked')
},
},
{
text: 'Share',
icon: share,
handler: () => {
console.log('Share clicked')
},
},
{
text: 'Play (open modal)',
icon: caretForwardCircle,
handler: () => {
console.log('Play clicked')
},
},
{
text: 'Favorite',
icon: heart,
handler: () => {
console.log('Favorite clicked')
},
},
{
text: 'Cancel',
icon: close,
role: 'cancel',
handler: () => {
console.log('Cancel clicked')
},
},
];

return { buttons, isOpenRef, setOpen }
}
</script>
// 吐司也有两种形态
<ion-button @click="openToast">Open Toast</ion-button>

<script>
import { toastController } from '@ionic/vue';
methods: {
async openToast() {
const toast = await toastController
.create({
message: 'Your settings have been saved.',
duration: 2000
})
return toast.present();
},

</script>
<style>
ion-toast{
--width: 100px
}
</style>
组件模式下,解决切换页面吐司在最顶层
<script>
import {onUnmounted} from 'vue'
setup() {
const isOpenRef = ref(false);
const setOpen = (state: boolean) => isOpenRef.value = state;
onUnmounted(()=>{
setOpen(false)
})
return { isOpenRef, setOpen }
}
</script>
// loadding 组件
// alert 组件 对话框 表单
// modal 组件 弹出层
<script>
import { modalController } from '@ionic/vue';
methods:{
close(){
// 关闭对话框 传值
modalController.dismiss({
username: '张三',
});
}
// 主页面接受传值
const { data } = await modal.onDidDismiss();
console.log(data);
}
/**
* 分页上拉加载更多
* ion-infinite-scroll 组件
* infinite-scroll-content
*
* 下拉刷新
* ion-refresher
* ion-refresher-content
*
*
* list 上面放 下拉刷新组件
**/
</script>

自定义主题

进入官方文档Colors 下面有 New Color Creator 选择颜色输入变量 将生成的 css 放入主题文件中。

再主题文件内可以修改组件内置样式

1
2
3
4
ion-button {
--background: red;
--color: #fff;
}

自定义指令结合手势事件

1
2
3
4
5
6
7
//  自定义指令  <div v-bg="red"></div>
Vue.directive('bg', {
beforeMount(el,binging){
el.style.background = binging.value; // 改变元素背景颜色
binging.value() // 执行传入函数
}
})

安装 hammerjs

npm install --save @types/hammerjs 貌似可以不安装好像是 angluar 中内置的手势库会用到

npm install --save hammerjs

使用 hammerjs

1
2
3
4
5
6
7
8
9
10
11
12
//  main.js
import Hammer from "hammerjs"

// 自定义指令
// 长按 press
// 滑动 swiper
Vue.directive('tap', {
beforeMount(el,binging){
const hammertime = new Hammer(el);
hammertime.on('tap', binging.value)
}
})

相关资料
[视频]ionic4 ionic5视频教程_ionic4.x ionic5.x入门实战教程-2020年6月更新

Ionic Snippets
Ionic 文档
Web Components

离子教程 - 构建跨平台应用程序|开发 - 离子教程 (devdactic.com)

ionic3中ion-slides切换页面返回后自动轮播startAutoplay()

自定义指令 — Vue.js (vuejs.org)

You need to set client_id and slot_id to show this AD unit. Please set it in _config.yml.