尝试使用electron将React生成的静态网页打包成桌面应用。

0. 项目结构

静态网页资源dist应先移动到application项目中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.
├── application
│ ├── package.json
│ └── main.js
├── dist
| └── index.html
└── web
├── package.json
├── vite.config.js
├── src
├── App.jsx
└── components
└── Navigation.jsx

1. 项目初始化与安装

参考:教程

但是在安装时出现问题,是网络的原因。运行以下命令后安装,解决:

1
npm config set registry https://registry.npmmirror.com; $env:ELECTRON_MIRROR = "https://npmmirror.com/mirrors/electron/"

后面运行命令出现网络问题也如此。

2. 打包

web项目使用npm run build后生成的静态网页进行打包。

但需要注意:

  1. Vite 的构建产物默认使用以 “/” 开头的资源路径(如 /assets/…),在 Electron 以 file:// 加载本地文件时,这种绝对路径会解析到磁盘根目录。因此应该改为:
1
2
3
4
5
6
7
8
9
// vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
base: './',
})
  1. application项目中,文件加载路径的写法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const { app, BrowserWindow } = require('electron/main')
const path = require('path')

const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600
})

win.loadFile(path.join(__dirname, '..', 'dist', 'index.html'));
}

app.whenReady().then(() => {
createWindow()

app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
})

app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})

但即使是这样,路由也会出现问题。发现是由于src源文件中React Router没有配置好。

更新后的src/App.jsx:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { BrowserRouter, HashRouter, Routes, Route } from 'react-router-dom'
import { Container } from 'react-bootstrap'
import Navigation from './components/Navigation'
import Home from './pages/Home'
import Books from './pages/Books'
import Borrowings from './pages/Borrowings'
import 'bootstrap/dist/css/bootstrap.min.css'
import './App.css'

const RouterComponent = window.location.protocol === 'file:' ? HashRouter : BrowserRouter

function App() {
return (
<RouterComponent>
<Navigation />
<Container className="mt-3">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/books" element={<Books />} />
<Route path="/borrowings" element={<Borrowings />} />
</Routes>
</Container>
</RouterComponent>
)
}

export default App

同时在Navigation组件中,注意LinkContainer 包裹品牌区,避免 file:// 下 href=”/“ 的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import { Navbar, Nav, Container } from 'react-bootstrap'
import { LinkContainer } from 'react-router-bootstrap'

function Navigation() {
return (
<Navbar bg="dark" variant="dark" expand="lg">
<Container>
<LinkContainer to="/">
<Navbar.Brand>图书管理系统</Navbar.Brand>
</LinkContainer>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="me-auto">
<LinkContainer to="/">
<Nav.Link>首页</Nav.Link>
</LinkContainer>
<LinkContainer to="/books">
<Nav.Link>图书管理</Nav.Link>
</LinkContainer>
<LinkContainer to="/borrowings">
<Nav.Link>借阅管理</Nav.Link>
</LinkContainer>
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
)
}

export default Navigation

再重新npm run build生成静态网页, 并将dist目录移动到application项目中。