时间:2023-05-26 02:36:01 | 来源:网站运营
时间:2023-05-26 02:36:01 来源:网站运营
使用Electron开发记事本:electron-vue
去作为项目的基础模版,而是在github上着了一个加了webpack
功能的模版,输入以下命令开始:git clone git@github.com:szwacz/electron-boilerplate.git life-memory
v7.1.7
看看是否可以正常运行,这里为了避免因为网络问题导致下载失败就直接用cnpm
进行安装了。src/menu
目录下,我们要做两件事,一是为Mac系统的菜单腾出第一个位置,二是补充自己需要的菜单项。下面是我的菜单项定义:// file_menu_template.jsimport { dialog } from 'electron'import log from 'electron-log'export const fileMenuTemplate = { label: '文件', submenu: [ // { // label: '新建', // accelerator: 'CmdOrCtrl+N' // }, { label: '打开', accelerator: 'CmdOrCtrl+O', click: openFile }, { type: 'separator' }, { label: '保存', accelerator: 'CmdOrCtrl+S', click: saveFile }, { label: '另存为', accelerator: 'CmdOrCtrl+Shift+S', click: saveAsFile } ]}export const macAppMenuTemplate = { label: '生活记', submenu: [ { label: '退出', role: 'quit' } ]}/** * 打开文件 * @param {MenuItem} menuItem 菜单项 * @param {BrowserWindow} browserWindow 渲染进程窗口 * @param {Event} event 事件 */function openFile(menuItem, browserWindow, event) { dialog.showOpenDialog(browserWindow, { title: '打开文件', filters: [ { name: 'Markdown 文件', extensions: ['md', 'markdown'] }, { name: '文本文件', extensions: ['txt'] } ], properties: ['openFile'] }).then(dialogRes => { if (!dialogRes.canceled) { // 向当前获取焦点的窗口发送事件 if (browserWindow) { browserWindow.webContents.send('lm-open-file', dialogRes.filePaths) } } }).catch(e => { log.error(e) })}/** * 保存文件 * @param {MenuItem} menuItem 菜单项 * @param {BrowserWindow} browserWindow 渲染进程窗口 * @param {Event} event 事件 */function saveFile(menuItem, browserWindow, event) { if (browserWindow) { browserWindow.webContents.send('lm-save-file') }}/** * 另存为 * @param {MenuItem} menuItem 菜单项 * @param {BrowserWindow} browserWindow 渲染进程窗口 * @param {Event} event 事件 */function saveAsFile(menuItem, browserWindow, event) { if (browserWindow) { browserWindow.webContents.send('lm-save-as-file') }}
因为第一个位置暂时也不需要添加其他内容,所以我就没有将其拆分出去,而是和文件菜单放在一个文件里了。写这篇文档时项目已经完成,所以这个文档的代码中会包含一些现在用不到的代码,见谅见谅~
// edit_menu_template.jsexport const editMenuTemplate = { label: "编辑", submenu: [ { label: "撤销", accelerator: "CmdOrCtrl+Z", click: undo }, { label: "重做", accelerator: "Shift+CmdOrCtrl+Z", click: redo }, { type: "separator" }, { label: "剪切", accelerator: "CmdOrCtrl+X", selector: "cut:" }, { label: "复制", accelerator: "CmdOrCtrl+C", selector: "copy:" }, { label: "粘贴", accelerator: "CmdOrCtrl+V", selector: "paste:" }, { label: "全选", accelerator: "CmdOrCtrl+A", click: selectAll } ]};/** * 选择全部 * @param {MenuItem} menuItem 菜单项 * @param {BrowserWindow} browserWindow 渲染进程窗口 * @param {Event} event 事件 */function selectAll(menuItem, browserWindow, event) { if (browserWindow) { browserWindow.webContents.send('lm-select-all') }}/** * 撤销 * @param {MenuItem} menuItem 菜单项 * @param {BrowserWindow} browserWindow 渲染进程窗口 * @param {Event} event 事件 */function undo(menuItem, browserWindow, event) { if (browserWindow) { browserWindow.webContents.send('lm-undo') }}/** * 重做 * @param {MenuItem} menuItem 菜单项 * @param {BrowserWindow} browserWindow 渲染进程窗口 * @param {Event} event 事件 */function redo(menuItem, browserWindow, event) { if (browserWindow) { browserWindow.webContents.send('lm-redo') }}
编辑菜单里面基本上都是对文档内容的快捷操作。// help_menu_template.jsimport { app, shell } from "electron";import jetpack from "fs-jetpack";const appDir = jetpack.cwd(app.getAppPath());const manifest = appDir.read("package.json", "json");export const helpMenuTemplate = { label: '帮助', submenu: [ { label: '学习Markdown语法', click: function (item, focusedWindow) { // 打开外部文档 shell.openExternal('https://www.runoob.com/markdown/md-tutorial.html') } }, // { // label: '帮助' // }, { label: '关于', submenu: [ { label: '版本 v' + manifest.version, enabled: false } // { // label: '更新记录' // } ] } ]}
帮助菜单中则是将应用的版本号显示出来,另外还有一个开发时显示的菜单,那个菜单只需要去掉退出应用的菜单项即可。background.js
中,我们需要将新增的菜单定义加入,并稍微修改一下逻辑,让Mac系统下的菜单列表前面增加一个占位的菜单。import { devMenuTemplate } from "./menu/dev_menu_template";import { editMenuTemplate } from "./menu/edit_menu_template";import { macAppMenuTemplate, fileMenuTemplate} from "./menu/file_menu_template";import { helpMenuTemplate } from "./menu/help_menu_template";const setApplicationMenu = () => { const menus = [fileMenuTemplate, editMenuTemplate, helpMenuTemplate]; if (process.platform === "darwin") { menus.unshift(macAppMenuTemplate); } if (env.name === "development") { menus.push(devMenuTemplate); } Menu.setApplicationMenu(Menu.buildFromTemplate(menus));};
此时运行程序,我们定义的菜单就会如期显示出来,接下来要让程序对用户点击菜单项做出响应,则需要在菜单定义中定义click函数
,普通的菜单点击,我们只需要将事件发送到当前聚焦的窗口,让它去处理这个事件即可。ipcRenderer
去获取,这一点官方文档已经讲得很详细了,我就不再细说了。electron-log
可以使用。我的用法比较简单,就在主线程中修改了日志记录的格式,后面因为全局共享一个实例,所以其他地方就不用去修改配置了,直接引入这个包即可。import log from "electron-log";// 修改日志记录的格式log.transports.console.format = "[{h}:{i}:{s}.{ms}] [{level} {processType}] › {text}";log.transports.file.format = "[{y}-{m}-{d} {h}:{i}:{s}.{ms}] [{level}] {text}";log.debug("path of user data: ", app.getPath("userData"));
在启动时我还去打印了一下用户数据的存放位置,方便以后排查问题。CodeMirror
这个插件,引入的时候还是遇到了一些问题的,下面是我的解决方案:import CodeMirror from "codemirror/lib/codemirror";import "codemirror/mode/markdown/markdown";this.editor = CodeMirror.fromTextArea( document.getElementById(textareaId), editorOptions );
到这一步还是正常的,可当我要引入它的CSS文件时,它就报错了,我也不知道为什么。但最后想出了一个解决办法,就是把css文件在html中引入:<!-- app/app.html --><link rel="stylesheet" href="../node_modules/codemirror/lib/codemirror.css">
这样做之后基本上就没什么问题了,关于记事本的代码其实很简单,逻辑也不复杂,就不贴出来献丑了。async/await
了~cnpm i sass node-sass sass-loader --save-dev
安装之后,找到build/webpack.base.config.js
,在rules
中添加:{ test: //.scss$/, use: [ { loader: 'style-loader' }, { loader: 'css-loader' }, { loader: 'sass-loader' } ] }
这样就可以解析scss文件了。1024x1024.png
的图标之后,还需要为windows平台生成.ico
格式的图标,此时我们可以在https://www.easyicon.net/language.en/covert/这个网站去转换。.icns
的图标则没有那么简单,因为icns格式并不是一个图标,而是包含不同分辨率图标的集合,我们需要一个一个的生成然后再去转换。我就填了这个坑,我在刚刚的网站把转换好的一张icns格式的图片放到项目中打包,结果打包后的应用是没有图标的!转换icns并没有网站可以帮我们做,我们只能在mac电脑中敲命令来做。具体可以参考这个博客,需要注意的是刚开始创建的目录,后面的
.iconset
不能省,前面的名字可以随便起。resources
目录下的图标文件即可。packages.json
中,恰好我用的这个模版里面的打包工具就是它,我们只需要在build
下面加上下面的配置即可关联自己定义的文件格式了。"fileAssociations": [ { "ext": "md", "name": "Markdown 文件", "role": "Editor" }, { "ext": "markdown", "name": "Markdown 文件", "role": "Editor" }, { "ext": "txt", "name": "文本文件", "role": "Editor" } ],
关联之后,程序只是虚有其表,因为我们并没有真正去处理传来的文件,所以下一步就是接收文件路径。在这一步自己也爬了一个又一个坑,都是血和泪的教训啊。。。open-file
事件的,而Windows则是通过进程对象来获取文件路径。// background.js// 外部文件路径let preFilePath = ''app.on('will-finish-launching', () => { log.debug('will-finish-launching') // 打开文件事件(MacOS有效) app.on("open-file", (e, filePath) => { log.debug("open-file: ", filePath); const fw = BrowserWindow.getFocusedWindow(); if (fw) { fw.webContents.send("lm-open-file", [filePath]); } else { preFilePath = filePath } }); // 检查进程是否含有参数(Windows有效) if (process.platform ==='win32' && process.argv.length >= 2) { log.debug('process argv:', process.argv) // windows系统当没有路径参数时这个位置默认有个.,需要加以判断 preFilePath = process.argv[1] === '.' ? '' : process.argv[1] }})mainWindow.once('ready-to-show', () => { log.debug('ready-to-show') mainWindow.show() // 检查是否存在需要直接打开的文件,有的话就直接打开 if (preFilePath) { mainWindow.webContents.send('lm-open-file', [preFilePath]) } })
其中,在app的will-finish-launching
事件中才开始监听文件打开事件,也是官方文档上面建议的:关键词:记事,使用