Electron中如何用MemoryRouter打开指定路由的新窗口?
我正在用Electron-React-Boilerplate开发应用,是Electron和React新手。模板用ReactRouter处理路由,想实现点击按钮打开新窗口展示指定路由(比如/settings的Settings页面)。
我的实现尝试
先添加了路由配置:
<MemoryRouter> <Routes> <Route path="/" element={<HomePage />} /> <Route path="/settings" element={<SettingsPage />} /> </Routes> </MemoryRouter>
然后试了几种方法:
Electron window.open()
调用代码:
<button onClick={function () { const childWindow = window.open('/settings', 'modal') childWindow.electron = window.electron; }}> SETTINGS </button>
新窗口能打开,但跳转到HomePage而非SettingsPage,控制台显示webpack-dev-server解析不了/settings,重定向到/index.html。
React Router Link组件
import { Link } from 'react-router-dom'; ... <Link to="/settings">SETTINGS</Link>
当前窗口能正常跳转,但加target="_blank"后新窗口还是显示HomePage,而且新窗口访问不到preload.ts暴露的window.electron。
a标签
用<a href="/settings">,无论加不加target="_blank"都完全无效。
补充尝试
把<MemoryRouter>换成<BrowserRouter>,开发环境里<Link to="/settings" target="_blank">能打开正确路由,但还是没有window.electron;打包后路由完全失效,连HomePage都加载不了,查了下BrowserRouter在Electron的文件环境里没法正常工作。
想知道这是模板路由处理的问题还是我代码错了?怎么用MemoryRouter实现新窗口打开指定路由?
解决方案
核心思路
MemoryRouter是内存路由,不会和浏览器URL绑定,新窗口是独立的上下文,默认会加载根页面(index.html),所以直接传路由路径没用。得给新窗口传递路由标识,在新窗口的React应用里根据这个标识初始化MemoryRouter的初始路由。
具体步骤
主窗口按钮逻辑:
打开新窗口时,通过查询参数传递目标路由,比如:<button onClick={() => { // 打开新窗口,把目标路由放在查询参数里 const childWindow = window.open(`/?route=/settings`, 'SettingsWindow'); // 同步electron对象(可选,看你需求) if (childWindow) { childWindow.electron = window.electron; } }}> 打开设置页面 </button>修改新窗口的入口渲染逻辑:
在应用入口文件(比如src/renderer/App.tsx),读取URL里的查询参数,作为MemoryRouter的初始路由:import { MemoryRouter, Routes, Route } from 'react-router-dom'; import HomePage from './pages/HomePage'; import SettingsPage from './pages/SettingsPage'; // 获取查询参数里的路由 const searchParams = new URLSearchParams(window.location.search); const initialRoute = searchParams.get('route') || '/'; function App() { return ( <MemoryRouter initialEntries={[initialRoute]}> <Routes> <Route path="/" element={<HomePage />} /> <Route path="/settings" element={<SettingsPage />} /> </Routes> </MemoryRouter> ); } export default App;解决window.electron的问题:
直接赋值childWindow.electron在Electron里可能受上下文隔离限制,更稳妥的方式是确保新窗口也加载preload脚本。在主进程的窗口配置里,处理window.open的新窗口时指定preload:// src/main/main.ts 里的窗口创建代码 import { BrowserWindow } from 'electron'; import path from 'path'; function createWindow() { const mainWindow = new BrowserWindow({ webPreferences: { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, nodeIntegration: false } }); // 处理新窗口的配置 mainWindow.webContents.setWindowOpenHandler((details) => { return { action: 'allow', overrideBrowserWindowOptions: { webPreferences: { preload: path.join(__dirname, 'preload.js'), // 新窗口复用preload contextIsolation: true, nodeIntegration: false } } }; }); }
为什么之前的方法不行?
- window.open('/settings'):MemoryRouter不依赖浏览器URL,新窗口加载index.html时路由默认是/,所以显示HomePage;webpack-dev-server找不到/settings静态资源,因此重定向到index.html。
- Link target="_blank":新窗口加载的仍是根页面,路由初始为/,且新窗口上下文独立,默认无法获取window.electron,除非preload脚本配置正确。
- BrowserRouter:依赖浏览器history API,Electron打包后使用本地文件协议(file://),BrowserRouter在该协议下无法正确处理路由,导致打包后失效。
内容的提问来源于stack exchange,提问作者お休みなさい

