跨端框架实操开发文档:Electron / Tauri / React Native

目标:把同一种能力(一个带本地存储的桌面/移动应用)用三种框架各落地一遍,从装环境到产出可分发的安装包。 技术栈: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 常见坑

  1. 首次运行 pnpm dev 卡在下载 Electron :electron 二进制从 GitHub 下载,国内慢。设置镜像:

    bash 复制代码
    # 项目根目录新建 .npmrc
    echo "ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/" >> .npmrc
  2. 打 Windows 包时报 signtool 错误 :你没配代码签名证书。开发期可以暂时在 electron-builder.yml 加:

    yaml 复制代码
    win:
      sign: null

    未签名的 exe,用户运行会被 SmartScreen 拦截。正式分发要买代码签名证书(EV 证书约 $200/年)。

  3. macOS 打的 dmg 别人下载后打不开,说"已损坏" :你需要 Apple Developer 账号做公证(notarization)。开发自用的话,让用户跑 xattr -cr /Applications/MyApp.app 可绕过。

  4. 打包后白屏 :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_HOMENDK_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-filedialog: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.jsonbundle 段控制产物:

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.msinsis/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.AppImagedeb/

跨架构(如 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 常见坑

  1. pnpm tauri dev Rust 编译慢得离谱 :第一次很正常。可以装 sccache 缓存:cargo install sccache 然后 export RUSTC_WRAPPER=sccache
  2. Linux 打的包在另一个发行版跑不起来:WebKitGTK 版本差异。建议用 AppImage,或针对每个发行版分别打。
  3. Windows 上 WebView2 没装的用户打开就闪退 :在 tauri.conf.json"webviewInstallMode": {"type": "downloadBootstrapper"},安装器会自动下载 WebView2。
  4. 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 cocoapodsbrew install cocoapods

Android 开发(三平台都行):

  • Android Studio

  • 装 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.gradlesigningConfigs,用你的 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 常见坑

  1. Metro bundler 端口 8081 被占npx react-native start --port 8082

  2. Android 构建报 SDK 找不到 :检查 local.propertiessdk.dir,或环境变量 ANDROID_HOME

  3. iOS pod install 卡住 :CocoaPods 走 GitHub,国内慢。换镜像:

    bash 复制代码
    cd ios
    bundle exec pod install --repo-update

    或用 cocoapods 镜像

  4. Expo SDK 升级后红屏 :清缓存 pnpm expo start --clear,并删 node_modules + ios/Pods 重装。

  5. 真机预览扫码进不去:手机和电脑要在同一 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 云端无平台限制

第五部分:建议的学习路径

如果你只能从一个开始:

  1. 桌面优先:先 Electron 跑通本文 1.2--1.5,感受三进程模型;再做 Tauri 同样的功能,对比体积和启动速度。
  2. 移动优先:直奔 Expo,用 EAS Build 出第一个 APK 跑在真机上,体验整个发布流程;之后再深入原生模块。

三个框架的官方文档质量都很高,遇到问题先去官方,再 Stack Overflow,最后才 Google 博客(很多 RN/Tauri 老博客的命令已经过期):


附:你下一步要做的事

  1. 选一个框架,按对应章节把"hello world + 读文件"跑通(包括成功打包出安装包)。
  2. 把同一个 demo 再用另一个框架做一遍,对比工作流和产物。
  3. 决定主力之后,去看官方的"安全"和"性能"章节------这是后续深入和真正区分新手/老手的地方。
相关推荐
掘金者阿豪2 分钟前
把业务数据变成共享仪表盘:Metabase可视化与远程访问实践
前端·后端
kyriewen22 分钟前
折腾了半年 AI 编程工作流,最后发现效率瓶颈是桌上那块屏幕
前端·javascript·ai编程
蜗牛前端1 小时前
codex 全流程开发上线的高颜值礼簿小程序
前端·微信小程序
大龄秃头程序员1 小时前
我在图文流 App 里落地双层缓存、弱网降级与 OOM 治理
前端
老王以为2 小时前
React Renderer 分离的多平台架构
前端·react native·react.js
hunterandroid2 小时前
Kotlin Coroutines 与 Flow:让异步任务更清晰
前端
Bigger2 小时前
从零搭建 AI 代码审查服务:一份前端也能看懂的 Python 学习笔记
前端·ci/cd·ai编程
lichenyang4532 小时前
JSAPI、NAPI、Biz、Imp:ASCF Demo 如何真正调用系统能力和 C++ 能力
前端
lichenyang4533 小时前
IPC、JSVM、UIThread、libuv:ASCF 架构图里最容易混的几个词
前端
用户059540174463 小时前
Redis记忆存储故障恢复测试踩坑实录:手动测试让我漏掉了2个一致性Bug
前端·css