Markdown 实时预览怎么实现双向同步?
我用 contenteditable 做了个 Markdown 编辑器,左边写源码右边实时预览,但改预览区内容没法同步回源码区,这咋整?
试过监听 input 事件,但预览区是渲染后的 HTML,转回 Markdown 会丢格式。比如我加粗文字 **hello** 渲染成 <strong>hello</strong>,再转回去就变不回来了。
有没有靠谱的库或者思路能保持两边同步?现在用的是 marked.js 渲染:
const markdown = document.getElementById('markdown');
const preview = document.getElementById('preview');
markdown.addEventListener('input', () => {
preview.innerHTML = marked.parse(markdown.value);
});
**)。不过有现成的库可以一定程度上解决这个问题,我推荐用 turndown 这个库来做反向转换。
首先你需要引入 turndown:
然后改造你的代码,大概思路是这样的:
代码块
});
// 标记是否来自源码区的更新(避免死循环)
let isUpdatingFromSource = false;
// 源码区 → 预览区
markdown.addEventListener('input', () => {
isUpdatingFromSource = true;
preview.innerHTML = marked.parse(markdown.value);
isUpdatingFromSource = false;
});
// 预览区 → 源码区(关键部分)
preview.addEventListener('input', () => {
if (isUpdatingFromSource) return;
// 获取预览区的 HTML,转成 Markdown
const html = preview.innerHTML;
const convertedMarkdown = turndownService.turndown(html);
// 更新源码区
markdown.value = convertedMarkdown;
});
``
**原理说明:**
turndown 这个库做的事情就是尽可能把 HTML 转回 Markdown。它会分析 HTML 标签结构,比如看到
就转成**,看到转成*,看到转成#。
hello**注意事项:**
这种方案有个现实问题无法完全避免:某些复杂格式可能会丢失或者略有变化。比如你原来写的
渲染成hello,turndown 会转回hello`,这个其实没问题。但有些情况比如嵌套的复杂列表、表格之类的,转换后可能会有细微差别。如果你的场景对格式要求特别严格,可能需要考虑换一个思路:不用 contenteditable + 渲染的方式,而是直接用支持双向绑定的编辑器库,比如 ByteMD 或者 Milkdown,这些库内部维护的是 Markdown AST,编辑和渲染都基于同一份数据,天然支持双向同步。
但如果你只是想快速实现一个基本可用的版本,turndown 这个方案够用了,自己加一些边界情况的处理就行。