跳转至正文
教程

使用 Angular 和 Capacitor 构建移动应用

学习如何使用 Angular 和 Capacitor 开发移动应用,并掌握提升 Capgo 原生导航、过渡动画及 iOS 布局最佳实践的方法。

使用 Angular 和 Capacitor 构建移动应用 Capgo 博客插图

在本教程中,我们将从一个全新的 Angular Angular 应用开始,并使用 Capacitor 进阶到原生移动应用领域。你还可以添加 Capgo Native Navigation and Transitions 来获得原生移动体验,并使用 tailwind-capacitor 来处理安全区域。

Capacitor 可以让你轻松地将 Angular Web 应用转换为原生移动应用,无需进行大规模修改,也不必学习像 React Native 这样的新技能。

只需几个简单的步骤,大多数 Angular 应用都能转化为移动端 App。

本教程将带你走完整个流程:先创建一个新的 Angular 应用,然后引入 Capacitor,进入原生移动应用领域。你还可以使用 Capgo Native Navigation、Transitions,以及用于安全区域的 tailwind-capacitor。

关于 Capacitor

CapacitorJS 彻底改变了开发体验!你可以轻松把它集成到任何 Web 项目中,它会将你的应用封装进原生 WebView,并为你生成原生的 Xcode 和 Android Studio 项目。此外,它的插件还能通过 JS bridge 访问摄像头等原生设备功能。

借助 Capacitor,你无需复杂配置,也不用面对陡峭的学习曲线,就能获得出色的原生移动应用。其轻量的 API 和精简的功能让集成变得轻而易举。相信我,你会惊讶于用 Capacitor 实现功能完备的原生 App 竟如此简单!

准备你的 Angular App

要创建一个新的 Angular 应用,请运行以下命令:

ng new my-app
cd my-app

当提示选择 Angular 版本时,请选择 “Angular”。

要创建一个原生移动应用,我们需要一个 export 项目。因此,让我们在 package.json 可用于构建并复制 Angular 项目:

{
  "scripts": {
    // ...
    "build": "ng build --prod"
  }
}

执行完该命令 build后,你应该能在 dist 项目根目录下看到一个新的文件夹。

Capacitor 稍后会用到这个文件夹,但现在我们需要正确配置它。

在 Angular App 中添加 Capacitor

要将任何 Web App 打包进原生移动容器,我们需要先完成几个初始步骤,但之后只需执行一条 sync 命令。

首先,我们可以安装 Capacitor CLI 将其作为开发依赖安装,然后在我们的项目中配置。设置过程中,按“enter”即可接受 name 和 bundle ID 的默认值。

接下来,我们需要安装核心包以及 iOS 和 Android 平台的相关包。

最后,我们可以添加平台,Capacitor 会在项目根目录下为每个平台创建文件夹:

# Install the Capacitor CLI locally
npm install -D @capacitor/cli

# Initialize Capacitor in your Angular project
npx cap init

# Install the required packages
npm install @capacitor/core @capacitor/ios @capacitor/android

# Add the native platforms
npx cap add ios
npx cap add android

到这一步,你应该已经能看到新的 iOS 以及 Android 文件夹出现在你的 Angular 项目中了。

这些都是真正的原生项目!

之后要访问 Android 项目,你需要安装 Android Studio 中的错误日志. 对于 iOS,你需要一台 Mac,并安装 Xcode 的 Mac.

此外,你还需要找到 capacitor.config.ts 文件,其中包含同步过程中用到的一些基础 Capacitor 设置。你唯一需要注意的是 webDir,它必须指向 build 命令的输出目录。目前该配置不正确。

要修复此问题,请打开 capacitor.config.json 文件并更新 webDir:

{
  "appId": "com.example.app",
  "appName": "my-app",
  "webDir": "dist"
}

你可以执行以下命令来试用:

npm run build
npx cap sync

第一条命令 npm run build 只会构建你的 Angular 项目并复制静态构建产物,而第二个命令 npx cap sync 会将所有 Web code 同步到原生平台的对应位置,以便在 App 中显示。

