Electron碰到的问题
项目使用create-react-app搭建,本地开发时候,先跑起一个WebServer,再用electron加载
localhost:8000
页面,走的是http协议。打包后,因为访问的是本地文件,走的是file协议。因此会出现不少问题…
1. 渲染进程使用Node模块
Node. js 的所有 内置模块 都在Electron中可用, 第三方 node 模块中也完全支持。渲染进程除了额外能够使用node模块的能力外,与普通网页没有什么区别1
2
3
4
5
6
7
8<html>
<body>
<script>
const { app } = require('electron').remote
console.log(app.getVersion())
</script>
</body>
</html>
2. 请求 url 为绝对路径
1 | const host = |
3. 路由失效
一开始用的 React-router 的 <BrowserRouter>
打开后,页面一片空白。要使用<HashRouter>
,并设置hashType: 'noslash'
-> index.html#liveroom
。
1 | // Router.js |
electron 入口文件,打包后可以直接进入liveroom路由1
2
3
4
5
6
7
8
9
10
11
12
13let startUrl;
if (isDev) {
startUrl = 'http://localhost:8000#liveroom';
mainWindow.loadURL(startUrl);
} else {
startUrl = url.format({
pathname: path.join(__dirname, '/../build/index.html'),
protocol: 'file:',
slashes: false,
hash: 'liveroom'
});
mainWindow.loadURL(startUrl);
}
4. window.location不可用
当使用 api(如 webContents.loadURL 和 webContents.back) 来修改导航的时候,这个事件将不会发出,它也不会在页内发生跳转。
使用did-navigate-in-page
事件可以监听到 —— 点击锚链接或更新 window.location.hash。调用 event.preventDefault() 可以默认事件。
1 | // Page navigate |
在代码中统一使用修改 hash 值,进行路由跳转
1 | window.location.hash = 'liveroom'; |
5. 首次进入页面白屏
在加载页面时,渲染进程第一次完成绘制时,会发出 ready-to-show 事件 。 在此事件后显示窗口将没有视觉闪烁:
1 | const { BrowserWindow } = require('electron'); |
这个事件通常在 did-finish-load 事件之后发出,但是页面有许多远程资源时,它可能会在 did-finish-load 之前发出事件。
6. frameless窗口设置拖动区域
注意,mac 关闭了 devTool 拖动才能生效
1 | .div { |
7. 打开新窗口
需要全局定义一个win
变量来存储所有新窗口,不然关闭窗口会报错。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
31
32let win = {};
ipcMain.on('newwin', (event, value) => {
const { width, height, minWidth, minHeight, hash } = value;
win[hash] = new BrowserWindow({
width,
height,
minHeight,
minWidth,
fullscreenable: false,
titleBarStyle: 'hidden',
parent: mainWindow,
webPreferences: {
nodeIntegration: true //设置此处
}
});
let startUrl;
if (isDev) {
startUrl = `http://localhost:8000#${hash}`;
} else {
startUrl = url.format({
pathname: path.join(__dirname, '/../build/index.html'),
protocol: 'file:',
slashes: false,
hash
});
}
win[hash].loadURL(startUrl);
win[hash].on('closed', () => {
win[hash] = null;
});
});
渲染进程中打开新窗口1
2
3
4
5
6
7ipcRenderer.send('newwin', {
width,
height,
minWidth,
minHeight,
hash,
});
8. 多个窗口之间通信
这里记录一个需求:打开了一个新窗口,在新窗口点击一个按钮时,会自动关闭该窗口,并在主窗口显示一个小弹窗。1
2
3
4
5
6
7// electron入口文件
ipcMain.on('close-newwin', (event, value) => {
const { type } = JSON.parse(value);
// 监听窗口向主进程发送消息,收到消息后,向父窗口的渲染进程发消息
mainWindow.webContents.send('newmsg', value);
win[type].close();
});
9. 关闭窗口二次确认框
关闭窗口时,弹出自定义弹框。1
2
3
4
5
6
7
8
9
10
11
12
13
14// electron入口文件
// 阻止默认close事件,先渲染进程发送弹出确认框的消息
mainWindow.on('close', e => {
e.preventDefault();
const url = mainWindow.webContents.getURL();
mainWindow.webContents.send('confirm-close-app', url);
});
// 接收渲染进程的close-app消息,收到则关闭app
ipcMain.on('close-app', (event, value) => {
mainWindow = null;
app.exit();
});
渲染进程中,监听confirm-close-app1
2
3
4
5
6
7
8
9
10
11
12componentDidMount() {
const { ipcRenderer } = window.electron;
ipcRenderer.on('confirm-close-app', (event, arg) => {
if (flag) {
// 显示弹窗
// ...
} else {
// 关闭窗口
ipcRenderer.send('close-app', 'close');
}
});
}