摸鱼无聊怎么办,怼着代码死里干!
为了接触更多的新技术,拓展自己的技术能力,本文给大家带来我第一次使用electron的技术文章,记录了对electron的一些基本的认识,主要介绍了electron的一些基本的架构和api的使用,由于对react的熟悉度还是比较低,所以这次选择的脚手架是electron-react
Electron自带的Notifytion通知
定义普通消息通知看看效果
js
const notification = new Notification({
title: '测试',
body: '测试内容',
});
notification.show();
这个内容比较简单,而且根据官方提供的api也无法对弹窗做更大的修改,所以准备通过用新建窗口的方法来制作一个弹窗。
自定义弹窗结果展示
虽然效果很简单,但是途中踩了很多的坑,各位看官接着往下看
这个速度确实有点慢 后续看看如何优化
主进程加载窗口
准备该消息通知在桌面的右下角显示
使用screen.getPrimaryDisplay().workAreaSize获取用于获取主显示器(primary display)的工作区尺寸,即可用的工作区域的大小, x和y是偏移量
resolveHtmlPath
是electron-react提供的的一个路径函数,获取渲染进程文件夹的目录下的文件,也是打包后的renderer下的文件
js
//main
import { app, BrowserWindow, shell, ipcMain,screen } from 'electron';
const { width ,height} = screen.getPrimaryDisplay().workAreaSize
const digalogWidth = 400
const dialogHeight = 150
const dialogWindow = new BrowserWindow({
width:digalogWidth,
height:dialogHeight,
x:width-digalogWidth,
y:height-dialogHeight,
// frame: false, // 隐藏标题栏和缩放按钮
webPreferences: {
nodeIntegration: true,
// session: session.fromPartition('persist:main'),
devTools: false,
},
});
dialogWindow.loadURL(resolveHtmlPath('dialog.html'));
新建弹窗dialog目录
新建文件夹来存放需要打开的页面,这里称为窗口可能更加合适
主进程中监听渲染进程的事件
js
//mainjs
ipcMain.on('ipc-example', async (event, arg) => {
const msgTemplate = (pingPong: string) => `IPC test: ${pingPong}`;
event.reply('ipc-example', msgTemplate('pong'));
showNotification()
});
const showNotification = () => {
new BrowserWindow({ frame: false }).show()
//在主进程中监听消息弹窗的显示请求
const { width ,height} = screen.getPrimaryDisplay().workAreaSize
const digalogWidth = 400
const dialogHeight = 150
const dialogWindow = new BrowserWindow({
width:digalogWidth,
height:dialogHeight,
x:width-digalogWidth,
y:height-dialogHeight,
// autoHideMenuBar: true,
// frame: false, // 隐藏标题栏和缩放按钮
webPreferences: {
nodeIntegration: true,
session: session.fromPartition('persist:main'),
devTools: false,
},
});
dialogWindow.loadURL(resolveHtmlPath('dialog.html'));
}
渲染进程触发
js
//App.tsx
function Hello() {
const showLoading = ()=> {
window.electron.ipcRenderer.sendMessage('ipc-example', ['show']);
}
return (
<div>
<div className="Hello">
<img width="200" alt="icon" src={icon} />
</div>
<h1>electron-react-boilerplate</h1>
<div className="Hello">
<button type="button" onClick={showLoading}>
<span role="img" aria-label="books">
📚
</span>
Read our docs1
</button>
<button type="button">
<span role="img" aria-label="folded hands">
🙏
</span>
Donate
</button>
</div>
</div>
);
}
开始踩坑
加载dialog.html页面
主进程和渲染进程都已经好了,新增一个窗口,我们就需要放入一个html,但是根据官方提供的模版来看,是没有html的,所以到底是怎么生成的呢,一开始我是根本不知道的
我们可以看到其实是有一个html的文件的,但是在目录中只有index.ejs,这一步是在webpack中实现的
js
new HtmlWebpackPlugin({
filename: path.join('index.html'),
template: path.join(webpackPaths.srcRendererPath, 'index.ejs'),
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true,
},
isBrowser: false,
env: process.env.NODE_ENV,
isDevelopment: process.env.NODE_ENV !== 'production',
nodeModules: webpackPaths.appNodeModulesPath,
}),
webpack通过加载模版将结果输出到index.html中,所以要实现dialog.html的页面渲染到新窗口中,我们可以模仿官方给我们的写法。
js
new HtmlWebpackPlugin({
filename: path.join('dialog.html'),
template: path.join(webpackPaths.srcRendererPath, 'components/index.ejs'),
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true,
},
isBrowser: false,
env: process.env.NODE_ENV,
isDevelopment: process.env.NODE_ENV !== 'production',
nodeModules: webpackPaths.appNodeModulesPath,
}),
这一步就是把我们的dialog模板输出到和index.html同样的目录下。
filename: path.join('dialog.html')这一步要写好,一开始我写的是 component/dialog.html,想要生成的路径是在component下面,结果失败了。
注意template的路径,指向的是新窗口的模板。修改之后记得重启,否则可能会出现找不到dialog.html的情况 ,表现形式为在控制台会404
js
components/index.ejs
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta
http-equiv="Content-Security-Policy"
content="script-src 'self' 'unsafe-inline'"
/>
<title>下班通知</title>
</head>
<body>
<div id="dialog"></div>
</body>
</html>
加载index.js
这里的index.js是给dialog.html使用的
js
webPreferences: {
nodeIntegration: true,
devTools: true,
},
把新建窗口的devtools打开,查看一下控制台
这里又踩了一下坑,由于webpack打包的原因,这里我们新建窗口,用到的js都是同一个js,但是我们来看一下我们的根目录js是怎么写的
js
renderer/index.tsx
import { createRoot } from 'react-dom/client';
import App from './App';
import Dialog from './components/dialog';
const container = document.getElementById('root') as HTMLElement;
const root = createRoot(container);
root.render(<App />);
这里加载的是id=root这个节点,但是dialog.html中只有id=dialog这节点,所以就会发生报错
使用BrowserWindow打开新窗口的时候,都会默认加载同一个js
骚操作
既然你同一个js,那种得区分开来吧,这里是通过判断窗口的标题来执行不同的操作,虽然有点不正规,但是也是可以实现的,后续会通过切割分包的形式看看是否可行
js
import { createRoot } from 'react-dom/client';
import App from './App';
import Dialog from './components/dialog';
if(document.title=='Electron'){
const container = document.getElementById('root') as HTMLElement;
const root = createRoot(container);
root.render(<App />);
}else{
const container = document.getElementById('dialog') as HTMLElement;
const root = createRoot(container);
root.render(<Dialog />);
}
到这一步我们就已经完成了生成dialog.html文件,加载dialog.html文件,并且将react的内容挂载到dialog.html上面。虽然看几个步骤都比较简单,但是其中的排错由于是第一次上手耗费了比较多的时间,单单上面这几个坑都够我喝一壶的了,最终也是能简单的实现这个效果。