此外,sync 命令可能会更新原生平台并安装插件,因此当你安装新的 Capacitor 插件 是时候运行 npx cap sync 再次尝试。

不知不觉中,你已经完成了所有操作,现在让我们在真机上看看 App 吧!

构建并部署原生应用

要开发 iOS 应用,你需要安装 Xcode 的 Mac 好,而开发 Android 应用则需要安装 Android Studio 中的错误日志 。此外,如果你计划在应用商店发布应用,则必须加入 iOS 的 Apple Developer Program 和 Android 的 Google Play Console。

如果你刚接触原生移动开发,可以使用 Capacitor CLI 轻松打开这两个原生项目:

npx cap open ios
npx cap open android

原生项目配置好后,将应用部署到已连接的设备非常简单。在 Android Studio 中,只需等待一切就绪,无需更改任何设置即可将应用部署到已连接的设备。示例如下:

android-studio-run

在 Xcode 中,你需要配置签名账号,才能将应用部署到真机而非仅在模拟器上运行。如果你之前没有操作过,Xcode 会引导你完成整个流程(但前提是你必须加入了 Developer Program)。之后,你只需点击运行按钮,即可在顶部选中的已连接设备上运行应用。示例如下:

xcode-run

恭喜!你已成功将 Angular Web 应用部署到移动设备。示例如下:

angular-mobile-app

不过等等,在开发过程中其实还有一种更快捷的方法……

Capacitor 热重载

到目前为止,你可能已经习惯了现代框架提供的热重载(hot reload)功能,好消息是,你也可以在移动设备上 实现同样的功能 而且几乎无需额外工作!

通过 live reload 访问本地托管的应用 在您的网络中 通过让 Capacitor 应用从特定 URL 加载内容来实现。

第一步是确定你的本地 IP 地址。如果你使用的是 Mac,可以在终端运行以下命令来查看:

ipconfig getifaddr en0

在 Windows 上,运行:

ipconfig

然后找到 IPv4 地址。

我们可以通过向我们的配置中再添加一个条目,让 Capacitor 直接从服务器加载应用 capacitor.config.ts 文件:

import { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
  appId: 'com.example.app',
  appName: 'my-app',
  webDir: 'dist',
  bundledWebRuntime: false,
  server: {
    url: 'http://192.168.x.xx:4200',
    cleartext: true
  }
};

export default config;

请务必使用 正确的 IP 和端口,我在本示例中使用了 Angular 默认端口。

现在,我们可以通过将这些更改复制到我们的原生项目中来应用它们:

npx cap copy

本指南中介绍的 copy 命令类似于 sync,但它只会 将 web 文件夹中的更改复制过来 以及配置,而不会更新 native 项目。

您现在仍可以通过 Android Studio 或 Xcode 部署应用。之后,如果您修改了 Angular 应用中的内容, App 将会自动重新加载 并显示更改!

请记住 如果你安装了新的插件(例如 camera),仍然需要重新构建你的 native 项目。这是因为 native 文件发生了变化,无法即时完成。

请注意,您应在配置中使用正确的 IP 和端口。上方的 code 代码块仅用于演示 Angular 的默认端口。

使用 Capacitor 插件

我们来看看如何实际使用 Capacitor 插件,前面已经提过几次。为此,可以运行以下命令安装一个相当简单的插件:

npm i @capacitor/share

这个 分享插件,但它还是会弹出原生分享对话框!为此,我们现在只需导入这个 package,并调用相应的 share() 函数即可。让我们把 src/app/app.component.ts 修改为:

import { Component } from '@angular/core';
import { Share } from '@capacitor/share';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'my-app';

  async share() {
    await Share.share({
      title: 'Open Youtube',
      text: 'Check new video on youtube',
      url: 'https://www.youtube.com',
      dialogTitle: 'Share with friends'
    });
  }
}

如前所述,安装新 plugin 时,我们需要执行 sync 操作,然后将应用重新部署到设备上。为此,请运行以下命令:

npx cap sync

点击按钮后,你就能看到精美的原生分享对话框弹出啦!

接下来,你可以通过 Capgo 导航和过渡效果,让应用在 iOS 和 Android 上更具原生感,并修复会导致水平溢出或安全区域被裁剪的常见 iOS 布局问题。

