赞
踩
目录
3、使用 TypeScript 或 JavaScript 构建
某天水壶哥在开发app时,急需查看ionic的官方api解决一个问题,但不知什么原因,官方api死活上不去,而且水壶哥发现,ionic的官方api时不时的就会挂掉,如果着急查找什么会很耽误事,所幸,趁着ionic官网能访问,将关于vue这部分的app开发文档摘录了下来,以便不时之需。
@ionic/vue 将核心 Ionic 框架体验与为 Vue 开发人员量身定制的工具和 API 相结合。
Ionic Vue 建立在 Vue 3.0.0 之上。如果您使用 Ionic Vue 的早期版本构建了一个应用程序,您将需要升级到最新版本并升级您的 Vue 依赖项。
Ionic Vue 项目附带与常规 Vue CLI 项目相同的工具。这意味着您将使用 Vue CLI 及其所有功能进行构建。此外,入门项目还附带了一些默认启用的功能,例如路由和 TypeScript 支持。
Capacitor是官方的跨平台应用程序运行时,用于使您的Ionic Vue
Web 应用程序在 iOS、Android 和 Web 上本地运行。
Ionic Vue
虽然与Cordova插件一起使用没有已知的技术限制,但正式推荐使用 Capacitor。目前没有计划Ionic Vue
在Ionic CLI 工具中支持 Cordova 集成。有关详细信息,请参阅此处
- $ npm install -g @ionic/cli
- $ ionic start myApp tabs --type vue
-
- $ ionic serve
首先,如果你是一位初学者,那么欢迎你!Ionic Framework 是一个免费的开源组件库,用于构建在 iOS、Android、Electron 和 Web 上运行的应用程序。使用熟悉的技术(HTML、CSS、JavaScript)编写您的应用程序并部署到任何平台。
除了 UI 组件,Ionic Framework 还提供了一个命令行工具来创建新的应用程序,以及部署到我们支持的各种平台。
在本指南中,我们将介绍 Vue 和 Ionic 框架的基础知识,包括任何 Ionic 框架特定的功能。如果您熟悉 Vue,请阅读该指南并了解有关 Ionic 框架的新知识。如果您不熟悉其中任何一个,请不要担心!本指南将涵盖基础知识并提供足够的信息来启动和运行应用程序。
首先,让我们安装最新版本的 Ionic CLI。
npm install -g @ionic/cli@latest
安装完成后,全局命令ionic
将允许使用 Ionic 框架和任何其他依赖项创建 Vue 项目。要创建新项目,请运行以下命令 :
- ionic start myApp blank --type vue
- cd myApp
随后,我们可以用ionic serve命令
在浏览器中运行我们的项目。
我们喜欢 Ionic 的 TypeScript,并且很长一段时间以来一直相信它是构建可扩展应用程序的绝佳工具。也就是说,我们知道 Vue 社区对简单性的重视程度——在他们的工具、语言等方面。事实上,这很可能是最初吸引您使用 Vue 的原因。从简单开始——然后根据需要扩大规模。
所以,如果你更喜欢使用 JavaScript 而不是 TypeScript,你可以在生成 Ionic Vue 应用程序后,请执行以下步骤:
(1)删除 TypeScript 依赖项:
npm uninstall --save typescript @types/jest @typescript-eslint/eslint-plugin @typescript-eslint/parser @vue/cli-plugin-typescript @vue/eslint-config-typescript
(2)将所有.ts
文件更改为.js
. 如果在空白的 Ionic Vue 应用程序中,这应该只是
router/index.ts文件和main.ts文件
(3)
从中删除@vue/typescript/recommended
和。@typescript-eslint/no-explicit-any:
‘off’,
.eslintrc.js
(4)Array<RouteRecordRaw>从中删除router/index.js。
(5)
删除shims-vue.d.ts
文件
(6)从所有 Vue 组件中的script标签中删除lang="ts"。在空白的 Ionic Vue 应用程序中,这应该只是App.vueand views/Home.vue。
我们的应用程序的基础将在src
目录中,主入口点将是我们的main.ts
文件。如果我们在代码编辑器中打开我们的项目并打开main.ts
,我们应该会看到以下内容:
- import { createApp } from 'vue';
- import { IonicVue } from '@ionic/vue';
-
- import App from './App.vue';
- import router from './router';
-
- const app = createApp(App).use(IonicVue).use(router);
-
- router.isReady().then(() => {
- app.mount('#app');
- });
那么这里发生了什么?前四行引入了一些依赖项。该createApp
函数让我们初始化我们的 Vue 应用程序,同时IonicVue
是一个允许我们在 Vue 环境中使用 Ionic Framework 的插件。
第三个导入是我们应用程序的根组件,简单命名为App
. 这是我们的第一个 Vue 组件,将在我们的 Vue 应用程序的引导过程中使用。
第四个导入获取我们的路由配置。稍后我们将更深入地研究这一点。
如果我们打开App.vue
,我们应该看到以下内容:
- <template>
- <ion-app>
- <ion-router-outlet />
- </ion-app>
- </template>
-
- <script lang="ts">
- import { IonApp, IonRouterOutlet } from '@ionic/vue';
- import { defineComponent } from 'vue';
-
- export default defineComponent({
- name: 'App',
- components: {
- IonApp,
- IonRouterOutlet,
- },
- });
- </script>
让我们分解它,从第一组导入开始。
import { IonApp, IonRouterOutlet } from '@ionic/vue';
要在 Vue 中使用组件,您必须先导入它。因此对于 Ionic 框架,这意味着任何时候我们想要使用按钮或卡片,都必须将其添加到我们的导入中。对于我们的App
组件,我们使用IonApp
and IonRouterOutlet
。如果您发现自己重复导入相同的组件,您也可以全局注册组件。这伴随着我们在Optimizing Your Build中介绍的性能权衡。
再让我们看一下模板。
- <template>
- <ion-app>
- <ion-router-outlet />
- </ion-app>
- </template>
所有 Vue 组件都必须有一个<template>
. 在这里,我们放置我们的IonApp
和IonRouterOutlet
组件。
最后,我们看一下组件定义:
- import { IonApp, IonRouterOutlet } from '@ionic/vue';
- import { defineComponent } from 'vue';
-
- export default defineComponent({
- name: 'App',
- components: {
- IonApp,
- IonRouterOutlet,
- },
- });
Vue 3的defineComponent
在创建组件以改进工具支持时提供了一个新功能,我们将在这里使用它。我们首先定义组件的名称,然后提供将在模板中使用的组件。
您可以在此处提供几个参数,例如methods
,setup
等等。这在 Vue 的Composition API 文档中进行了解释。
Ionic Vue 使用vue-router依赖项,因此如果您已经熟悉 Vue Router,您将能够将您所知道的应用到 Ionic Vue 中的导航。再来看看我们之前提到的路由器配置。在router/index.ts
中,您应该会看到类似于以下内容的内容:
- import { createRouter, createWebHistory } from '@ionic/vue-router';
- import { RouteRecordRaw } from 'vue-router';
- import Home from '@/views/Home.vue';
-
- const routes: Array<RouteRecordRaw> = [
- {
- path: '/',
- redirect: '/home',
- },
- {
- path: '/home',
- name: 'Home',
- component: Home,
- },
- ];
-
- const router = createRouter({
- history: createWebHistory(process.env.BASE_URL),
- routes,
- });
-
- export default router;
注意
此示例使用 Ionic Vue Blank空白项目启动应用程序,因此您的实际路由可能并不与此完全相同。
这里的设置和你vue-router
直接使用一样,但是你需要从包中导入依赖项,例如createRouter
和。createWebHistory
@ionic/vue-router
导入依赖后,我们可以在routes
数组中声明我们的路由。从那里,我们可以创建一个路由器实例,并为它提供我们的路由和我们想要使用的历史类型。
使用 Ionic Vue,延迟加载开箱即用。除了导入我们的Home
组件,我们还可以这样做:
- const routes: Array<RouteRecordRaw> = [
- {
- path: '/',
- redirect: '/home',
- },
- {
- path: '/home',
- name: 'Home',
- component: () => import('@/views/Home.vue'),
- },
- ];
现在,您可能想知道:为什么我们在描述组件的路径时使用@,该@符号是我们可以用来描述相对于src
目录的路径的快捷方式。如果我们试图在一个文件中有几个文件夹深的文件中引用一个组件,这很有用。我们可以简单地写'@/views/Home.vue'
而不是'../../../views/Home.vue'
。
以上的App
组件并没有太多需要修改的地方。它是容器组件的基本示例。使用路由器逻辑集,它所负责的只是渲染与给定 URL 路由匹配的组件。由于我们已经设置了一个组件/路由器,让我们继续修改我们的Home
组件。
目前,该Home
组件如下所示:
- <template>
- <ion-page>
- <ion-header :translucent="true">
- <ion-toolbar>
- <ion-title>Blank</ion-title>
- </ion-toolbar>
- </ion-header>
-
- <ion-content :fullscreen="true">
- <ion-header collapse="condense">
- <ion-toolbar>
- <ion-title size="large">Blank</ion-title>
- </ion-toolbar>
- </ion-header>
-
- <div id="container">
- <strong>Ready to create an app?</strong>
- <p>
- Start with Ionic
- <a target="_blank" rel="noopener noreferrer" href="https://ionicframework.com/docs/components"
- >UI Components</a
- >
- </p>
- </div>
- </ion-content>
- </ion-page>
- </template>
-
- <script lang="ts">
- import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/vue';
- import { defineComponent } from 'vue';
-
- export default defineComponent({
- name: 'Home',
- components: {
- IonContent,
- IonHeader,
- IonPage,
- IonTitle,
- IonToolbar,
- },
- });
- </script>
-
- <style scoped>
- #container {
- text-align: center;
-
- position: absolute;
- left: 0;
- right: 0;
- top: 50%;
- transform: translateY(-50%);
- }
-
- #container strong {
- font-size: 20px;
- line-height: 26px;
- }
-
- #container p {
- font-size: 16px;
- line-height: 22px;
-
- color: #8c8c8c;
-
- margin: 0;
- }
-
- #container a {
- text-decoration: none;
- }
- </style>
就像我们开始使用App的组件时一样,我们有一些针对特定 Ionic 框架导入的组件,和一些从 Vue 导入的组件,以及与我们的组件一起使用的样式。
对于我们的样式,请注意我们已将样式指定为scoped
. 这意味着我们在此处编写的样式将仅适用于该组件。这对于防止样式泄漏到组件外部并影响应用程序的其他部分很有用。我们强烈建议在 Ionic Vue 应用程序的样式中使用scoped
。
IonPage
是所有页面的基础组件(带有路由/URL 的组件),并且包括全屏组件的一些常见构建块,例如标题、标题和内容组件。
注意
创建自己的页面时,不要忘记使用
IonPage做为
根组件。使用IonPage
根组件很重要,因为它有助于确保转换正常工作,并提供 Ionic Framework 组件所依赖的基本 CSS。
IonHeader
是一个存在于页面顶部的组件。除了处理一些基于 flexbox 的布局之外,它本身并没有做太多事情。它一般用于存放类似IonToolbar或
IonSearchbar的组件
。
IonContent
顾名思义,是我们页面的主要内容区域。它负责提供用户将与之交互的可滚动内容,以及可在应用程序中使用的任何滚动事件。
我们当前的内容相对简单,但不包含任何可以在实际应用程序中使用的内容,所以让我们对其进行更改。
注意
为简洁起见,我们排除了组件的重复部分,例如函数声明或来自其他组件的导入语句。
- <template>
- <ion-page>
- ...
- <ion-content>
- <ion-list>
- <ion-item>
- <ion-checkbox slot="start"></ion-checkbox>
- <ion-label>
- <h1>Create Idea</h1>
- <ion-note>Run Idea By Brandy</ion-note>
- </ion-label>
- <ion-badge color="success" slot="end"> 5 Days </ion-badge>
- </ion-item>
- </ion-list>
- </ion-content>
- </ion-page>
- </template>
在IonContent组件中
,我们添加了一个IonList
组件。让我们看看它的核心IonItem组件
。
- <ion-item>
- <ion-checkbox slot="start"></ion-checkbox>
- <ion-label>
- <h1>Create Idea</h1>
- <ion-note>Run Idea By Brandy</ion-note>
- </ion-label>
- <ion-badge color="success" slot="end"> 5 Days </ion-badge>
- </ion-item>
这里有一个特殊的属性,称为 slot。这是在渲染IonItem组价中的IonCheckbox组件
时,明确放置位置的关键。IonCheckbox
不是 Vue API,而是 Web 标准 API。此外,这与您可能从 Vue 2 中回忆的插槽 API 不同。
让我们看看 Ionic Framework 的另一个组件 FAB。浮动操作按钮是提供从应用程序的其余部分提升的主要操作的好方法。对于这个 FAB,我们将需要三个组件:一个 FAB、一个 FAB 按钮和一个图标。
- <template>
- <ion-page>
- <ion-content>
- <ion-list> ... </ion-list>
-
- <ion-fab vertical="bottom" horizontal="end" slot="fixed">
- <ion-fab-button>
- <ion-icon :icon="add"></ion-icon>
- </ion-fab-button>
- </ion-fab>
- </ion-content>
- </ion-page>
- </template>
-
- <script>
- import { add } from 'ionicons/icons';
-
- ...
-
- export default defineComponent({
- name: 'Home',
- ...,
- setup() {
- return {
- add
- }
- }
- })
- </script>
在我们的 IonFab
上,我们使用垂直和水平属性设置它的位置。我们还使用 slot 属性将渲染位置设置为“固定”。这将告诉 IonFab 在 IonContent 中的可滚动内容之外呈现。
现在让我们连接一个点击处理程序。单击 FAB 按钮时,我们希望导航到一个新页面(稍后我们将创建该页面)。为此,我们需要访问 Vue Router 的导航 API。这可以通过useRouter
从vue-router
包中导入来完成。
- <template>
- <ion-page>
- <ion-content>
- <ion-list> ... </ion-list>
-
- <ion-fab vertical="bottom" horizontal="end" slot="fixed">
- <ion-fab-button @click="() => router.push('/new')">
- <ion-icon :icon="add"></ion-icon>
- </ion-fab-button>
- </ion-fab>
- </ion-content>
- </ion-page>
- </template>
-
- <script>
- import { add } from 'ionicons/icons';
- import { useRouter } from 'vue-router';
-
- ...
-
- export default defineComponent({
- name: 'Home',
- components: {
- IonContent,
- IonFab,
- IonFabButton,
- IonHeader,
- IonIcon,
- IonPage,
- IonTitle,
- IonToolbar
- },
- setup() {
- return {
- router: useRouter(),
- add
- }
- }
- });
- </script>
在我们的组件文件中,我们正在导入useRouter
函数。调用时,此函数将路由器依赖项注入到组件中。它使我们能够从 Vue Router 访问历史 API,允许我们将新路由推送到导航堆栈。在我们的IonFabButton
中,我们可以添加一个点击处理程序,然后调用router.push
并传入新路由。在这种情况下,我们将导航到new
。
<ion-fab-button @click="() => router.push('/new')"> ... </ion-fab-button>
现在我们已经准备好在我们的应用程序中导航的部分,我们需要创建一个新组件并将新路由添加到我们的路由器声明中。让我们打开我们的router/index.ts
文件并添加新路由。
- import { createRouter, createWebHistory } from '@ionic/vue-router';
- import { RouteRecordRaw } from 'vue-router';
- import Home from '@/views/Home.vue';
- import NewItem from '@/views/NewItem.vue';
-
- const routes: Array<RouteRecordRaw> = [
- {
- path: '/',
- redirect: '/home',
- },
- {
- path: '/home',
- name: 'Home',
- component: Home,
- },
- {
- path: '/new',
- name: 'NewItem',
- component: NewItem,
- },
- ];
-
- const router = createRouter({
- history: createWebHistory(process.env.BASE_URL),
- routes,
- });
-
- export default router;
我们的路由器现在有了一个路由条目/new
,我们将创建所需的组件NewItem
。这将存在于views/NewItem.vue
.
NewItem.vue
让我们暂时用一些占位符内容填充文件。
- <template>
- <ion-page>
- <ion-header>
- <ion-toolbar>
- <ion-buttons slot="start">
- <ion-back-button></ion-back-button>
- </ion-buttons>
- <ion-title>New Item</ion-title>
- </ion-toolbar>
- </ion-header>
- <ion-content></ion-content>
- </ion-page>
- </template>
-
- <script>
- import { IonBackButton, IonButtons, IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/vue';
- import { defineComponent } from 'vue';
-
- export default defineComponent({
- name: 'NewItem',
- components: {
- IonBackButton,
- IonButtons,
- IonContent,
- IonHeader,
- IonPage,
- IonTitle,
- IonToolbar,
- },
- });
- </script>
注意
每个视图必须包含一个
IonPage
组件。没有它,页面转换将无法正常工作。有关详细信息,请参阅IonPage 文档。
这里的内容应该与Home
组件类似。这里不同的是IonBackButton
组件。这用于导航回之前的路线。看起来很容易,对吧?好的,但是如果我们重新加载页面呢?
在这种情况下,内存中的历史记录会丢失,因此后退按钮会消失。为了解决这个问题,default-href
如果没有历史记录,我们可以将属性值设置为我们想要导航到的 URL。
<ion-back-button default-href="/home"></ion-back-button>
现在,如果没有应用历史记录,我们将能够导航回我们的主页上。
为了在任何 Ionic Vue 组件上调用方法,您首先需要获取对组件实例的引用。 接下来,您将需要使用 $el 访问底层 Web 组件并调用该方法。
在 Ionic React 等其他框架集成中,这不是必需的,因为ref
您提供的任何内容都会自动转发到底层 Web 组件实例。由于 Vue 管理 refs 的限制,我们无法在这里做同样的事情。
- <template>
- <ion-content ref="content">
- <ion-button @click="scrollToBottom">Scroll to Bottom</ion-button>
-
- ...
- </ion-content>
- </template>
-
- <script lang="ts">
- import { IonButton, IonContent } from '@ionic/vue';
- import { defineComponent, ref } from 'vue';
-
- export default defineComponent({
- components: { IonButton, IonContent },
- setup() {
- const content = ref();
- const scrollToBottom = () => {
- content.value.$el.scrollToBottom(300);
- };
-
- return { content, scrollToBottom };
- },
- });
- </script>
Ionic Vue 预装了Ionicons。开发人员可以在他们的应用程序中通过以下几种方式使用它们。
在组件中导入的方式是使用 Ionicons 的推荐方法。这涉及从ionicons
包中导入您选择的图标并将其传递给您的模板:
- <template>
- <ion-page>
- <ion-content>
- <ion-icon :icon="heart"></ion-icon>
- </ion-content>
- </ion-page>
- </template>
-
- <script>
- import { heart } from 'ionicons/icons';
- import { IonContent, IonPage } from '@ionic/vue';
- import { defineComponent } from 'vue';
-
- export default defineComponent({
- name: 'Icon',
- components: {
- IonContent,
- IonPage,
- },
- setup() {
- return { heart };
- },
- });
- </script>
让我们分解一下我们在这里所做的事情。首先,我们从 ionicons/icons
导入heart
图标。这将为我们的图标加载适当的 SVG 数据。
接下来,通过heart
在方法中将数据传递给我们的模板setup
。
最后,我们通过属性将图标数据传递给ion-icon
组件。icon
开发者还可以根据模式设置不同的图标:
- <template>
- <ion-page>
- <ion-content>
- <ion-icon :ios="logoApple" :md="logoAndroid"></ion-icon>
- </ion-content>
- </ion-page>
- </template>
-
- <script>
- import { logoAndroid, logoApple } from 'ionicons/icons';
- import { IonContent, IonPage } from '@ionic/vue';
- import { defineComponent } from 'vue';
-
- export default defineComponent({
- name: 'Icon',
- components: {
- IonContent,
- IonPage,
- },
- setup() {
- return { logoAndroid, logoApple };
- },
- });
- </script>
注意
任何带有连字符的图标名称在导入时都应使用驼峰式大小写。
另一种选择是全局导入特定图标。通常不建议这样做,因为它会在应用程序每次启动时强制加载图标,并且会增加应用程序的初始块大小。
全局加载图标的用例如下:
main.ts
- import { addIcons } from 'ionicons';
- import { heart } from 'ionicons/icons';
-
- addIcons({
- heart: heart,
- });
Home.vue
- <template>
- <ion-page>
- <ion-content>
- <ion-icon icon="heart"></ion-icon>
- </ion-content>
- </ion-page>
- </template>
-
- <script>
- import { IonContent, IonPage } from '@ionic/vue';
- import { defineComponent } from 'vue';
-
- export default defineComponent({
- name: 'Home',
- components: {
- IonContent,
- IonPage,
- },
- });
- </script>
在main.ts
中,该addIcons
函数允许我们全局注册图标并给它一个字符串作为键。然后,我们在Home
组件中通过该键引用图标。
Vue 为您提供了多种工具来微调您的应用程序。本节将介绍与 Ionic Framework 最相关的选项。
默认情况下,Ionic Framework 组件在本地注册。通过本地注册,这些组件被导入并提供给您想要使用它们的每个 Vue 组件。这是推荐的方法,因为它允许延迟加载和 treeshaking 与 Ionic Framework 组件一起正常工作。
这种方法的一个缺点是多次重新导入 Ionic Framework 组件可能很乏味。但是,我们认为您获得的性能收益是值得的。
另请注意,本地注册的组件在子组件中不可用。您需要重新导入要在子组件中使用的 Ionic Framework 组件。
我们来看看本地组件注册是如何工作的:
- <template>
- <ion-page>
- <ion-content>
- <Subcomponent></Subcomponent>
- </ion-content>
- </ion-page>
- </template>
-
- <script lang="ts">
- import { defineComponent } from 'vue';
- import { IonContent, IonPage } from '@ionic/vue';
- import Subcomponent from '@/components/Subcomponent.vue';
-
- export default defineComponent({
- components: { IonContent, IonPage, Subcomponent },
- });
- </script>
在上面的示例中,我们使用了IonPage和
IonContent
组件。要使用它们,我们首先从@ionic/vue中
导入,然后,我们在选项中将它们提供给我们的 Vue 组件。从那里,我们可以使用模板中的组件。
请注意,由于我们是在本地注册的IonPage和
IonContent
组件,在子组件中必须重新注册才可以使用。
有关更多信息,请参阅本地注册 Vue 文档。
注册组件的另一个选项是使用全局注册。全局注册涉及导入您要使用的组件,并在您的 Vue 应用程序实例main.ts中
调用component
方法。
虽然这使得将 Ionic Framework 组件添加到您的 Vue 应用程序变得更加容易,但全局注册通常并不推荐。就像 Vue 文档中提到的:“如果你使用像 Webpack 这样的构建系统,全局注册所有组件意味着即使你停止使用一个组件,它仍然可以包含在你的最终构建中。这不必要地增加了你的 JavaScript 数量,强迫用户必须下载”。
我们来看看全局组件注册是如何工作的:
main.ts
- import { IonContent, IonicVue, IonPage } from '@ionic/vue';
-
- const app = createApp(App).use(IonicVue).use(router);
-
- app.component('ion-content', IonContent);
- app.component('ion-page', IonPage);
MyComponent.vue
- <template>
- <ion-page>
- <ion-content>
- <Subcomponent></Subcomponent>
- </ion-content>
- </ion-page>
- </template>
-
- <script lang="ts">
- import { defineComponent } from 'vue';
- import Subcomponent from '@/components/Subcomponent.vue';
-
- export default defineComponent({
- components: { Subcomponent },
- });
- </script>
在上面的示例中,我们使用了IonPage和
IonContent
组件。要使用它们,我们首先在main.ts中通过
@ionic/vue
导入它们。在那里,我们在我们的应用实例上调用该component
方法,并将标签名称和组件定义传递给它。完成此操作后,我们可以在应用程序的其余部分中使用这些组件,而无需将它们导入到每个 Vue 组件中。
有关更多信息,请参阅全局注册 Vue 文档。
默认情况下,Vue CLI 会自动为应用程序中的 JavaScript 生成预提取提示。预提取利用浏览器空闲时间来下载用户可能在不久的将来访问的文档。当用户访问需要预提取文档的页面时,可以从浏览器的缓存中快速提供。
预提取会消耗带宽,因此如果您有一个大型应用程序,您可能需要禁用它。您可以通过修改或创建vue.config.js
文件来做到这一点:
vue.config.js
- module.exports = {
- chainWebpack: (config) => {
- config.plugins.delete('prefetch');
- },
- };
上面的配置将阻止所有文件被预提取,而是在需要时加载。您还可以选择某些块进行预提取。查看有关预取的 Vue CLI 文档以获取更多示例。
我们现在已经掌握了 Ionic Vue 应用程序的基础知识,包括一些 UI 组件和导航。Ionic Framework 组件的伟大之处在于它们可以在任何地方工作,包括 iOS、Android 和 PWA。为了部署到移动、桌面等,我们使用 Ionic 的跨平台应用运行时Capacitor。它提供了一组一致的、以 Web 为中心的 API,使应用程序能够尽可能接近 Web 标准,同时在支持它们的平台上访问丰富的本机设备功能。
添加本机功能很容易。首先,将 Capacitor 添加到您的项目中:
ionic integrations enable capacitor
接下来,构建项目,然后添加您选择的平台:
- ionic build
- ionic cap add ios
- ionic cap add android
我们使用标准的原生 IDE(Xcode 和 Android Studio)来打开、构建和运行 iOS 和 Android 项目:
- ionic cap open ios
- ionic cap open android
可以在此处找到更多详细信息。
接下来,检查所有可用的 API 。有一些很棒的功能,包括Camera API。我们只需几行代码就可以实现照片捕捉功能:
- <template>
- <ion-page>
- <ion-header>
- <ion-toolbar>
- <ion-title>Ionic Blank</ion-title>
- </ion-toolbar>
- </ion-header>
- <ion-content class="ion-padding">
- <img :src="photo" />
- <ion-button @click="takePhoto()">Take Photo</ion-button>
- </ion-content>
- </ion-page>
- </template>
-
- <script lang="ts">
- import { IonButton, IonContent, IonHeader, IonPage, IonTitle } from '@ionic/vue';
- import { defineComponent, ref } from 'vue';
- import { Plugins, CameraResultType } from '@capacitor/core';
- const { Camera } = Plugins;
-
- export default defineComponent({
- name: 'Home',
- components: {
- IonButton,
- IonContent,
- IonHeader,
- IonPage,
- IonTitle,
- },
- setup() {
- const imageSrc = ref('');
- const takePhoto = async () => {
- const image = await Camera.getPhoto({
- quality: 90,
- allowEditing: true,
- resultType: CameraResultType.Uri,
- });
-
- imageSrc.value = image.webPath;
- };
-
- return {
- photo: imageSrc,
- takePhoto,
- };
- },
- });
- </script>
Ionic 的伟大之处在于,通过一个代码库,您可以只使用 HTML、CSS 和 JavaScript 为任何平台构建。跟随我们一步一步地创建一个真实的应用程序来学习 Ionic 应用程序开发的基础知识。
我们将创建一个 Photo Gallery 应用程序,该应用程序提供使用您设备的相机拍摄照片、将它们显示在网格中并将它们永久存储在设备上的功能。
亮点包括:
在 GitHub 上查找本指南中引用的完整应用程序代码。
立即下载并安装这些以确保最佳的 Ionic 开发体验:
在命令行终端中运行以下命令来安装 Ionic CLI ( ionic
),native-run
用于在设备和模拟器/模拟器上运行本机二进制文件,以及cordova-res
用于生成本机应用程序图标和启动屏幕:
注意
要在 Visual Studio Code 中打开终端,请转到终端 -> 新终端。
npm install -g @ionic/cli@latest native-run cordova-res
注意
该
-g
选项意味着全局安装。全局安装包时,EACCES
可能会发生权限错误。考虑将 npm 设置为在没有提升权限的情况下全局运行。有关详细信息,请参阅解决权限错误。
接下来,创建一个 Ionic Vue 应用程序,该应用程序使用“Tabs”启动模板并添加 Capacitor 以实现本机功能:
ionic start photo-gallery tabs --type vue --capacitor
这个入门项目包含三个预先构建的页面和 Ionic 开发的最佳实践。有了通用的构建块,我们可以轻松添加更多功能!
接下来,切换到 app 文件夹:
cd photo-gallery
接下来,我们需要安装必要的 Capacitor 插件以使应用程序的本机功能正常工作:
npm install @capacitor/camera @capacitor/storage @capacitor/filesystem
一些 Capacitor 插件,包括 Camera API,通过 Ionic PWA Elements 库提供基于 Web 的功能和 UI 。
这是一个单独的依赖项,所以接下来安装它:
npm install @ionic/pwa-elements
安装后,在您选择的代码编辑器中打开项目。
接下来,通过编辑src/main.ts
.导入@ionic/pwa-elements
- // Above the createApp() line
- import { defineCustomElements } from '@ionic/pwa-elements/loader';
-
- // Call the element loader after the platform has been bootstrapped
- defineCustomElements(window);
在你的 命令行中运行这个命令:
ionic serve
有三个选项卡。单击 Tab2 选项卡。这是一块空白的画布,也就是变成照片库的理想场所。Ionic CLI 具有实时重新加载功能,因此当您进行更改并保存它们时,应用程序会立即更新!
打开/src/views/Tab2.vue
. 我们看:
- <template>
- <ion-page>
- <ion-header>
- <ion-toolbar>
- <ion-title>Tab 2</ion-title>
- </ion-toolbar>
- </ion-header>
- <ion-content :fullscreen="true">
- <ion-header collapse="condense">
- <ion-toolbar>
- <ion-title size="large">Tab 2</ion-title>
- </ion-toolbar>
- </ion-header>
-
- <ExploreContainer name="Tab 2 page" />
- </ion-content>
- </ion-page>
- </template>
我们将应用程序的视觉方面放入<ion-content>
. 在这种情况下,我们将在其中添加一个按钮,用于打开设备的摄像头并显示摄像头捕获的图像。但首先,删除ExploreContainer
组件,从 import 语句开始:
import ExploreContainer from '@/components/ExploreContainer.vue';
接下来,从Default Export 和 HTMLExploreContainer
的列表中删除组件名称 ( ) :components
<ExploreContainer name="Tab 2 page" />
我们将用浮动操作按钮(FAB) 替换它。首先,更新<script>
标签中的导入以包含相机图标以及我们将很快使用的一些 Ionic 组件:
- import { camera, trash, close } from 'ionicons/icons';
- import {
- IonPage,
- IonHeader,
- IonFab,
- IonFabButton,
- IonIcon,
- IonToolbar,
- IonTitle,
- IonContent,
- IonGrid,
- IonRow,
- IonCol,
- IonImg,
- } from '@ionic/vue';
接下来,将我们将使用的新 Ionic 组件添加到默认导出中,并在setup()
方法中返回 Ionicons(Composition API的一部分):
- export default {
- name: 'Tab2',
- components: {
- IonPage,
- IonHeader,
- IonFab,
- IonFabButton,
- IonIcon,
- IonToolbar,
- IonTitle,
- IonContent,
- IonGrid,
- IonRow,
- IonCol,
- IonImg,
- },
- setup() {
- return {
- camera,
- trash,
- close,
- };
- },
- };
然后,将 FAB 添加到页面底部。使用摄像头图像作为图标,takePhoto()
点击该按钮时调用该函数(即将实现):
- <ion-content :fullscreen="true">
- <ion-fab vertical="bottom" horizontal="center" slot="fixed">
- <ion-fab-button @click="takePhoto()">
- <ion-icon :icon="camera"></ion-icon>
- </ion-fab-button>
- </ion-fab>
- </ion-content>
稍后我们将创建takePhoto
使用相机和其他原生功能的方法和逻辑。
接下来,打开src/views/TabsPage.vue
删除图标ellipse并改为导入图标images:
import { images, square, triangle } from 'ionicons/icons';
在标签栏 ( <ion-tab-bar>
) 中,将标签更改为“照片”,将中间标签按钮的ellipse
图标更改为:images
- <ion-tab-button tab="tab2" href="/tabs/tab2">
- <ion-icon :icon="images" />
- <ion-label>相册</ion-label>
- </ion-tab-button>
最后在setup()
方法中将ellipse
替换为images
- setup() {
- return {
- images,
- square,
- triangle,
- }
- }
添加使用 Capacitor Camera API使用设备相机拍照的功能。我们将从为 Web 构建它开始,然后进行一些小的调整以使其在移动设备(iOS 和 Android)上运行。
为此,我们将创建一个与 Vue 的 Composition API 配对的独立合成函数来管理画廊的照片。
注意
如果你不熟悉 Vue 的 Composition API,为什么选择 Composition API?Vue 官方文档是一个很好的入门资源。
创建一个新文件src/composables/usePhotoGallery.ts
并打开它。
我们将首先从 Vue 核心和 Capacitor 导入我们将使用的各种实用程序:
- import { ref, onMounted, watch } from 'vue';
- import { Camera, CameraResultType, CameraSource, Photo } from '@capacitor/camera';
- import { Filesystem, Directory } from '@capacitor/filesystem';
- import { Storage } from '@capacitor/storage';
接下来,创建一个名为 usePhotoGallery 的函数:
- export function usePhotoGallery() {
- const takePhoto = async () => {
- const photo = await Camera.getPhoto({
- resultType: CameraResultType.Uri,
- source: CameraSource.Camera,
- quality: 100,
- });
- };
-
- return {
- takePhoto,
- };
- }
我们的usePhotoGallery
函数公开了一个名为 takePhoto 的方法,该方法又调用 Capacitor Camera API 的getPhoto
方法。
请注意这里的神奇之处:没有特定于平台的代码(Web、iOS 或 Android)!Capacitor Camera 插件为我们抽象了这一点,只留下一个方法调用 -getPhoto()
这将打开设备的相机并允许我们拍照。
我们需要采取的最后一步是使用 Tab2 页面中的新功能。回到 Tab2.vue 并导入它:
import { usePhotoGallery } from '@/composables/usePhotoGallery';
接下来,在默认导出中,添加一个 setup 方法,它是Composition API的一部分。从usePhotoGallery中
解构takePhoto
函数,然后返回它:
- <script lang="ts">
- import { camera, trash, close } from 'ionicons/icons';
- import { IonPage, IonHeader, IonFab, IonFabButton, IonIcon,
- IonToolbar, IonTitle, IonContent, IonGrid, IonRow,
- IonCol, IonImg } from '@ionic/vue';
- import { usePhotoGallery } from '@/composables/usePhotoGallery';
-
- export default {
- name: 'Tab2',
- components: { IonPage, IonHeader, IonFab, IonFabButton, IonIcon,
- IonToolbar, IonTitle, IonContent, IonGrid, IonRow,
- IonCol, IonImg },
- setup() {
- const { takePhoto } = usePhotoGallery();
-
- return {
- takePhoto,
- camera, trash, close
- }
- }
- }
- </script>
在照片库选项卡上,单击相机按钮。如果您的计算机有任何类型的网络摄像头,则会出现一个模式窗口。自拍一张(由于种种原因,我并没有露脸)!
拍照后,它立即消失。我们仍然需要在我们的应用程序中显示它并保存它以供将来访问。
首先,我们将创建一个新类型来定义我们的照片,它将保存特定的元数据。将以下 UserPhoto 接口添加到usePhotoGallery.ts
文件中,在 main 函数之外的某个位置:
- export interface UserPhoto {
- filepath: string;
- webviewPath?: string;
- }
回到函数的顶部(在引用 Capacitor Camera 插件之后),定义一个数组,以便我们可以存储使用相机拍摄的每张照片。使用 Vue 的ref 函数使其成为反应变量。
const photos = ref<UserPhoto[]>([]);
当相机完成拍照后,Photo
从 Capacitor 返回的结果将被添加到photos
数组中。更新takePhoto
方法,在Camera.getPhoto
之后添加以下代码:
- const fileName = new Date().getTime() + '.jpeg';
- const savedFileImage = {
- filepath: fileName,
- webviewPath: photo.webPath,
- };
-
- photos.value = [savedFileImage, ...photos.value];
接下来,更新 return 语句以包含 photos 数组:
- return {
- photos,
- takePhoto,
- };
回到 Tab2 组件,更新 import 语句以包含UserPhoto
接口:
import { usePhotoGallery, UserPhoto } from '@/composables/usePhotoGallery';
然后,访问照片数组:
const { photos, takePhoto } = usePhotoGallery();
最后,添加photos
返回setup()
:
- return {
- photos,
- takePhoto,
- camera,
- trash,
- close,
- };
将照片存储到主数组中,我们现在可以在屏幕上显示图像。添加一个Grid 组件,以便每张照片在添加到图库时都能很好地显示,并循环遍历 photos 数组中的每张照片,为每张照片添加一个 Image 组件 ( <ion-img>
)。将src
(源)指向照片的路径:
- <ion-content>
- <ion-grid>
- <ion-row>
- <ion-col size="6" :key="photo" v-for="photo in photos">
- <ion-img :src="photo.webviewPath"></ion-img>
- </ion-col>
- </ion-row>
- </ion-grid>
-
- <!-- <ion-fab> markup -->
- </ion-content>
保存所有文件。在网络浏览器中,单击相机按钮并拍摄另一张照片。这一次,照片显示在照片库中!
接下来,我们将添加对将照片保存到文件系统的支持,以便以后可以在我们的应用程序中检索和显示它们。
我们现在可以拍摄多张照片并将它们显示在我们应用程序第二个选项卡上的照片库中。但是,这些照片目前并未永久存储,因此当应用程序关闭时,它们将丢失。
幸运的是,将它们保存到文件系统只需要几个步骤。首先打开usePhotoGallery
函数 ( src/composables/usePhotoGallery.ts
),然后从Filesystem
类中访问writeFile
方法:
接下来,创建几个新函数。文件系统 API 要求写入磁盘的文件作为 base64 数据传入,因此稍后将使用此辅助函数来协助:
- const convertBlobToBase64 = (blob: Blob) =>
- new Promise((resolve, reject) => {
- const reader = new FileReader();
- reader.onerror = reject;
- reader.onload = () => {
- resolve(reader.result);
- };
- reader.readAsDataURL(blob);
- });
接下来,添加一个将照片保存到文件系统的函数。我们传入photo
表示新捕获的设备照片的对象以及文件名,该文件名将为要存储的文件提供路径。
接下来我们使用 Capacitor Filesystem API将照片保存到文件系统。我们首先将照片转换为 base64 格式,然后将数据提供给 Filesystem 的writeFile
函数:
- const savePicture = async (photo: Photo, fileName: string): Promise<UserPhoto> => {
- let base64Data: string;
-
- // Fetch the photo, read as a blob, then convert to base64 format
- const response = await fetch(photo.webPath!);
- const blob = await response.blob();
- base64Data = (await convertBlobToBase64(blob)) as string;
-
- const savedFile = await Filesystem.writeFile({
- path: fileName,
- data: base64Data,
- directory: Directory.Data,
- });
-
- // Use webPath to display the new image instead of base64 since it's
- // already loaded into memory
- return {
- filepath: fileName,
- webviewPath: photo.webPath,
- };
- };
最后,更新takePhoto
要调用的函数savePicture
。保存照片后,将其插入反应photos
数组的前面:
- const takePhoto = async () => {
- const photo = await Camera.getPhoto({
- resultType: CameraResultType.Uri,
- source: CameraSource.Camera,
- quality: 100,
- });
-
- const fileName = new Date().getTime() + '.jpeg';
- const savedFileImage = await savePicture(photo, fileName);
-
- photos.value = [savedFileImage, ...photos.value];
- };
这样,每次拍摄新照片时,它都会自动保存到文件系统中。
我们已经实现了拍照并保存到文件系统。缺少最后一项功能:照片存储在文件系统中,但我们需要一种方法来保存指向每个文件的指针,以便它们可以再次显示在照片库中。
幸运的是,这很容易:我们将利用 Capacitor Storage API将我们的照片数组存储在键值存储中。
首先定义一个常量变量,该变量将作为usePhotoGallery
函数顶部的存储键
const PHOTO_STORAGE = 'photos';
接下来,添加一个cachePhotos
将 Photos 数组作为 JSON 保存到文件存储的函数:
- const cachePhotos = () => {
- Storage.set({
- key: PHOTO_STORAGE,
- value: JSON.stringify(photos.value),
- });
- };
接下来,使用 Vue 的watch 函数来观察photos
数组。每当阵列被修改(在这种情况下,拍摄或删除照片),触发该cachePhotos
功能。我们不仅可以重用代码,而且应用程序用户何时关闭或切换到其他应用程序也无关紧要 - 始终保存照片数据。
watch(photos, cachePhotos);
现在照片数组数据已保存,创建一个函数以在 Tab2 加载时检索数据。首先,从 Storage 中检索照片数据,然后将每张照片的数据转换为 base64 格式:
- const loadSaved = async () => {
- const photoList = await Storage.get({ key: PHOTO_STORAGE });
- const photosInStorage = photoList.value ? JSON.parse(photoList.value) : [];
-
- for (const photo of photosInStorage) {
- const file = await Filesystem.readFile({
- path: photo.filepath,
- directory: Directory.Data,
- });
- photo.webviewPath = `data:image/jpeg;base64,${file.data}`;
- }
-
- photos.value = photosInStorage;
- };
在移动设备上(下一节会讲到!),我们可以直接将图像标签的来源设置为<img src="x" />
文件系统上的每个照片文件,并自动显示它们。然而,在 Web 上,我们必须将文件系统中的每个图像读取为 base64 格式,因为文件系统 API 将它们存储在底层 IndexedDB中的 base64 中。
最后,我们需要一种在loadSaved
加载照片库页面时调用该函数的方法。为此,请使用 Vue安装的生命周期钩子。首先,onMounted
从 Vue 导入:
import { ref, onMounted, watch } from 'vue';
然后,在usePhotoGallery
函数中,添加onMounted
函数并调用loadSaved
:
onMounted(loadSaved);
至此,我们在可在网络上运行的 Ionic 应用程序中构建了一个完整的照片库功能。接下来,我们将把它变成一个适用于 iOS 和 Android 的移动应用程序!
在 iOS、Android 和 Web 上运行之前,我们的照片库应用程序并不完整——所有这些都使用一个代码库。只需要一些小的逻辑更改来支持移动平台,安装一些本地工具,然后在设备上运行应用程序。我们走吧!
让我们从进行一些小的代码更改开始——然后我们的应用程序将在我们将其部署到设备时“正常工作”。
首先,我们将更新照片保存功能以支持移动设备。我们将根据平台(移动设备或网络)运行略有不同的代码。从 Ionic Vue导入Platform
API:
import { isPlatform } from '@ionic/vue';
在savePicture
函数中,检查应用程序在哪个平台上运行。如果是“混合”(Capacitor,本机运行时),则使用该readFile
方法将照片文件读取为 base64 格式。此外,使用 Filesystem API 返回照片的完整文件路径。设置时webviewPath
,请使用特殊Capacitor.convertFileSrc
方法(此处详细说明)。否则,在 web 上运行应用程序时使用与以前相同的逻辑。
- const savePicture = async (photo: Photo, fileName: string): Promise<UserPhoto> => {
- let base64Data: string;
- // "hybrid" will detect mobile - iOS or Android
- if (isPlatform('hybrid')) {
- const file = await Filesystem.readFile({
- path: photo.path!,
- });
- base64Data = file.data;
- } else {
- // Fetch the photo, read as a blob, then convert to base64 format
- const response = await fetch(photo.webPath!);
- const blob = await response.blob();
- base64Data = (await convertBlobToBase64(blob)) as string;
- }
- const savedFile = await Filesystem.writeFile({
- path: fileName,
- data: base64Data,
- directory: Directory.Data,
- });
-
- if (isPlatform('hybrid')) {
- // Display the new image by rewriting the 'file://' path to HTTP
- // Details: https://ionicframework.com/docs/building/webview#file-protocol
- return {
- filepath: savedFile.uri,
- webviewPath: Capacitor.convertFileSrc(savedFile.uri),
- };
- } else {
- // Use webPath to display the new image instead of base64 since it's
- // already loaded into memory
- return {
- filepath: fileName,
- webviewPath: photo.webPath,
- };
- }
- };
接下来,在loadSaved
函数中添加一点新的逻辑。在移动设备上,我们可以直接指向 Filesystem 上的每个照片文件并自动显示它们。然而,在网络上,我们必须将文件系统中的每个图像读取为 base64 格式。这是因为文件系统 API 在底层使用了IndexedDB。更新loadSaved
函数:
- const loadSaved = async () => {
- const photoList = await Storage.get({ key: PHOTO_STORAGE });
- const photosInStorage = photoList.value ? JSON.parse(photoList.value) : [];
-
- // If running on the web...
- if (!isPlatform('hybrid')) {
- for (const photo of photosInStorage) {
- const file = await Filesystem.readFile({
- path: photo.filepath,
- directory: Directory.Data,
- });
- // Web platform only: Load the photo as base64 data
- photo.webviewPath = `data:image/jpeg;base64,${file.data}`;
- }
- }
-
- photos.value = photosInStorage;
- };
我们的照片库现在包含一个可在 Web、Android 和 iOS 上运行的代码库。接下来,我们将应用程序部署到设备。
由于我们在首次创建 Capacitor 时将其添加到我们的项目中,因此在照片库应用程序在我们的设备上之前只剩下几个步骤!
提示
请记住,您可以在此处找到此应用程序的完整源代码。
Capacitor 是 Ionic 的官方应用运行时,可以轻松地将 Web 应用部署到 iOS、Android 等原生平台。如果您过去使用过 Cordova,请考虑在此处阅读更多关于差异的信息。
完成 Ionic 项目的全新构建,修复它报告的所有错误:
ionic build
接下来,创建 iOS 和 Android 项目:
- $ ionic cap add ios
- $ ionic cap add android
项目根目录下的 android 和 ios 文件夹均已创建。这些是完全独立的本机项目,应该被视为您的 Ionic 应用程序的一部分(即,将它们检入源代码控制,使用它们的本机工具对其进行编辑等)。
每次您执行ionic build
更新您的web目录(默认:)的构建(例如)时build
,您都需要将这些更改复制到您的本机项目中:
ionic cap copy
注意:对代码的本机部分进行更新(例如添加新插件)后,使用sync
命令:
ionic cap sync
注意
要构建 iOS 应用程序,您需要一台 Mac 计算机。
Capacitor iOS 应用程序通过 Xcode(Apple 的 iOS/Mac IDE)配置和管理,依赖项由 CocoaPods 管理。在 iOS 设备上运行此应用程序之前,需要完成几个步骤。
首先,运行 Capacitoropen
命令,在 Xcode 中打开原生 iOS 项目:
ionic cap open ios
为了使某些本机插件工作,必须配置用户权限。在我们的照片库应用程序中,这包括相机插件:iOS 在第一次Camera.getPhoto()
调用后会自动显示一个模式对话框,提示用户允许应用程序使用相机。驱动这个的权限被标记为“隐私 - 相机使用”。要设置它,必须修改Info.plist
文件(此处有更多详细信息)。要访问它,请单击“信息”,然后展开“自定义 iOS 目标属性”。
Info.plist
中的每个设置都有一个低级参数名称和一个高级名称。默认情况下,属性列表编辑器显示高级名称,但切换到显示原始的低级名称通常很有用。为此,请右键单击属性列表编辑器中的任意位置并切换“原始键/值”。
添加NSCameraUsageDescription
键并将值设置为描述应用程序需要使用相机的原因,例如“拍照”。权限提示打开时,会向应用程序用户显示值字段。
按照相同的过程添加相机插件所需的其他两个键:NSPhotoLibraryAddUsageDescription
和NSPhotoLibraryUsageDescription
。
接下来,单击App
左侧的 Project Navigator,然后在该Signing & Capabilities
部分中选择您的开发团队。
有了适当的权限并选择了开发团队,我们就可以在真实设备上试用该应用程序了!将 iOS 设备连接到您的 Mac 计算机,选择它(对我来说是App -> Matthew’s iPhone
)然后单击“构建”按钮在您的设备上构建、安装和启动应用程序:
点击照片库选项卡上的相机按钮后,将显示权限提示。点击确定,然后使用相机拍照。之后,照片在应用程序中显示!
Capacitor Android 应用程序通过 Android Studio 进行配置和管理。在 Android 设备上运行此应用程序之前,需要完成几个步骤。
首先,运行 Capacitor open命令,在 Android Studio 中打开原生 Android 项目:
ionic cap open android
与 iOS 类似,我们必须启用正确的权限才能使用相机。在AndroidManifest.xml
文件中配置这些。Android Studio 可能会自动打开此文件,但如果没有,请在android/app/src/main/
.
滚动到该Permissions
部分并确保包含以下条目:
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
保存文件。有了适当的权限,我们就可以在真实设备上试用该应用程序了!将 Android 设备连接到您的计算机。在 Android Studio 中,单击“运行”按钮,选择附加的 Android 设备,然后单击“确定”在您的设备上构建、安装和启动应用程序。
再次点击照片库选项卡上的相机按钮后,应显示权限提示。点击确定,然后使用相机拍照。之后,照片应该出现在应用程序中。
至此,一个完整的app项目已经完成。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。