做过 Electron + React 桌面端开发的兄弟,大概率都被路由兼容坑惨过:原生 react-router-dom 在开发环境跑得溜,打包生产直接失效;多窗口场景下路由互相污染,关窗还残留历史栈;开发/生产环境加载逻辑不一致,调试到头秃。
今天带来 electron-router-dom 完整版教程,基于官方入门文扩展,不仅保留基础上手流程,还把路由守卫、动态传参、嵌套路由、生产优化、底层逻辑一次性讲透,10年桌面端开发踩坑经验全塞进去,复制代码直接落地。
核心定位:react-router-dom 官方适配器,专为 Electron 多窗口、开发/生产双环境定制
核心解决:环境兼容、多窗口路由隔离、路由污染、生产失效四大痛点
一、先搞懂:为什么原生 react-router-dom 不适配 Electron?
很多人直接把网页路由搬到 Electron,踩坑了都不知道原因。底层逻辑很简单:
- 网页是单窗口、hash/history 路由模式,Electron 多窗口是独立渲染进程,路由状态无法隔离
- 开发环境用 localhost 服务,生产环境加载本地 HTML 文件,路由路径解析规则不一致
- 原生路由没有窗口 ID 绑定,多窗口共用一个路由栈,导致跳转混乱、内存泄漏
而 electron-router-dom 就是做了一层封装:通过窗口 ID 绑定路由,让每个窗口拥有独立路由栈,自动适配开发/生产环境的路径解析,完美兼容 react-router-dom 原有 API(useNavigate、useParams 等)。
二、完整安装流程(含依赖避坑)
该库依赖 react-router-dom,必须同步安装,别漏装导致启动报错:
# npm 安装
npm i electron-router-dom react-router-dom
# yarn 安装
yarn add electron-router-dom react-router-dom
# pnpm 安装(推荐)
pnpm add electron-router-dom react-router-dom
重点提醒:react-router-dom 必须是 v6 版本(v5 不兼容),当前主流项目都是 v6,直接安装即可。
三、主进程全配置(开发/生产双环境+多窗口)
主进程核心是创建窗口 + 绑定窗口 ID + 区分环境加载路由,这步是路由生效的关键,窗口 ID 必须和渲染进程严格对应,不能错!
import { app, BrowserWindow, BrowserWindowConstructorOptions } from 'electron'
import { createFileRoute, createURLRoute } from 'electron-router-dom'
import { join } from 'path'
// 封装创建窗口函数,id 为路由唯一标识
function createWindow(id: string, options: BrowserWindowConstructorOptions = {}) {
const window = new BrowserWindow({
width: 700,
height: 473,
...options,
// 推荐开启,避免白屏
show: false,
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
// 关闭跨域限制(桌面端常用)
webSecurity: false,
nodeIntegration: false,
contextIsolation: true
}
})
// 开发环境:加载本地服务路由
const devURL = createURLRoute(process.env.ELECTRON_RENDERER_URL!, id)
// 生产环境:加载本地 HTML 文件路由
const prodRoute = createFileRoute(join(__dirname, '../renderer/index.html'), id)
// 环境区分加载
if (process.env.NODE_ENV === 'development') {
window.loadURL(devURL)
} else {
window.loadFile(...prodRoute)
}
// 页面加载完毕再显示,避免闪烁
window.once('ready-to-show', () => window.show())
return window
}
// 应用就绪后创建窗口
app.whenReady().then(() => {
// 主窗口,id = main
createWindow('main')
// 关于窗口,id = about
createWindow('about', { width: 450, height: 350 })
})
// 关闭所有窗口退出(mac 除外)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
四、渲染进程路由配置(基础+嵌套+多窗口)
渲染进程通过 Router 组件按窗口 ID 配置路由,和主进程 ID 一一对应,支持嵌套路由、路由分组。
1. 路由配置文件(routes.tsx)
import { Router, Route } from 'electron-router-dom'
// 引入页面组件
import { MainScreen, SearchScreen, AboutScreen, UserDetailScreen } from './screens'
// 引入路由守卫组件
import { AuthGuard } from './guards/AuthGuard'
export function AppRoutes() {
return (
<Router
// 主窗口路由(id=main),支持嵌套/多路由
main={
<>
{/* 基础路由 */}
<Route path="/" element={<MainScreen />} />
{/* 带路由守卫的路由(需登录) */}
<Route path="/search" element={<AuthGuard><SearchScreen /></AuthGuard>} />
{/* 动态传参路由 */}
<Route path="/user/:id" element={<UserDetailScreen />} />
</>
}
// 关于窗口路由(id=about),独立路由栈
about={<Route path="/" element={<AboutScreen />} />}
/>
)
}
2. 入口文件挂载路由(index.tsx)
import React from 'react'
import ReactDOM from 'react-dom/client'
import { AppRoutes } from './routes'
const root = ReactDOM.createRoot(document.getElementById('root')!)
root.render(
<React.StrictMode>
<AppRoutes />
</React.StrictMode>
)
五、核心进阶用法(原文缺失,必看)
1. 路由跳转(useNavigate 用法)
和原生 react-router-dom 完全一致,直接复用原有写法,无需改逻辑:
import { useNavigate } from 'react-router-dom'
export function MainScreen() {
const navigate = useNavigate()
return (
<main style={{ padding: '20px' }}>
<h1>主窗口</h1>
{/* 普通跳转 */}
<button onClick={() => navigate('/search')}>跳转搜索页</button>
{/* 动态传参跳转 */}
<button onClick={() => navigate('/user/1001')}>查看用户详情</button>
{/* 返回上一页 */}
<button onClick={() => navigate(-1)}>返回</button>
</main>
)
}
2. 动态路由参数获取(useParams)
import { useParams } from 'react-router-dom'
export function UserDetailScreen() {
// 获取路由上的 id 参数
const { id } = useParams<{ id: string }>()
return (
<div>
<h2>用户详情页</h2>
<p>用户ID:{id}</p>
</div>
)
}
3. 路由守卫(权限控制,登录拦截)
封装高阶组件,实现未登录跳转登录页,桌面端权限控制必备:
// src/guards/AuthGuard.tsx
import { Navigate } from 'react-router-dom'
interface AuthGuardProps {
children: React.ReactNode
}
export function AuthGuard({ children }: AuthGuardProps) {
// 判断登录状态(可从 store/preload 读取)
const isLogin = localStorage.getItem('token') ? true : false
// 未登录跳转首页
if (!isLogin) return <Navigate to="/" replace />
// 已登录放行
return children
}
4. 多窗口通信 + 路由联动
通过 preload 暴露方法,主进程打开新窗口,渲染进程触发,路由自动隔离:
// 页面组件内调用
const { App } = window // preload 暴露的 API
<button onClick={() => App.openAboutWindow()}>打开关于窗口</button>
六、生产打包避坑(关键!)
- 打包前务必校验 窗口 ID 一致性,主进程和渲染进程必须完全匹配
- 生产环境关闭 devTools,路由文件路径别写错,避免加载失败
- 路由不要用绝对路径,统一用相对路径,防止跨域/文件找不到
- 多窗口关闭时,同步清理路由状态,避免内存泄漏
七、底层逻辑简析(看懂不踩坑)
electron-router-dom 本质是路由分发器:
- 主进程通过窗口 ID 标记路由,开发环境拼接 URL,生产环境拼接文件路径
- 渲染进程通过 ID 匹配对应路由组,每个窗口路由栈独立,互不干扰
- 内部兼容 react-router-dom v6 核心 API,上层写法无感知,底层做环境适配
八、总结
electron-router-dom 是 Electron + React 开发的路由神器,解决了原生路由最头疼的环境和多窗口问题。基础用法简单易上手,进阶用法(守卫、传参、嵌套)完全兼容 react-router-dom,上手成本极低。
建议大家把这份教程收藏,项目里直接复制配置,再也不用折腾路由兼容问题。如果碰到窗口白屏、路由失效,优先检查窗口 ID 是否一致、环境路径是否正确,90% 的坑都能解决。
点赞+收藏,Electron 开发少走一周弯路,需要完整项目模板的评论区留言~