大量のテキストを食っても速い Markdown Editor 作った

もう人生で何個目かわからない markdown エディタ作った。が、今回のは結構気に入っている。

https://markdown-buffer.netlify.com/ で遊べる。

用途としては、GitHub か Qiita か はてなブログかわからないが、なにか書こうと思ったときに、どのサービスも中途半端に重いので、とりあえずのバッファが必要、という感じで作った。なので速度重視。

ブラウザのストレージで永続化してる。オフラインで動く。できるだけエディタとしてはスコープを大きくせず、単に編集バッファ(textarea)でしかない、というのを意識している。

結構頑張って作り込んでしまった https://nedi.app が色々肥大化してしまっていて入力時にラグを感じるので、編集体験を見つめ直す自戒もある。

機能

  • 数式対応
  • コードハイライト対応
  • バックグラウンドで自動保存
  • 改行を br に(line-break)
  • Desktop PWA 対応 (chrome://flags から有効化)
  • Ctrl+Alt+F で Prettier
  • Ctrl+1 で プレビューをトグル
  • WordCount 機能

スクロールシンクを実装してない。厳密にやろうとすると面倒なので後回し。

off-the-main-thread

メインスレッドのパフォーマンスをかなり意識した。

たとえば、なぜ仮想DOMという概念が俺達の魂を震えさせるのか - Qiitamarkdown、8000字程度を remark でコンパイルするのに、メインスレッドで 23~40ms 掛かる。これは一文字打つたびに、実際にもたつきとして感じられる。

これを WebWorker に逃がすことで、メインスレッドで入力中は全くラグらなくなった。

DevToolsのフレームグラフみても全然負荷掛かってない。

その代り、postMessage が往復する時間で 4ms x 2 ぐらいのプレビューの遅延が発生した。0.5フレームなので人間が意識するのは難しい。

バックグラウンド側のスレッドの初期化がチョット重いのだが、これは prettier/standalone のせいで、ただこいつを入れた結果ブラウザで prettier 使えるようになったので、まあいいかという感じ。もっとグレースフルに初期化する方法もあるが、そこはサボった。

使ったライブラリ

https://github.com/GoogleChromeLabs/comlink を使って postMessage をラップした。とても使いやすかった。

WebWorker 側だと localStorage に触れず、 IndexedDb を使う必要があったので、https://github.com/dfahlander/Dexie.js を使った。

remark の設定

このエディタでの書いたものを、外に持ち出したい場合の remark

import "github-markdown-css/github-markdown.css";
import "katex/dist/katex.min.css";
import "highlight.js/styles/default.css";

import remark from "remark";
import math from "remark-math";
import hljs from "remark-highlight.js";
import breaks from "remark-breaks";
import katex from "remark-html-katex";
import html from "remark-html";

const processor = remark()
  .use(breaks)
  .use(math)
  .use(katex)
  .use(hljs)
  .use(html);

const html = processor.processSync("# Hello!).toString()