使用 Capgo 原生导航与过渡效果,打造原生感 UI

我多年来一直在使用 Ionic 构建跨平台应用,但把它和 Angular 集成很 hacky,而且当你已经有 Tailwind CSS.

若想在 Angular + Capacitor 应用中获得原生移动端体验,请使用 Capgo 插件,而不是 Konsta UI 等仅限 Web 的 UI 组件库:

  • @capgo/capacitor-native-navigation — 原生导航栏、iOS 上的 Liquid Glass 标签栏,以及 Android 上的毛玻璃标签栏样式。Angular Router 保留路由状态,原生界面外壳则由插件负责。
  • @capgo/capacitor-transitions — 在 WebView 层实现 Ionic 风格的页面过渡和 iOS 边缘侧滑返回,而无需采用 Ionic UI。

安装两者:

bun add @capgo/capacitor-native-navigation @capgo/capacitor-transitions
bunx cap sync

使用 CSS inset 模式配置原生导航,让 Web 内容正确避让原生栏:

import { NativeNavigation } from '@capgo/capacitor-native-navigation';

await NativeNavigation.configure({
  contentInsetMode: 'css',
  animationDuration: 360,
  glass: {
    effect: 'liquidGlass',
  },
});

渲染 Liquid Glass 标签栏(iOS 使用系统原生渲染;Android 使用毛玻璃 WebView 背景):

import { inject } from '@angular/core';
import { Router } from '@angular/router';

const router = inject(Router);

await NativeNavigation.setTabbar({
  selectedId: 'home',
  labelVisibilityMode: 'labeled',
  icons: true,
  colors: { dynamic: true },
  tabs: [
    { id: 'home', title: 'Home', icon: { svg: '...' } },
    { id: 'settings', title: 'Settings', icon: { svg: '...' } },
  ],
});

await NativeNavigation.addListener('tabSelect', ({ id }) => {
  router.navigate([`/${id}`]);
});

Add native page transitions in your app shell:

