前言
由于好奇Electron技术加上著名的markdown编辑器Typora宣布收费,于是就想借着开源的bytemd实现自己的markdown编辑器,顺便记录下在使用Electron中的踩的一些坑
开源项目地址:https://github.com/andyqier88/omd ,欢迎star~
Electron架构
Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入 Chromium 和 Node.js 到 二进制的 Electron 允许您保持一个 JavaScript 代码代码库并创建 在Windows上运行的跨平台应用 macOS和Linux——不需要本地开发 经验。
图截自Shelley Vohr 在 JSHeroes 演讲PPT
Electron是整合了Chromium和Node.js,将Node.js集成到了Chromium;
如何将Node.js
集成到 Chromium
:
如下图Electron
起了一个新的安全线程去轮询 backend_fd
,当 Node.js
有一个新的事件后,通过 PostTask
转发到 Chromium
的事件循环中,这样就实现了 Electron
的事件融合。
截自Shelley Vohr 在 JSHeroes 演讲PPT
工程搭建
借助vue/cli 和 electron-builder
-
安装Vue脚手架,若已安装则可以跳过,未安装可用如下方式安装
yarn global add @vue/cli
-
创建项目
vue create electronmd
-
配置Electron
cd electronmd vue add electron-builder
-
启动
yarn electron:serve
项目中用到的技术&业务实现
项目搭建好后,先确定下初步的关键需求,我们是想实现一个简单的markdown文档编辑工具
- markdown相关功能(集成bytemd)
- 把编辑好的内容保存(CTRL+S)成.md文件
集成bytemd
这个集成过程非常简单
通过包管理工具yarn add bytemd
、相关插件(@bytemd/plugin-highlight等)、主题这里用的是github-markdown-css
github的主题,社区还有很多其他主题,可以自行选择也可以自己定制。
集成完的代码:
<template>
<Editor :value="value" :plugins="plugins" :fullscreen="true" @change="handleChange" />
</template>
<script>
import 'bytemd/dist/index.css'
import 'github-markdown-css/github-markdown.css'
import { Editor } from '@bytemd/vue-next'
import gfm from '@bytemd/plugin-gfm'
import mediumZoom from '@bytemd/plugin-medium-zoom'
import frontmatter from '@bytemd/plugin-frontmatter'
import highlight from '@bytemd/plugin-highlight'
import mermaid from '@bytemd/plugin-mermaid'
const plugins = [
gfm(),
mediumZoom(),
frontmatter(),
mermaid(),
highlight()
// Add more plugins here
]
export default {
components: { Editor },
data() {
return { value: '', plugins }
},
methods: {
// 获取编辑的内容
handleChange(v) {
console.log(v);
this.value = v
}
}
}
</script>
集成好markdown编辑器后,通过electron主进程和渲染进程通信,来实现文件保存的功能,实现代码逻辑如下:监听keyup
事件,在.vue组件中监听
window.addEventListener('keyup', this.handleKeyPress, true)
handleKeyPress (event) {
// You can put code here to handle the keypress.
if(event.ctrlKey&&event.keyCode==83){
// 向IPC通道发送信号,此时主线程收到信号立即执行相对应的响应函数
window.electron.ipcRenderer.send('open-save-chart-dialog',this.value);
}
},
踩坑-填坑方案
在实现上边代码之前遇到的一个问题就是,在.vue文件中使用electron API时webpack识别不了的一个报错
解决办法:
-
首先新建一个preload.js文件
import { contextBridge, ipcRenderer } from "electron"; contextBridge.exposeInMainWorld("electron", { ipcRenderer, });
-
其次在
vue.config.js
中配置pluginOptions: { electronBuilder: { preload: "./src/preload.js" } }
-
然后在electron main.js中在
new BrowserWindow
对象中配置webPreferences
如下代码:webPreferences: { preload: path.join(__dirname, "preload.js") }
在electron官方文档中也能找到说明
preload
string (可选) -在页面运行其他脚本之前预先加载指定的脚本 无论页面是否集成Node, 此脚本都可以访问所有Node API 脚本路径为文件的绝对路径。 当 node integration 关闭时, 预加载的脚本将从全局范围重新引入node的全局引用标志 参考示例
填完此坑后,我们需要把当前的编辑的内容获取到,通过主进程和渲染进程通信调用文件保存
获取编辑内容很简单,就是在集成bytemd的组件中监听change事件之后赋值
handleChange(v) {
this.value = v
}
主进程-渲染进程通信
拿到md编辑器的内容后,我们知道md编辑器的内容是在渲染进程中,如何把内容传递给主进程调用呢,就是在渲染进程发送事件ipcRenderer.send
window.electron.ipcRenderer.send('open-save-chart-dialog',this.value);
在main.js中通过ipcMain.on
监听对应的事件名称达到通信的目的
ipcMain.on('open-save-chart-dialog', (event, message) => {
// console.log(`receive message from render: ${message}`)
save(message)
})
保存文件
接下来是实现save(message)的逻辑,通过electron的dialog API调起文件保存提示框,借助node.js的fs模块实现文件的保存,具体实例如下:
import { dialog, ipcMain } from "electron";
let fs = require("fs");
export function save(content) {
dialog
.showSaveDialog({
filters: [
{
name: "MD文件",
extensions: ["md"],
},
],
properties: ["openFile"],
defaultPath: '',
message: "选择要导入的Mackdown文件",
buttonLabel: "导出",
title: "保存文件",
})
.then((res) => {
console.log(res);
fs.writeFileSync(res.filePath, content);
})
.catch((req) => {
console.log(req);
});
}
初次尝试electron,不禁感觉electron的强大之处,有些功能还在完善中,如果你也在使用electron,欢迎交流
项目地址:https://github.com/andyqier88/omd,欢迎star~