v-model无法直接实现markdown↔html双向绑定,因编辑器操作dom或自定义模型而非纯字符串,导致粘贴富文本崩溃、重载丢格式、预览不同步。

不能靠编辑器默认行为自动双向绑定,必须手动桥接 Markdown 与 HTML 的数据流 —— 否则粘贴富文本会崩、保存后重载丢失格式、预览与源码不同步。
为什么 v-model 或 v-bind 无法直接实现 Markdown ↔ HTML 双向绑定
HTML 编辑器(如 Quill、Milkdown、v-md-editor)内部操作的是 DOM 节点或自定义文档模型(如 ProseMirror JSON),不是纯字符串。你传给它的 v-model 值如果是 Markdown,它不会自动解析;你从它读出的值如果是 HTML 或 Delta,也不是原始 Markdown。
常见错误现象:
- 在
quill-editor上绑定v-model="markdownContent",输入## hello,编辑器显示为纯文本而非二级标题 - 用
v-md-editor编辑后取editorRef.value.getHtml()存库,再赋值回v-model,内容变成带内联样式、<div> 包裹的 HTML 片段,而非可读 Markdown<li>用户复制微信文章粘贴进来,编辑器渲染正常,但 <code>v-model里突然多出一堆<span style="color:#333"></span>,后续转 Markdown 时 turndown 无法还原语义 - 编辑器只接收和输出 HTML:
quill-editor的v-model绑定htmlContent,不直接连 Markdown 字段 - Markdown 输入变更时,用
marked转成 HTML 再写入编辑器:htmlContent = marked(markdownContent) - 编辑器内容变更(
@text-change或@change)时,用turndown把 HTML 转回 Markdown:markdownContent = turndownService.turndown(html) - 务必配置 turndown 过滤非语义标签:
turndownService.remove('script,style,iframe'),否则粘贴含广告脚本的网页会带进源数据 - marked 必须开启
sanitize: true或配合DOMPurify.sanitize(),否则markdownContent = '<img src="x" onerror="alert(1)">'会在预览区执行 - 监听
@paste事件,第一时间event.preventDefault() - 调用
navigator.clipboard.read()(仅限用户手势上下文,所以只能在 paste 里用) - 遍历
clipboardItem.types,优先找"text/html",降级到"text/plain";跳过"application/x-notion"等私有类型 - 拿到 HTML 字符串后,**不直接插入编辑器**,先用
turndownService.turndown(html)转成 Markdown,再更新markdownContent字段 —— 这样后续所有同步都从干净源码出发 - 避免用正则清洗 HTML:表格嵌套、自闭合标签、
<pre class="brush:php;toolbar:false;"><code></code> 结构会被破坏;turndown 是唯一可靠选择</pre> - 构造两个 Blob:
new Blob([md], { type: 'text/plain' })和new Blob([html], { type: 'text/html' }) - 传入
new ClipboardItem({ 'text/plain': plainBlob, 'text/html': htmlBlob }) -
htmlBlob必须是合法 HTML 片段,例如<p><strong>hello</strong></p>,不能是 Quill 的 Delta 或<div class="ql-editor"> 容器<li>别漏掉 <code>text/plain—— 大量终端、IDE、钉钉文档粘贴时根本不会查text/html,只 fallback 到纯文本
如何用 marked + turndown 实现可控的双向驱动逻辑(Vue2/3 通用)
核心是把编辑器当作“HTML 端”,把 textarea / 数据字段当作“Markdown 端”,中间用两个转换器单向桥接,禁止反向污染。
实操建议:
Clipboard API 粘贴时如何保持 Markdown 源码纯净
用户从 Word、Notion 复制内容进编辑器,默认触发浏览器原生粘贴,直接插入 HTML,绕过你的转换逻辑 —— 这是双向绑定断裂最常发生的时刻。
必须拦截并重写粘贴行为:
复制操作如何让不同目标应用各取所需(VS Code / Word / 微信)
用户点击「复制」时,不能只生成 Markdown 或只生成 HTML —— VS Code 要 text/plain,Word 要 text/html,微信 PC 版只认前者。
正确做法是用 clipboard.write() 一次性写入多格式 ClipboardItem:
真正难的不是写对一次转换,而是确保每次粘贴、每次编辑、每次复制,都走同一套降级路径:HTML → 语义剥离 → Markdown → 渲染 → HTML。中间任何一环跳过净化或误用正则,数据就不可逆地脏了。











