实现一个简陋的跨平台MarkDown编辑器

莫名的又任性了一次 早上本来开开心心的要写blog,一直用来写markdown的vscode突然bug了,重装无果,去网上找了几个MD编辑器,尝试之后觉得都不怎么舒心,冲动之下又造了个轮子。

效果图

不过做到一半的时候是有点后悔的,MD编辑器涉及的东西确实比较多,可是能怎么办呢,自己挖的坑含着泪也要填完,目前为止算是能满足最简单的需求了,记录一下吧

说出来你可能不信,现在这篇blog就是用这小编辑器写的,使用到的工具有:

  • Electron用来跨平台
  • Vue 写前端很舒服
  • marked MD语法解析
  • highlight.js 代码语法高亮

框架选型

跨平台的桌面应用框架无非这几种,java/python/qt/H5系列,为啥要选Electron呢?

首先java的几套UI框架都不是很好用,而且不好看。 python的UI框架wxPython虽然不错,但是python和java一样发布起来需要自带runtime,否则就需要客户系统安装环境才能使用。 qt的话需要针对平台编译,没办法做到一份编译在所有环境都能运行,而且c写起来也太麻烦。

其实上面的那些都不是原因,真正的原因是因为MarkDown的解析结果和H5是最亲和的,得的解析结果以HTML的形式直接使用,想要自定义样式也是非常方便,只需要改一下css即可。

因为Electron是基于H5的框架,那么UI肯定不能直接写HTML啊,太low,前端框架用起来,在Vue和React之间选择了Vue。

MD解析和语法高亮都有现成的,markedhighlight.js直接拿来用就好了。

搭建项目

vue和Electron的项目都有官方的初始化方式,但是两个框架如何融合使用官方并没有给出确切的文档。所幸在github找到了一个配置好的vue框架模板,拿来用。

安装vue-cli(vue环境必需)

npm install -g vue-cli

使用electron-vue模板初始项目

vue init simulatedgreg/electron-vue my-project

安装项目依赖并运行

cd my-project
yarn # 或者 npm install
yarn run dev # 或者 npm run dev

或者使用cnpm,速度会快一些

这时候我们已经得到了一个Electron版本的vue项目,和vue项目的区别不大,只是运行时会使用Electron来进行显示而不是浏览器。

安装marked依赖

npm install marked --save

安装highlight.js依赖

npm install highlight.js

实现渲染

vue生成的项目是默认带有router和vuex的,vuex我们暂时用不上。新建一个Editor.vue文件并把这个组件设为router的默认页面: router/index.js

...
export default new Router({
    routes: [
        {
            path: '/',
            name: 'editor-page',
            component: require('@/components/Editor')
        },...
    ]
})

编辑Editor.vue,把组件平分成左右两个区域,左边用textarea,右边用div

<template>
    <div id="wrapper">
        <textarea :value="input" @input="update" class="text-area" ></textarea>
        <div class="split-bar" >
            <div class="split-line"/>
        </div>
        <div v-html="compiledMarkdown" class="render-area"></div>
    </div>
</template>

样式这里不帖了,然后把UI和数据进行绑定

<script>
    let marked = require('marked');
    import 'highlight.js/styles/googlecode.css' //样式文件
    marked.setOptions({
        renderer: new marked.Renderer(),
        gfm: true,
        tables: true,
        breaks: false,
        pedantic: false,
        sanitize: false,
        smartLists: true,
        smartypants: false,
        highlight: function (code, lang) {
            return require('highlight.js').highlightAuto(code).value;
        }
    });
    export default {
        name: 'editor-page',
        data: function () {
            return {
                input: "",
            }
        },
        methods: {
            update: function (e) {
                this.input = e.target.value
            }
        },
        computed: {
            compiledMarkdown: function () {
                return marked(this.input, {sanitize: true})
            }
        }
    }
</script>

这是一个双向绑定,textarea的数值变更会触发update方法并更新input字段,同时,input字段通过compiledMarkdown方法进行解析和渲染并将结果以html形式绑定到了右侧的div显示区域。

添加样式

marked解析器只是把MD格式的内容解析到想成的html文本,解析结果中并不包含任何样式,这种模式就提供给我们了很大的自由空间,样式由我们自已定义,方法很简单,Editor.vue文件中直接写入即可:

<style>
h1{
...
}
h2{
...
}
p{
...
}
....
</style>

可以说这个样式想怎么写就怎么写。

保存和打开

保存和打开文件的形式有很多,比如选择文件/最近一次打开/拖拽等等。这里我们只实现最基础的拖拽打开和ctrl+s保存。

首先是拖拽打开,H5原生支持拖拽打开文件的特性,这里先把父容器的拖拽事件禁止掉 新增methods

preventDefaultEvent (eventName) {
	document.addEventListener(eventName, function (e) {
		e.preventDefault();
	}, false)
}
mounted () {
	['dragleave', 'drop', 'dragenter', 'dragover'].forEach(e => {
		this.preventDefaultEvent(e);
	});
}

在编辑界面新增拖拽响应,新增绑定 @drop.prevent=“ondrop”,在methods中新增方法

    ondrop(event){
        let fileList = event.dataTransfer.files;
        console.info(123, fileList)
        if (fileList.length === 0 || fileList.length > 1) {
            return false
        }
        let file = fileList[0];
        if (file.type != 'text/markdown') {
            return false
        }
        fs.readFile(file.path, 'utf8', (error, data) => {
            this.input = data
            this.filePath = file.path
            document.title = file.name
        })
    }

fs是通过require(‘fs’)引入的文件操作模块

接下来是ctrl+s保存,在最外层的div上加入按键监听@keyup.ctrl.83="onsave",代表监听ctrl摁下且ascii码为83的按键,也就是s,同时在methods中加入onsave方法。

onsave(){
    if (this.filePath) {
        fs.writeFile(this.filePath, this.input, (error) => {
            console.info(error)
        })
     }
}

编译打包

运行build命令

npm  run build

之后会在build文件夹中生成相应平台的可执行文件。

完工。

到目前为止,这个小编辑器可谓是非常的简陋,简陋到连一个菜单都没有,但是作为一个MarkDown编辑器的基本功能是完备的。

代码地址: https://github.com/NightFarmer/MDEditor

文章目录
,