```typescript
// app.component.ts
import { Component, CUSTOM_ELEMENTS_SCHEMA, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
import { Router } from '@angular/router';
import '@capgo/capacitor-transitions';
import { initTransitions, setDirection, setupRouterOutlet } from '@capgo/capacitor-transitions';

initTransitions({ platform: 'auto' });

@Component({
  selector: 'app-root',
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  template: `
    <cap-router-outlet #outlet platform="auto" swipe-gesture="auto">
      <router-outlet></router-outlet>
    </cap-router-outlet>
  `,
})
export class AppComponent implements AfterViewInit {
  @ViewChild('outlet') outlet?: ElementRef<HTMLElement>;

  constructor(private router: Router) {}

  ngAfterViewInit() {
    if (this.outlet?.nativeElement) {
      setupRouterOutlet(this.outlet.nativeElement, { platform: 'auto', swipeGesture: 'auto' });
    }
  }

  openSettings() {
    setDirection('forward');
    this.router.navigate(['/settings']);
  }
}

将路由页面包裹在 cap-router-outlet, cap-page,以及 cap-content,并调用 setDirection('forward') 或 setDirection('back') 导航前。当原生导航接管这些区域时,请勿重复添加 Web 页眉或页脚。

查看完整指南: 使用 @capgo/capacitor-native-navigation 以及 使用 @capgo/capacitor-transitions.

使用 Tailwind 处理安全区域

在 Tailwind CSS 中处理设备安全区域,请使用 @capgo/tailwind-capacitor (发布为 tailwind-capacitor 在 npm 上)。它提供 safe-areas 工具类和其他对 Capacitor 友好的 Tailwind 插件:

bun add -D tailwind-capacitor

在 src/styles.css:

@import 'tailwindcss';
@plugin "@capgo/tailwind-capacitor/platform";
@plugin "@capgo/tailwind-capacitor/safe-areas";

使用以下工具类,例如 pt-safe, pb-safe,以及 px-safe 而不是零散地 env(safe-area-inset-*) 手动完成。该项目正处于活跃开发阶段 —— 如果你的 Angular 配置中缺少某些功能, 请在 GitHub 上提交 PR.

修复 iOS 布局问题(视口、安全区域与水平溢出)

如果 iOS 上的内容看起来被裁剪、偏移或可水平滚动,仅靠添加更多 overflow-x: hidden 或单独调整 viewport 标签通常无法解决问题。请按顺序进行以下检查。

确保 viewport meta 标签已正确应用

在 src/index.html, 请在...中设置 viewport meta 标签 <head>:

<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />

只用一个根容器处理 iOS 安全区域

创建一个统一的 app shell,并在其中应用 safe area 内边距,而不是在多个嵌套组件中:

html,
body,
app-root {
  width: 100%;
  min-height: 100%;
  margin: 0;
  padding: 0;
  overflow-x: hidden;
}

* {
  box-sizing: border-box;
}

.app-shell {
  min-height: 100dvh;
  width: 100%;
  padding-top: env(safe-area-inset-top);
  padding-right: env(safe-area-inset-right);
  padding-bottom: env(safe-area-inset-bottom);
  padding-left: env(safe-area-inset-left);
}

将所有页面内容包裹在 .app-shell。在 header、modal 和 layout wrapper 中重复设置 safe-area padding 往往会让 UI 看起来被裁剪或过大。

使用 @capgo/tailwind-capacitor,你可以使用如下 utility 类来实现相同的 padding pt-safe pb-safe px-safe 在这个单一 shell 上。

设置 Capacitor iOS contentInset 改为 never 首先

在 capacitor.config.ts,建议禁用 native inset,让 CSS(或 Native Navigation 的 contentInsetMode: 'css')来管理安全区域:

const config: CapacitorConfig = {
  appId: 'com.example.myapp',
  appName: 'my-app',
  webDir: 'www',
  ios: {
    contentInset: 'never',
  },
};

将 Capacitor 的自动 content inset 与 CSS env(safe-area-inset-*) padding 混合使用,是导致间距翻倍的常见原因。

找到真正溢出的元素

常见原因是某个元素使用了 100vw,Tailwind w-screen,固定像素宽度或较大的 min-width.

在 Safari Web Inspector 中运行:

[...document.querySelectorAll('*')]
  .filter(el => el.scrollWidth > document.documentElement.clientWidth)
  .map(el => ({
    el,
    tag: el.tagName,
    class: el.className,
    scrollWidth: el.scrollWidth,
    clientWidth: document.documentElement.clientWidth,
  }));

使用 Tailwind 时,替换 w-screen 配合 w-full 尽可能避免。许多水平溢出问题都来自 100vw / w-screen重复的 safe-area 内边距或固定宽度容器,而不是 viewport meta 标签本身。

总结

Capacitor 是基于现有 Web 项目构建原生应用的绝佳选择,它提供了一种简单的方式来共享 code 并保持一致的 UI。

此外,通过引入 Capgo,为应用添加 Live Updates 变得更加简单,确保用户始终能获得最新功能和 Bug 修复。

如果你想了解如何在 Angular 应用中添加 Capgo,请阅读下一篇文章:

延续《使用 Angular 和 Capacitor 构建移动应用》的内容

如果你正在使用 使用 Angular 和 Capacitor 构建移动应用 来规划原生媒体和界面行为,并将其与 使用 @capgo/capacitor-live-activities 用于使用 @capgo/capacitor-live-activities 时的原生能力, @capgo/capacitor-live-activities 在 @capgo/capacitor-live-activities 中的实现细节, 使用 @capgo/capacitor-video-player 用于使用 @capgo/capacitor-video-player 时的原生能力, @capgo/capacitor-video-player 在 @capgo/capacitor-video-player 中的实现细节,以及 使用 @capgo/capacitor-native-navigation 用于使用 @capgo/capacitor-native-navigation. 时的原生能力

为 Capacitor 应用提供 Live Updates

当 Web 层出现线上 Bug 时,通过 Capgo 发布修复,而不用等 App Store 审核好几天。用户会在后台收到更新,原生变更则仍走正常审核流程。

博客最新动态

Capgo 为你提供打造真正专业的移动应用所需的最有价值的洞察。