跨端框架实操开发文档: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. 决定主力之后,去看官方的"安全"和"性能"章节------这是后续深入和真正区分新手/老手的地方。
相关推荐
ZC跨境爬虫1 小时前
跟着 MDN 学 HTML day_60:(表单与按钮技能测试实战)
服务器·前端·javascript·数据库·ui·html
lihaozecq1 小时前
做 Agent SDK 必须支持的插件能力:8 个钩子搞定横切关注点
前端·agent·ai编程
秦歌6661 小时前
Agent Skills详解
服务器·前端·数据库
ljt27249606611 小时前
Vue笔记(四)--组件基础
前端·vue.js·笔记
哈撒Ki1 小时前
快速入门WebSocket
前端·websocket
张元清1 小时前
React 里不用 setTimeout 的计时器写法:useTimeout、useInterval、useCountDown 和 useRafFn
前端·javascript·面试
李白的天不白1 小时前
HMR模块热替换
前端
2601_958492551 小时前
A Technical Log: Hosting Gravity Dunk - HTML5 Casual game
前端·html·html5
. . . . .1 小时前
css module
前端·css