本教程将带你使用 React 和 Capacitor 开发移动应用。完成后,你将掌握如何通过 Capacitor 将 React.js Web 应用转化为原生移动应用,并利用 Capgo 原生导航与过渡效果提升原生体验。
Capacitor 可以轻松将你的 React.js Web 应用转换为原生移动应用,无需进行大规模改动,也无需学习 React Native 等新框架。
整个过程只需几个简单的步骤,很快你的 React.js 应用就能成为功能完备的移动应用。请跟随我们的指引开始这段旅程吧。
Capacitor 概览
CapacitorJS 彻底改变了开发模式。它可以与任何 Web 项目无缝集成,在生成原生 Xcode 和 Android Studio 项目的同时,将您的应用封装进原生 WebView 中。此外,通过其插件,您还可以通过 JS bridge 调用摄像头等原生设备功能。
Capacitor 提供了一种简单直接的方式来创建原生移动应用,无需任何繁琐操作或陡峭的学习曲线。其简洁的 API 和精简的功能使其能够轻松融入您的项目。
配置您的 React.js 应用
让我们采用最简单的方法来启动 React 应用。我们将使用 npm 包管理器来创建一个新的 React 应用:
npx create-react-app my-app
为了将我们的项目转换为原生移动应用,需要对应用进行 导出 操作。
我们稍后再讨论这一点。首先,让我们了解如何将 Capacitor 集成到我们的 React 应用中。
将 Capacitor 集成到您的 React.js 应用中
初始设置步骤可能稍显繁琐,但之后,更新您的原生应用壳程序(wrapper)将变得非常简单,只需运行一条 sync 命令。
首先,我们将 Capacitor CLI 作为开发依赖进行安装,并在项目中完成配置。在设置过程中,直接按“enter”键接受默认的 name 和 bundle ID 即可。
接下来,我们将安装核心包以及 iOS 和 Android 平台所需的相关包。
最后,我们将添加平台,Capacitor 会在项目根目录下为每个平台创建文件夹:
# Install the Capacitor CLI locally
npm install -D @capacitor/cli
# Initialize Capacitor in your React 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 现在你的 React.js 项目中已经存在这些目录了。
若要稍后访问 Android 项目,请安装 Android Studio 中的错误日志. 对于 iOS,你需要一台 Mac,并安装 Xcode 的 Mac.
接下来,更新 webDir 在你的 capacitor.config.json 文件中,如下所示:
{
"appId": "com.example.app",
"appName": "my-app",
"webDir": "build",
"bundledWebRuntime": false
}
运行 build 命令并使用 Capacitor 同步你的项目:
npm run build
npx cap sync
本指南中介绍的 npm run build 命令将构建你的 React.js 项目,同时 npx cap sync 会将 Web code 对齐到原生平台的准确位置,以便在 App 中运行。
现在,只要运气够好且没有报错,你的 React.js App 就可以准备在设备上发布了!
构建与部署你的 Native Apps
开发 iOS App 需要 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,等待所有内容加载完毕,然后将应用部署到已连接的设备即可。
对于 Xcode,请配置好签名账号,以便将应用部署到真机而非仅在模拟器上运行。完成后,只需点击运行按钮,即可在顶部选择的已连接设备上运行应用。
如果一切顺利,你就已经成功将 React.js Web 应用转换成了原生移动应用!
Capacitor 热重载
现代开发框架通常都自带热重载(hot reload),幸运的是,通过 Capacitor 你也能实现同样的效果,但 是在你的移动设备上!
你可以通过让 Capacitor 应用从特定 URL 加载内容,从而在局域网内通过 live reload 访问你本地托管的应用。
首先,确定你的本地 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: 'build',
bundledWebRuntime: false,
server: {
url: 'http://192.168.x.xx:3000',
cleartext: true
}
};
export default config;
请务必使用正确的 IP 和端口。运行 npx cap copy 将这些更改应用到我们的原生项目中。
再次通过 Android Studio 或 Xcode 部署应用后,React 应用中的任何改动都会自动重新加载并显示在你的应用中!
请记住,如果安装了新插件(例如相机插件),就需要重新构建原生项目。这是因为原生文件已发生变化,无法热更新。
使用 Capacitor 插件
让我们快速看看如何使用 Capacitor 插件。我们来安装一个简单的插件,即 分享插件,会弹出原生分享对话框:
npm i @capacitor/share
要使用它,请导入该 package,并在我们的 app 中调用相应的 share() 函数。请参考 App.js:
import { Share } from '@capacitor/share';
function ShareButton() {
const share = async () => {
await Share.share({
title: 'React App',
text: 'Visit this React App',
url: 'http://localhost:3000',
dialogTitle: 'Share with...'
});
};
return (
<button onClick={share}>
Share
</button>
);
}
export default ShareButton;
安装新 plugin 后,记得用以下命令再次同步你的 React 项目: npx cap sync.
接下来,你可以通过 Capgo 导航和过渡效果,让应用在 iOS 和 Android 上更具原生感,并修复会导致水平溢出或安全区域被裁剪的常见 iOS 布局问题。
使用 Capgo 原生导航与过渡效果,打造原生感 UI
我多年来一直在使用 Ionic 用于构建跨平台应用,但把它和 React 集成很 hacky,通常不值得,尤其是当你已经有了 Tailwind CSS.
若想在 React + Capacitor 应用中获得原生移动体验,请使用 Capgo plugins,而不是像 Konsta UI 这样的纯 Web UI kit:
- @capgo/capacitor-native-navigation — 原生导航栏、iOS 上的 Liquid Glass 标签栏,以及 Android 上的毛玻璃标签栏样式。你的 React 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 背景):
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 }) => {
navigate(`/${id}`);
});
在应用外壳中添加原生页面转场效果:
import { useEffect, useRef } from 'react';
import { useNavigate } from 'react-router-dom';
import '@capgo/capacitor-transitions';
import { initTransitions, setDirection, setupRouterOutlet } from '@capgo/capacitor-transitions/react';
initTransitions({ platform: 'auto' });
export function AppShell() {
const navigate = useNavigate();
const outletRef = useRef<HTMLElement>(null);
useEffect(() => {
if (outletRef.current) {
setupRouterOutlet(outletRef.current, { platform: 'auto', swipeGesture: 'auto' });
}
}, []);
const openSettings = () => {
setDirection('forward');
navigate('/settings');
};
return <cap-router-outlet ref={outletRef}>{/* routes */}</cap-router-outlet>;
}
将路由页面包裹在 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/index.css:
@import 'tailwindcss';
@plugin "@capgo/tailwind-capacitor/platform";
@plugin "@capgo/tailwind-capacitor/safe-areas";
使用以下工具类,例如 pt-safe, pb-safe,以及 px-safe 而不是零散地 env(safe-area-inset-*) 手动完成。项目正在积极开发中 —— 如果你的 React 环境缺少某些东西, 请在 GitHub 上提交 PR.
修复 iOS 布局问题(视口、安全区域与水平溢出)
如果 iOS 上的内容看起来被裁剪、偏移或可水平滚动,仅靠添加更多 overflow-x: hidden 或单独调整 viewport 标签通常无法解决问题。请按顺序进行以下检查。
确保 viewport meta 标签已正确应用
在……中添加 viewport meta 标签 index.html 内部 <head>:
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
只用一个根容器处理 iOS 安全区域
创建一个统一的 app shell,并在其中应用 safe area 内边距,而不是在多个嵌套组件中:
html,
body,
#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: 'build',
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 项目构建原生 App 的无缝方案,让共享 code 并保持 UI 一致变得非常简单。
得益于 Capacitor 等技术,从 React.js Web 应用构建移动端 App 变得前所未有的简单。通过打造出色的原生移动 App,让你的 Web 开发技能更进一步。祝你 coding 愉快!
想要了解更多关于如何加速 App 开发流程的信息, 注册免费账号 今天就开始。
继续阅读:使用纯 React.js 与 Capacitor 构建移动 App
如果你正在使用 使用纯 React.js 和 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. 时的原生能力