目标:把同一种能力(一个带本地存储的桌面/移动应用)用三种框架各落地一遍,从装环境到产出可分发的安装包。 技术栈:TypeScript + React。 覆盖平台:Windows / macOS / Linux / iOS / Android。 文档写法:每节都能照抄命令跑通,遇到坑会告诉你为什么。
0. 三者怎么选(先看完再动手)
| 维度 | Electron | Tauri (v2) | React Native |
|---|---|---|---|
| 渲染层 | 内置 Chromium | 系统 WebView(Win=WebView2 / mac=WKWebView / Linux=WebKitGTK) | 原生组件(无 WebView) |
| 后端语言 | Node.js | Rust | JS + 原生(Obj-C/Java/Kotlin) |
| 安装包大小 | 80--150 MB 起 | 3--10 MB | 20--40 MB(移动) |
| 内存占用 | 高 | 低 | 中 |
| 桌面支持 | ✅ 三平台 | ✅ 三平台 | ⚠ 通过 RN for Windows/macOS,能用但生态弱 |
| 移动支持 | ❌ | ✅ v2 起支持 iOS/Android(仍年轻) | ✅ 一等公民 |
| 上手难度 | 最低(纯 JS) | 中(要懂一点 Rust) | 中(要懂原生工具链) |
| 适合场景 | 复杂桌面工具/IDE 类 | 体积敏感的桌面工具 | 移动 App 为主,桌面顺带 |
一句话建议:
- 桌面为主、团队只会 JS → Electron。
- 桌面为主、追求小体积和性能 → Tauri。
- 移动为主 → React Native;想顺手出桌面再考虑 RN-Windows/macOS,但别指望和 Electron/Tauri 一样顺。
第一部分:Electron 实操
1.1 环境准备(Windows / macOS / Linux)
所有平台都需要:
- Node.js ≥ 20 LTS(推荐用 fnm 或 nvm 管理)
- Git
- 一个包管理器:pnpm(推荐)或 npm
Windows 额外:
- 安装 Visual Studio Build Tools 2022,勾选 "使用 C++ 的桌面开发"。原生模块编译要用。
- 一个 PowerShell 或 Git Bash 都行(你当前是 bash,OK)。
macOS 额外:
xcode-select --install(装命令行工具)。- 真要打 .dmg + 公证的话需要 Apple Developer 账号($99/年),见 1.6。
Linux (Ubuntu/Debian) 额外:
bash
sudo apt update
sudo apt install -y build-essential libnss3 libatk-bridge2.0-0 libgtk-3-0 libgbm1 libasound2
验证:
bash
node -v # v20.x+
pnpm -v # 8.x+
1.2 创建项目(Electron + Vite + React + TS)
我们用社区主流脚手架 electron-vite(不是 electron-forge,因为它和 Vite 集成更自然)。
bash
pnpm create @quick-start/electron my-electron-app
# 选项:
# - Framework: React
# - Add TypeScript? Yes
# - Add ESLint? Yes(可选)
生成的关键目录:
perl
my-electron-app/
├── src/
│ ├── main/ # 主进程(Node 环境,能访问文件/系统)
│ ├── preload/ # 预加载脚本(桥接主进程与渲染进程)
│ └── renderer/ # 渲染进程(React UI,浏览器环境)
├── electron.vite.config.ts
├── electron-builder.yml # 打包配置
└── package.json
启动开发:
bash
cd my-electron-app
pnpm install
pnpm dev
应该会弹出一个窗口,热更新已经接好。
1.3 核心概念:三进程模型
主进程 (main) :Node 环境,全局只有一个,负责创建窗口、文件 IO、系统 API。 渲染进程 (renderer) :每个窗口一个,运行 React UI,不能直接访问 Node 。 预加载脚本 (preload):在渲染进程加载前注入,是渲染进程和主进程之间唯一的安全桥梁。
禁忌 :不要在渲染进程开启 nodeIntegration: true。这是 Electron 历史上最大的安全坑,电子签名插件、XSS 都可能直接接管系统。
1.4 实操:渲染层调用主进程读取本地文件
src/main/index.ts(节选):
ts
import { app, BrowserWindow, ipcMain } from 'electron'
import { readFile } from 'node:fs/promises'
import path from 'node:path'
ipcMain.handle('app:readFile', async (_evt, filePath: string) => {
// 永远要校验路径!否则就是任意文件读取漏洞
const safe = path.resolve(app.getPath('userData'), filePath)
if (!safe.startsWith(app.getPath('userData'))) {
throw new Error('illegal path')
}
return await readFile(safe, 'utf-8')
})
src/preload/index.ts:
ts
import { contextBridge, ipcRenderer } from 'electron'
contextBridge.exposeInMainWorld('api', {
readFile: (p: string) => ipcRenderer.invoke('app:readFile', p),
})
src/renderer/src/App.tsx:
tsx
declare global {
interface Window {
api: { readFile: (p: string) => Promise<string> }
}
}
export default function App() {
const onClick = async () => {
const txt = await window.api.readFile('config.json')
console.log(txt)
}
return <button onClick={onClick}>Read</button>
}
记住一个心智模型 :preload 是白名单。你想让 React 用什么能力,就在 preload 里挑出来 exposeInMainWorld,别全暴露。
1.5 打包(electron-builder)
electron.vite.config.ts 那套脚手架已经接好了 electron-builder。配置在 electron-builder.yml:
yaml
appId: com.yourcompany.myapp
productName: MyApp
directories:
output: dist
files:
- 'out/**/*'
- 'resources/**/*'
win:
target:
- target: nsis
arch: [x64, arm64]
icon: resources/icon.ico
mac:
target:
- target: dmg
arch: [x64, arm64]
category: public.app-category.productivity
icon: resources/icon.icns
linux:
target:
- target: AppImage
- target: deb
category: Utility
icon: resources/icon.png
打包命令(注意:跨平台打包很难,强烈建议在目标平台打包):
bash
# 本机平台
pnpm build # 只编译
pnpm build:win # 出 .exe (NSIS 安装包)
pnpm build:mac # 出 .dmg
pnpm build:linux # 出 .AppImage + .deb
产物在 dist/ 下:
- Windows:
MyApp-Setup-1.0.0.exe(大概 100 MB) - macOS:
MyApp-1.0.0.dmg - Linux:
MyApp-1.0.0.AppImage
1.6 常见坑
-
首次运行
pnpm dev卡在下载 Electron :electron 二进制从 GitHub 下载,国内慢。设置镜像:bash# 项目根目录新建 .npmrc echo "ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/" >> .npmrc -
打 Windows 包时报
signtool错误 :你没配代码签名证书。开发期可以暂时在electron-builder.yml加:yamlwin: sign: null但未签名的 exe,用户运行会被 SmartScreen 拦截。正式分发要买代码签名证书(EV 证书约 $200/年)。
-
macOS 打的 dmg 别人下载后打不开,说"已损坏" :你需要 Apple Developer 账号做公证(notarization)。开发自用的话,让用户跑
xattr -cr /Applications/MyApp.app可绕过。 -
打包后白屏 :99% 是渲染进程加载路径不对。检查
BrowserWindow.loadFile()路径,生产环境用__dirname而非相对路径。
第二部分:Tauri v2 实操
2.1 环境准备
所有平台都需要 Rust 工具链:
bash
# Linux / macOS
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Windows
# 去 https://rustup.rs 下 rustup-init.exe 跑一遍
然后 rustc --version 验证。
Windows 额外:
- Visual Studio Build Tools 2022(同 Electron)。
- WebView2 Runtime :Win11 自带,Win10 需要装 Edge WebView2 Runtime。
macOS 额外:
xcode-select --install。
Linux (Ubuntu/Debian) 额外(依赖很多,一次装齐):
bash
sudo apt update
sudo apt install -y libwebkit2gtk-4.1-dev build-essential curl wget file \
libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev
移动端额外(Tauri v2 新增):
- iOS:macOS + Xcode +
rustup target add aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios - Android:Android Studio + NDK +
rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android,并设置ANDROID_HOME和NDK_HOME环境变量。
2.2 创建项目
bash
pnpm create tauri-app
# 选项:
# - Project name: my-tauri-app
# - Identifier: com.yourcompany.mytauriapp
# - Frontend language: TypeScript / JavaScript
# - UI template: React
# - UI flavor: TypeScript
目录结构:
bash
my-tauri-app/
├── src/ # React 前端
├── src-tauri/ # Rust 后端
│ ├── src/main.rs # 入口
│ ├── tauri.conf.json # 应用配置
│ └── Cargo.toml
└── package.json
启动:
bash
cd my-tauri-app
pnpm install
pnpm tauri dev
第一次会编译 Rust 依赖,慢,五到十分钟正常。后面增量编译就快了。
2.3 核心概念:Command + 权限
Tauri 没有 Electron 那种主/渲染进程的拆分,前后端通信通过 Command (Rust 函数)和 Event。
src-tauri/src/main.rs:
rust
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
前端调用:
tsx
import { invoke } from '@tauri-apps/api/core'
const msg = await invoke<string>('greet', { name: 'World' })
权限系统(v2 重点) :Tauri 2 引入 capability + permission,前端默认什么 API 都不能用。在 src-tauri/capabilities/default.json 里声明:
json
{
"identifier": "default",
"windows": ["main"],
"permissions": [
"core:default",
"fs:allow-read-text-file",
"dialog:allow-open"
]
}
然后前端才能 import { readTextFile } from '@tauri-apps/plugin-fs' 调用。
2.4 实操:读取本地文件
装插件:
bash
pnpm tauri add fs
pnpm tauri add dialog
src-tauri/capabilities/default.json 加上 fs:allow-read-text-file 和 dialog:allow-open。
React:
tsx
import { open } from '@tauri-apps/plugin-dialog'
import { readTextFile } from '@tauri-apps/plugin-fs'
async function pickAndRead() {
const path = await open({ multiple: false })
if (typeof path === 'string') {
const content = await readTextFile(path)
console.log(content)
}
}
2.5 桌面打包
src-tauri/tauri.conf.json 里 bundle 段控制产物:
json
{
"bundle": {
"active": true,
"targets": "all",
"icon": ["icons/icon.ico", "icons/icon.icns", "icons/icon.png"]
}
}
打包:
bash
pnpm tauri build
产物在 src-tauri/target/release/bundle/ 下:
- Windows:
msi/MyApp_1.0.0_x64_en-US.msi和nsis/MyApp_1.0.0_x64-setup.exe(只有 3-8 MB) - macOS:
dmg/MyApp_1.0.0_aarch64.dmg - Linux:
appimage/my-tauri-app_1.0.0_amd64.AppImage和deb/
跨架构(如 Apple Silicon 上打 Intel):
bash
rustup target add x86_64-apple-darwin
pnpm tauri build --target x86_64-apple-darwin
2.6 移动端打包(Tauri v2)
bash
# 初始化(一次)
pnpm tauri ios init
pnpm tauri android init
# 开发
pnpm tauri ios dev
pnpm tauri android dev
# 打包
pnpm tauri ios build
pnpm tauri android build
Android 输出 .apk / .aab,iOS 输出 .ipa(需要 Apple 开发者账号)。
实话:Tauri 移动端 2025 年还在快速演进,遇到怪问题去 GitHub Issue 看,比看老博客强。
2.7 常见坑
pnpm tauri devRust 编译慢得离谱 :第一次很正常。可以装sccache缓存:cargo install sccache然后export RUSTC_WRAPPER=sccache。- Linux 打的包在另一个发行版跑不起来:WebKitGTK 版本差异。建议用 AppImage,或针对每个发行版分别打。
- Windows 上 WebView2 没装的用户打开就闪退 :在
tauri.conf.json设"webviewInstallMode": {"type": "downloadBootstrapper"},安装器会自动下载 WebView2。 invoke类型推断丢失 :手动写泛型invoke<ReturnType>('cmd', args),并用@tauri-apps/api/core而非旧版@tauri-apps/api/tauri。
第三部分:React Native 实操
3.1 环境准备
RN 的环境是三者里最重的,严格按 官方 Environment Setup 走一遍,不要跳步。
所有平台都需要:
- Node.js ≥ 20
- 一个 JDK 17(推荐 Temurin / Zulu)
- Watchman(macOS/Linux 推荐,Windows 可省)
iOS 开发(仅 macOS):
- Xcode 15+(从 App Store 装)
- CocoaPods:
sudo gem install cocoapods或brew install cocoapods
Android 开发(三平台都行):
-
装 SDK Platform 34、SDK Build-Tools 34、Android Emulator、Platform-Tools
-
环境变量:
bash# macOS/Linux: ~/.zshrc 或 ~/.bashrc export ANDROID_HOME=$HOME/Library/Android/sdk # mac # export ANDROID_HOME=$HOME/Android/Sdk # linux # Windows (Git Bash) ~/.bashrc: # export ANDROID_HOME="/c/Users/你的用户名/AppData/Local/Android/Sdk" export PATH=$PATH:$ANDROID_HOME/emulator export PATH=$PATH:$ANDROID_HOME/platform-tools
验证:
bash
adb --version
java -version # 17.x
3.2 创建项目(用 Expo,强烈推荐)
2025 年的 RN 主流路径是 Expo。它解决了原生工具链 90% 的痛点,并且现在也支持 EAS Build 云端打包(不用本地装一堆东西)。
bash
pnpm create expo-app my-rn-app --template
# 选 "Blank (TypeScript)"
cd my-rn-app
启动:
bash
pnpm start
# 然后在打开的终端按 i (iOS 模拟器) 或 a (Android 模拟器)
# 也可以扫码用 Expo Go 在真机上跑(仅开发预览)
不用 Expo 的话:
npx @react-native-community/cli init MyApp --template react-native-template-typescript。功能更裸但坑更多,本文不展开。
3.3 核心概念
- RN 没有 WebView,UI 是 React 描述、由 Bridge(旧)或 JSI/Hermes/New Architecture(新)翻译成原生组件。
- 默认
<div>不存在,用<View>;<span>用<Text>。 - 样式不是 CSS,是
StyleSheet,子集且 camelCase。 - 路由用
expo-router(文件系统路由,类似 Next.js)或react-navigation。
app/index.tsx(expo-router 默认结构):
tsx
import { View, Text, Button, Alert } from 'react-native'
import * as FileSystem from 'expo-file-system'
export default function Index() {
const onPress = async () => {
const path = FileSystem.documentDirectory + 'hello.txt'
await FileSystem.writeAsStringAsync(path, 'hi from RN')
const txt = await FileSystem.readAsStringAsync(path)
Alert.alert('Read', txt)
}
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Hello RN</Text>
<Button title="Write + Read" onPress={onPress} />
</View>
)
}
装依赖:
bash
pnpm expo install expo-file-system
(用 expo install 而非 pnpm add,它会装匹配当前 Expo SDK 版本的版本)
3.4 打包:移动端(Android / iOS)
关键概念 :Expo Go 只是开发预览,真正发布要做"原生构建" (native build) ,产物是 .apk / .aab / .ipa。
路线 A:用 EAS Build(云端,最省心)
bash
pnpm add -g eas-cli
eas login # 用 Expo 账号
eas build:configure # 生成 eas.json
eas build -p android --profile preview # 出 APK,扫码可装
eas build -p ios --profile preview # 需要 Apple Dev 账号
eas build -p android --profile production # 出 AAB,传 Google Play
eas build -p ios --profile production
eas.json 关键片段:
json
{
"build": {
"preview": {
"android": { "buildType": "apk" },
"ios": { "simulator": false }
},
"production": {}
}
}
构建完会给你下载链接。免费额度每月 30 次构建,够个人项目用。
路线 B:本地打包
先生成原生工程:
bash
pnpm expo prebuild # 生成 ios/ 和 android/ 目录
Android:
bash
cd android
./gradlew assembleRelease # 出 apk,在 android/app/build/outputs/apk/release/
./gradlew bundleRelease # 出 aab
签名:编辑 android/app/build.gradle 的 signingConfigs,用你的 keystore:
bash
keytool -genkeypair -v -keystore my-key.keystore -alias my-alias \
-keyalg RSA -keysize 2048 -validity 10000
iOS(仅 macOS):
bash
cd ios && pod install && cd ..
open ios/MyRnApp.xcworkspace
# 在 Xcode 里:Product → Archive → Distribute App
3.5 打包:桌面端(RN for Windows / macOS)
这是个额外路线,由微软维护。Expo 不直接支持,要用原生 RN CLI。
bash
# 在已有 RN 项目里
npx react-native-windows-init --overwrite # Windows
npx react-native-macos-init # macOS
然后:
bash
npx react-native run-windows
npx react-native run-macos
打包:
- Windows:用 Visual Studio 打开
windows/MyApp.sln,Build → Publish → 选 MSIX 或 sideload。 - macOS:Xcode 打开
macos/MyApp.xcworkspace,Archive → Distribute。
坦白说:RN 桌面生态弱,组件库覆盖不全,遇到问题资料少。如果桌面是主要目标,用 Electron 或 Tauri。
3.6 常见坑
-
Metro bundler 端口 8081 被占 :
npx react-native start --port 8082。 -
Android 构建报 SDK 找不到 :检查
local.properties里sdk.dir,或环境变量ANDROID_HOME。 -
iOS
pod install卡住 :CocoaPods 走 GitHub,国内慢。换镜像:bashcd ios bundle exec pod install --repo-update或用 cocoapods 镜像。
-
Expo SDK 升级后红屏 :清缓存
pnpm expo start --clear,并删node_modules+ios/Pods重装。 -
真机预览扫码进不去:手机和电脑要在同一 WiFi;公司网络常会拦,开个手机热点试。
第四部分:横向对比表(实操层面)
| 操作 | Electron | Tauri | RN (Expo) |
|---|---|---|---|
| 项目创建 | pnpm create @quick-start/electron |
pnpm create tauri-app |
pnpm create expo-app |
| 开发启动 | pnpm dev |
pnpm tauri dev |
pnpm start |
| 调用原生能力 | preload + ipcMain.handle | #[tauri::command] + invoke |
expo-* 模块 或自写 Native Module |
| 权限模型 | 自管(contextIsolation) | capability 白名单 | 系统权限弹窗 |
| 桌面打包 | electron-builder | pnpm tauri build |
RN-Windows/macOS(额外配置) |
| 移动打包 | ❌ | pnpm tauri ios/android build |
eas build 或 gradle/xcode |
| 安装包大小 | 100 MB | 5 MB | 25 MB(Android) |
| 跨平台打包 | 同平台打最稳 | 同平台打最稳 | EAS Build 云端无平台限制 |
第五部分:建议的学习路径
如果你只能从一个开始:
- 桌面优先:先 Electron 跑通本文 1.2--1.5,感受三进程模型;再做 Tauri 同样的功能,对比体积和启动速度。
- 移动优先:直奔 Expo,用 EAS Build 出第一个 APK 跑在真机上,体验整个发布流程;之后再深入原生模块。
三个框架的官方文档质量都很高,遇到问题先去官方,再 Stack Overflow,最后才 Google 博客(很多 RN/Tauri 老博客的命令已经过期):
- Electron: www.electronjs.org/docs/latest...
- Tauri v2: v2.tauri.app/
- React Native: reactnative.dev/
- Expo: docs.expo.dev/
附:你下一步要做的事
- 选一个框架,按对应章节把"hello world + 读文件"跑通(包括成功打包出安装包)。
- 把同一个 demo 再用另一个框架做一遍,对比工作流和产物。
- 决定主力之后,去看官方的"安全"和"性能"章节------这是后续深入和真正区分新手/老手的地方。