写在前面
为什么要写个待办事项呢??? 之前有用过语雀的小记,功能很多,但很多都用不上,刚好正在学react
,于是就想做一个简约的待办事项
效果图
呜呜呜~~~
这篇文章写的有点烂,但这里只能是写一些开发中遇到的问题,只能怪小编的文笔太差了,这里附上github地址,点击这里
正文
用vite创建项目
yarn create vite todoSimple --template react
然后就可以开始写项目了,写的时候一开始可以在浏览器看效果,写的差不多了就可以介入electron
了
接入electron
先安装electron
yarn add electron -D
这里有个坑,为什么要把electron
放到devDependencies
呢,因为打包我用的是electron-builder
,打包的时候会提示报错,提示说electron
不应该放到dependencies
里面
下载完之后,我们来配置一下package.json
{
"name": "todosimple",
"version": "1.1.0",
"main": "./render/electron.js", // 这个是electron的入口文件
"scripts": {
"dev": "vite --host",
"build": "vite build",
"build:el": "vite build && electron-builder", // 这个是electron的打包命令
"serve": "vite preview",
"start": "cross-env NODE_ENV=development electron .", //这个是开发环境运行electron命令
"start:build": "cross-env NODE_ENV=production electron .",
"edev": "yarn dev | yarn start"
},
"dependencies": {
"@ant-design/icons": "^4.7.0",
"animate.css": "^4.1.1",
"antd": "^4.16.13",
"axios": "^0.24.0",
"dayjs": "^1.10.7",
"react": "^17.0.0",
"react-dom": "^17.0.0",
"react-redux": "^7.2.6",
"react-router-dom": "^5.3.0",
"redux": "^4.1.1"
},
"devDependencies": {
"@vitejs/plugin-react": "^1.0.0",
"cross-env": "^7.0.3",
"electron": "^15.3.0",
"electron-builder": "^22.13.1",
"electron-reloader": "^1.2.1",
"less": "^4.1.2",
"vite": "^2.6.4"
}
}
接着创建electron
// electron.js
const path = require('path')
const { app, BrowserWindow, Menu, Tray } = require('electron')
const getIcon = path.join(__dirname, '../render/assets/') //获取图标
const gotTheLock = app.requestSingleInstanceLock() // 监听多个窗口
const NODE_ENV = process.env.NODE_ENV
Menu.setApplicationMenu(null) // 清除标题栏
let tray = null // 托盘对象
let win = null
require('./file')
// try { 开启这个每次刷新都会自动重启app
// require('electron-reloader')(module);
// } catch { }
// 创建浏览器窗口
const createWindow = () => {
win = new BrowserWindow({
width: 1000,
height: 700,
icon: path.join(getIcon, 'favicon.ico'),
webPreferences: {
devTools: true, // 是否开启调试
nodeIntegration: true, // 这个两个一定要写 这样才能实现主进程和渲染进程的通信
contextIsolation: false
}
})
if (NODE_ENV === 'development') {
win.loadURL('http://localhost:3000/')
win.webContents.toggleDevTools() //打开调试工具
} else {
win.loadFile('./dist/index.html')
}
// 关闭window时触发下列事件.
win.on('close', e => {
e.preventDefault()
win.hide()
win.setSkipTaskbar(true)
})
}
// 设置系统托盘
const setAppTray = () => {
// 系统托盘图标目录
tray = new Tray(path.join(getIcon, 'favicon.ico'))
// 图标的上下文菜单 系统托盘右键菜单
const contextMenu = Menu.buildFromTemplate([
{
label: '退出',
click() {
app.exit()
}
}
])
// 监听鼠标单击
tray.on('click', () => {
let showwin = !win.isVisible()
if (showwin) {
win.show()
win.setSkipTaskbar(false)
return
}
win.hide()
win.setSkipTaskbar(true)
})
// 设置此托盘图标的悬停提示内容
tray.setToolTip('toDoSimple-待办事项')
// 设置此图标的上下文菜单
tray.setContextMenu(contextMenu)
}
console.log(gotTheLock, 'gotTheLock')
// 监听初始化完成
app.whenReady().then(() => {
if (!gotTheLock) return
setAppTray()
createWindow()
//监听应用打开 (macOs)
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
}
)
})
// 所有窗口关闭时退出应用.
app.on('window-all-closed', () => {
// macOS中除非用户按下 `Cmd + Q` 显式退出,否则应用与菜单栏始终处于活动状态.
if (process.platform !== 'darwin') {
app.quit()
}
})
然后启动yarn start
就打开桌面应用了
主进程和渲染进程之间的通信
主进程:electron
的入口文件,能使用node的api
渲染进程:就是我们用raect
写的页面
// 创建浏览器窗口
const createWindow = () => {
win = new BrowserWindow({
// ...
webPreferences: {
nodeIntegration: true, // 这个两个一定要写 这样才能实现主进程和渲染进程的通信
contextIsolation: false
}
})
然后新建一个file.js
使用ipcMain
向渲染进程通信
const { ipcMain } = require('electron')
const fs = require('fs')
const path = require('path')
const baseFileUrl = (title = '') => `${path.join(__dirname, 'data', title)}`
// 封装读取文件
const readFile = file => {
return new Promise((resolve, reject) => {
fs.readFile(file, 'utf8', (err, data) => {
if (err) {
reject(err)
return
}
resolve(JSON.parse(data))
})
})
}
// 封装写入文件
const writeFile = (file, data) => {
return new Promise((resolve, reject) => {
fs.writeFile(file, data, err => {
if (err) {
reject(err)
return
}
resolve('写入成功')
})
})
}
// 监听 渲染进程的事件
ipcMain.on('writeFile', async (event, arg) => {
let { title } = arg
let fileUrl = baseFileUrl(title) + '.json'
try {
await writeFile(fileUrl, JSON.stringify(arg))
event.reply('onWrite', { status: true }) // 向渲染进程发送信息
} catch (err) {
event.reply('onWrite', { status: false, msg: err })
}
})
// 在react里面
const { ipcRenderer } = require('electron') // 用import的话就或报错 而且不能在浏览器里使用
// 写入本地
ipcRenderer.send('writeFile', params)
// 监听本地写入成功
ipcRenderer.once('onWrite', (event, { status, msg }) => {
if (status) {
console.log(status)
return
}
})
这就实现通信了
打包
// package.json
{
// ....
"build": {
"appId": "com.toDoSimple.app",
"productName": "toDoSimple",
"copyright": "Copyright © 2021 呆呆兽的猫猫",
"icon": "render/assets/favicon.ico",
"asar": false, // 如果用到node模块的话就不能使用压缩 ,所以设为false,不然会访问不到,就会出现开发是正常的,但打包后却发现这个功能用不了
"nsis": { // 配置安装
"oneClick": false,
"allowToChangeInstallationDirectory": true
},
"files": [
"dist/**/*",
"render/**/*"
],
"directories": {
"buildResources": "assets",
"output": "dist_electron"
}
}
}