项目仓库传送门: Yogurt_cry / ccs-md-editor-vue3
1 前言
为什么说 “又” 做了一个呢?早在 2022 年年初的时候,趁着公司的项目开发不紧张的时候,业余用 Vditor
搞了一个桌面 Markdown 编辑器。把开发经历整理发到了社区,反响很好,有感兴趣的小伙伴 Star
和 Fork
了代码,提了一些好的建议。同时个人对这款编辑器长达 10 个多月的使用体验里,也发现了很多使用上不习惯的地方,为了解决这些问题,查了很多资料,有些可以解决,有些是真的没办法。
首先,Vditor
有点太强大了,尤其 实时渲染
和 WYSIWYG
(所见即所得) 的支持上,对于这个项目的魔改,以目前自己在前端上的技术实力来说,还不足以改得太深入,二开工作也只是停留在了对主题样式和一些相关配套功能上的集成而已。例如生成 PDF、打开本地 Md 文件、Markdown 图片管理、提供文章内搜索等等,这些我想要的功能,暂时还没法通过自己当前的技术能力在 Vditor
上得到实现。
其次,作为一名开发人员,绝大部分的笔记都是要记录代码的,然而,Vditor
在代码输入和修改过程中的体验实在是太不好了。
Vditor
在实时渲染下的代码的查看和输入时断开的,代码示例一旦变长,修改代码时的体验就非常的不好,都会界面会跳来跳去的。然而,这个功能也是我暂时还魔改不了的。
最后就是所有 Markdown 编辑器都绕不开的实时渲染性能问题,即便是这次基于 v-md-editor
重新设计的编辑器,其实也不能很好的解决这个问题。一方面一开始在做技术选型的时候,我的需求就是需要一个能像 Typora
那样的编辑器,Vditor
正好主打的就是 实时渲染
和 WYSIWYG
,因此一开始即便是编辑器有因为渲染问题导致的卡顿啊什么的都是觉得可以接受的,毕竟它解决了我当时最急切的痛点。但后来,当使用的频率越来越高,对它的要求也就越来越高了,渲染性能就成了绕不开的难题。加之 Vditor
原生支持了过多的渲染插件 (语法) —— 折线图、时序图、甘特图、折线图、五线谱等等在个人的实际使用过程中往往不如通过截图来呈现的效果更好,反而会因此而导致输入体验变差 (🤔 插入的图表多了,实时渲染就太卡了),实际使用的频率非常低。(🥰 功能是很多很全且非常好的,只是对于我来说有点太多了 🙈)
因此,为了解决 Markdown 笔记体验上所遇到的所有问题,再次踏上了魔改的道路。
2 需求分析
原本计划是自研 Markdown 引擎的 (你瞧瞧这说的是人话嘛 😷),不过还别说,真被我鼓捣出了识别段落的算法。
虽然简单,基本上可以对 Markdown 内容里的段落进行解析了,假以时日,说不定三五年的也能琢磨出个什么来。但对比开源社区里大牛们已经研究出来的成品引擎,比起自己埋头搞研发,实现起来的进度实在是太慢了,以我的能力,就算做出了解析的引擎,相关配套的功能和效果以及插件生态也没法跟已经成熟的产品 (例如: markdown-it) 相提并论,也有悖我单纯想快速要做一个能用的笔记软件的初心。结合年初的版本,以及个人使用 Markdown 码字的体验和痛点,重新思考了自己的需求。
2.1 延续上一版本的需求
- 😳 界面好看,暗黑主题。个人认为,作为一个需要长时间打交道的笔记软件来说,颜值真的很关键,一个不好看的交互界面,很影响码字的心情。主题依然是暗黑效果,一如既往是夜间码字的场景比较频繁。而且大部分的开发用的编辑器全部都是使用的暗黑主题,这样来回切换的时候眼睛不会疲劳。
- 😎 专注笔记本身。这个需求和软件的颜值一样,都没有变。只需要留下笔记和笔记相关的功能,其他的能不要都可以不要。
2.2 新的需求
- 😳 专注 Markdown 语法。这个需求如果从项目的实现难度角度来说,明显就是在偷懒,明明是因为 WYSIWYG 的技术难度太高了,自己实现不了就要退而求其次。然而从做一个 Markdown 编辑器的角度上来说,这却是比较关键的。如果单单是追求 WYSIWYG 的话,明明一般的富文本编辑器就可以实现,而且富文本编辑器的应用场景更广,优秀的产品更多,为什么还要去做 Markdown 编辑器呢?在我看来,其间的不同之处就在于 Markdown 语法本身的特别。专注于写作本身,通过标记符号的字符,为文本设置样式,在不影响可读性的前提下,既提高了写作效率,还能确保最终输出文档的排版美观。我想这是作为 Markdown 编辑器的特色,在这个项目里我觉得应该保留。像现在已经成熟的
Typora
、思源笔记
、MarkText
等产品在 WYSIWYG 场景下的应用非常出色,也是这个小项目未来进化的目标。 - 🤔 使用本地图片及图片转存。在离线场景中,本地图片的引用是刚需,尤其是截图,往往都是截图了直接往编辑器里一粘贴就完事了,如果还要另存图片,再把图片上传到图床,再引用链接,这一套下来怕是码字的思路都没了。急需解决。
- 🤩 导出 PDF。对于 Markdown 来说,其被创造之初的应用场景就是用来发邮件的,邮件内容格式属于 Html,其本质与打印场景是不相同的。在我试用过的所有 Markdown 编辑器中,几乎都提供了转 Word 的功能,但转出来的效果真的不尽人意。Word 和 Markdown 从某种程度上来说都属于同一类的产品,转换完了还要继续给 Word 改格式不如一开始就用 Word 来编辑更好。相比之下,PDF 更加适合 Markdown 作为打印结果的输出。在个人日常的应用场景中,通过输出 PDF 来形成正式文件会有一种很强烈的仪式感,促使我将文字写得更加严谨。
- 🫡 本地文件存储。之前那一版做的设计是将 Markdown 的内容存到本地搭建的数据库中,进而实现了全文搜索的功能,而实际上,在个人的使用场景中对于全文搜索有需求,但并没有那么大,每一篇笔记基本上都是独立的,如果要看那篇笔记,也会直接根据文章标题找到那一篇来看,比较少会用到全文搜索的功能。因此通过单文件来存储是离线场景的刚需。相对的,打开和编辑本地文件也是刚需。
- 😝 文内查找和定位。全文搜索的需求少了,但文内的搜索是相对频繁的。
- 😄 易于拓展。这个需求说白了就是自己没法有效预估未来是不是还会变更自己的需求,如果每次都像这次这样实现不了就换引擎、换框架的话,那不得累死。
3 技术选型
根据需求以及上一版出现过的问题,这次的选型方向是这样的:
3.1 核心组件
比较成熟且好用的 WYSIWYG 的开源项目目前 Yogurt 只找到了一个,就是 Vditor
,但这次又不想用它,后面想起了 VSCode
里的 Markdown 解析器是基于 markdown-it
开发的,那其实也可以选择它作为核心组件。然而,试用了一段时间后发现,需要二开魔改的地方太多了,时间精力和技术实力也不允许我搞那么多的二开,那就找找有没有基于此开发的编辑器了。最终在 N 多款基于 markdown-it
的编辑器组件里选择了本项目的核心组件 v-md-editor
。选择它的原因:
- 完全基于
markdown-it
开发而来,支持自定义添加所有基于markdown-it
的插件。符合 易于拓展 这一条 - 提供了基于
CodeMirror
二开的代码编辑器组件,编码区的样式可以做到样式更丰富。符合 好看 这一条 - 提供了捕获剪切板图片、文件的钩子,为 图片转存 提供了基础
- 提供了渲染区和编码区实时屏幕滚动的功能,方便查看效果。而且这个功能是除了 Markdown 语法解析以外最麻烦的一个了
3.2 前端部分
既然核心的组件都是基于 Node.js
开发的了,自然整个项目都是要在 Web 端来实现了。桌面端的离线 Web 项目,自然还是 Electron
了。本来是想尝试一下用 Tauri
的,结果部署那一关就很难过了,迟迟解决不了部署和配置问题,然后就放弃了。
3.3 其他部分
没了,本次项目全部都在 Vue3
+ TS
+ Electron
中实现。
4 项目成果
4.1 效果图
国际惯例先上图,各位小伙伴们多多指教。
完整样式
纯编辑器样式
图片预览
查找功能
4.2 实现功能
- 渲染区与编辑区位置调整。想必有小伙伴们已经发现有点不同了,一般的 Markdown 编辑器的分栏方式是 “左输入,右渲染” 的,但 Yogurt 长使用 Markdown 码字以及分屏写代码的经历来说,更习惯左侧渲染,右侧编辑,找遍了市面上的编辑器都没有这种布局的,就很奇怪,难道是我的问题?
- 图片转存本地。默认会把所有通过插入、粘贴等方式引入的图片全部转存为图片文件放置到当前笔记的同一目录下。这是根据个人的使用习惯来设置的,大多数情况下图片不会以引用的形式插进来,往往都是随手复制,随手粘贴的,不会去想着把图片放在哪个自定义的文件夹,因此直接放在文件目录下最好了。
- 打开及保存本地 Markdown 文件。这是相较于上一款产品搞出来的,之前不提供保存到本地,总是担心哪天数据库没了,所有的笔记都没了,写个文档、笔记啥的胆战心惊的。这下好了,直接改成打开和保存文件的形式,安全多了。
- 编辑区实时渲染。这个渲染自然是比不上 WYSIWYG 的了,但最起码可以简单的区分一下大致的文章段落,在编辑内容较多的文档时,可以直接关闭预览栏,直接编辑内容。而且这种编辑速度是最快的。
- 提供了文内查找、定位和替换。文档编辑嘛,难免的有点查找啊、替换啊什么的,把这个加上,码起字来更舒服了。
- 提供导出 PDF。对于 Markdown 来说,导出 PDF 的功能简直太重要了,真不是所有人都能看着白字黑字自己的脑补出文档的排版的。而且对于不使用的 Markdown 的小伙伴来说,PDF 才行是了解你写的什么的途径。之前就是因为没做这个功能,上一个项目每次要生成 PDF 的时候都要复制到 VSCode 里用插件 (
Markdown All in One
) 转成 PDF。后面有段时间就直接用 VSCode 来写 Markdown 了,也是这段时间对分离式的 Markdown 写法有感兴趣了的。 - 提供了 60 秒无输入自动保存。纯粹是个人习惯了,有时会忘记保存。
- 大图预览。解决插入图片尺寸过小,看不清内容的问题。
5 开发环境
信息 | 说明 |
---|---|
操作系统 | Microsoft Windows 11 专业工作站版 |
开发工具 | Visual Studio Code |
v-md-editor 版本 | 2.3.15 |
CodeMirror 版本 | 5.65.9 |
Vue 版本 | 3.0.0 |
Electron 版本 | 20.0.0 |
TypeScript 版本 | 4.1.5 |
Node 版本 | 16.13.1 |
NPM 版本 | 8.1.2 |
CNPM 版本 | 7.1.0 |
6 开发经历
6.1 魔改
如果上次开发是小试牛刀的话,那么这次就算是渐入佳境了。说实话,一开始真的想自己搞解析引擎来着,后面发现自己开发的效率实在太低了,出来的效果也仅仅是勉强达到及格线而已。更别说后面还有很多交互性的功能要做了。如果仅仅是自己在这埋头苦干的话,不知道猴年马月才能使上自己的产品。更可怕的是,已经成熟的产品进化的速度比我学习的速度更快。所以就秉持着 “站在巨人的肩膀上前行” 的心态做这次的魔改开发。
魔改的需求就是来源于我自己对 Markdown 的使用习惯了。
- 魔改主题样式。魔改了项目 Markdown 渲染的的主题样式,使其 仅 支持 暗黑 效果。并对整个编辑器的字体大小进行了统一调整。
- 更换默认中文字体。修改默认中文字体为
HarmonyOSSans
(免费商用),避免后期支持 PDF 输出内容时可能会引起的字体版权纠纷 (谁知道呢,万一火了呢🙈)。其次是因为这个字体确实好看,可读性挺强的,尤其是在粗体
的显示上相较于微软雅黑来说更加凸显。( 冷知识:微软雅黑字体如果打印出来的话其实是要版权的 ) - 兼顾实时渲染和输入流畅性。由于实时渲染容易造成卡顿,影响输入体验,因此魔改了
CodeMirror
(编辑器) 对 Markdown 语法的主题样式,能够在不开启实时预览的情况下能直观的查看语法的样式情况,兼顾内容编辑输入的流畅性。 - 本地图片。去掉了
v-md-editor
对xss
的输入限制。说实话,v-md-editor
里的xss
规则真的很严格,尤其是对链接路径。为了处理这个问题,我还把源码下下来魔改了之后重新编译的。不然根本没法在这个编辑器里引用本地图片。 - 大图预览。从
Element-Plus
里薅了一个ElImageView
组件过来,魔改了样式之后实现的。 - 查找替换。为了引入查询替换的模块,也把查询替换的组件源码 (
codemirror-search-replace
) 下下来魔改了。
哪里不喜欢就改哪里😄😄😄
6.2 输出 PDF
这次项目相对来说最麻烦的就数输出 PDF 了。以前只知道在浏览器里把网页打印成 PDF,这一下要把这玩意儿集成到软件里头反倒是不会了。后面有查到一个好用的工具 —— Puppeteer
。全网都说好,我把它集成进来后,确实打印效果不错,但当我一看打包好的包,整个文件夹的容量赫然写着 465 MB 的时候,我麻了。要是再加上 Electron 本身的 257 MB,整个项目我啥也没干,就快 1 个 G 了,都快赶上 Win7 的安装包了。人家实现一个操作系统,我就输出个 PDF…😳😳😳。
后来找了半天都没找到合适的解决方案时,不小心在 Electron
里发现了 printToPDF
的方法,一下子就通了,花了点时间看了一下文档,改完 Markdown 转 Html 的输出文件内容样式后,就调用这个接口直接输出 PDF 了,效果还阔以。
就是功能简单了点,能实现最基本的打印需求,更复杂的就不行了,不过好在 Markdown 本身也不需要太复杂的呈现形式,能用。
7 后记
相比起上一次的开发,这次其实有了很多参照。近一年多以来,不论是公司的项目还是个人的项目,自己在二开上也有了一些经验,应付起来也没有之前那么头疼了。
相比于上一次,这一次除了关注技术层面满足感外,更关心整个项目在使用上的完整度。比如这个项目从写 Markdown 到保存,关闭后再打开,另存为以及输出 PDF 的全链路,按照实际的使用场景来开发项目,让项目更具有实用性。
于此同时也在这个项目里纠正了我曾经的一个错误观念,一直以来,都觉得不管是什么项目,都应该从零开始一个个码出来的代码才算得上是自己的,虽然这个观点非常正确,但却不太符合现实情况。当我们的成长速度跑不过信息爆炸的速度时,某些程度上我们即便是做了所谓的 “原创”,也赶不上时代,那么对这个产品的投资 (时间、人力、金钱) 就将会是失败的。除非你能找到另外一条与当下时代不同的,并且具有弯道超车可能的道路,如果单纯只是模仿,走前人所走过的道路,那永远也赶不上前人,更别说是继承了。比如说这个项目就是在模仿前人,只是想在模仿过程中删删减减,以达到自己学习开发思路目的的行为。
但,也并没有那么悲观。因为现代计算机世界的高楼大厦都是建立在前辈们开源的基础上,且有源源不断的开发人员前赴后继地添砖加瓦,计算机世界才能拥有如此惊人的辉煌成就。“站在巨人的肩膀上前行” 并不丢人,当有那么一群人和你的想法一致,并且能够以代码的形式体现时,这是幸福的。在合理合法的条件下,“排列组合” 有时也是一种创新。
初心不变,这篇文章依然是在新鲜出炉的 创云笔记 v1.1.4
中编写的。
8 参考文献
排名不分先后
- [官方文档] v-md-editor 官方文档
- [官方文档] markdown-it 中文文档
- [官网文档] CodeMirror5
- [掘金社区] 如何在electron中将文件保存到指定目录
- [简书] Electron实践(二)—— 打开和保存本地文件
- [Electron 中文文档] webContents
- [简书] electron实现图片旋转后保存到本地
- [Github] zhuhs/codemirror-search-replace
- [NPM] codemirror-search-replace
- [HarmonyOS Developer] 通用设计(字体)
- [官网] Markdown 创始人建立的 Markdown 解析网站
- [CSDN] 使用 Electron 打印到 PDF
- [CSDN] Element-ui中 使用图片查看器(el-image-viewer) 预览图片
- [CSDN] electron+vue3的基本使用及通信
- [CSDN] npm安装本地包
- [CSDN] Vue3:显示 markdown 文档