
简介这是一套基于 Python、Flask 与 Editor.md 构建的在线 Markdown 编辑工具源码面向具备 Flask 基础、希望学习或直接搭建在线编辑平台的开发者。项目整合了 Flask-SQLAlchemy、Flask-Login 与 sm.ms 图床实现登录注册、文章编辑与文章列表三个页面并支持自动保存、图片上传至图床等功能适合作为 Web 开发练手项目或轻量级写作工具直接部署。压缩包共 559 个文件约 15.07MB以 208 个 js、170 个 html、60 个 css 等前端资源为主另有 14 个 py 后端文件、8 个 md 说明文档及图片、字体、配置等辅助文件并附有 readme.md 教程。目前已有 447 人学习。读者可从中获取完整的项目目录结构、前后端交互逻辑与图床集成思路便于理解 Flask 项目的组织方式并在此基础上二次开发。1. 在线 Markdown 编辑工具从「打开就能写」到「写完能交付」的完整链路很多人第一次接触在线 Markdown 编辑工具是因为临时要写一份技术文档本地没装 Typora或者公司电脑不让随便装软件。打开浏览器找个在线编辑器左边写# 标题右边实时渲染感觉挺顺手。但真正把它用进日常工作流之后问题就来了换行怎么不生效表格怎么复制到 Excel图片路径为什么在别人电脑上打不开数学公式渲染出来是乱码这些坑几乎每个长期用 Markdown 的人都会踩一遍。在线 Markdown 编辑工具的核心价值不是「能写 Markdown」——本地编辑器也能写。它的真正优势在于跨设备、免安装、可协作、能直接对接发布流程。你可以在公司 Windows 上写一半回家用 Mac 继续改可以把链接甩给同事对方不用装任何东西就能看渲染效果可以直接导出 Word、PDF或者复制到公众号后台。这篇文章面向的是需要把 Markdown 用进实际交付流程的工程师和文档写作者不是只想知道「Markdown 是什么」的纯新手。我会从选型、核心功能实现、避坑、进阶技巧四个层面把在线 Markdown 编辑工具这条链路讲透。2. 在线 Markdown 编辑工具的技术选型为什么不是随便找一个就行2.1 渲染引擎决定了下限marked、markdown-it 还是 remark在线编辑器的第一层是渲染引擎。你看到的「左边写、右边变」效果背后是某个 Markdown 解析库在干活。常见的有三类marked轻量、快GitHub 早期也在用。适合对性能敏感、不需要复杂扩展的场景。但它对表格、脚注、数学公式的原生支持较弱需要自己挂插件。markdown-it插件生态最丰富支持表格、任务列表、脚注、自定义容器、数学公式通过 markdown-it-katex 或 markdown-it-mathjax3。大多数在线编辑器选它因为「什么都能加」。remark / rehype基于 AST适合需要深度定制输出结构的场景比如你要把 Markdown 转成特定 JSON 给前端渲染或者做 lint 规则。学习曲线比前两个陡。选型建议很直接如果你只是做一个「能写能预览」的工具markdown-it 是默认答案。如果你要做「Markdown 转 Word 并保留序号自动编号」这种深度转换remark 的 AST 操作会更可控。2.2 编辑器内核CodeMirror、Monaco 还是 textarea渲染引擎管「怎么解析」编辑器内核管「怎么写」。三种常见方案内核优势劣势适用场景textarea零依赖、极简无语法高亮、无快捷键扩展极简工具、嵌入页面CodeMirror 6轻量、移动端友好、扩展性好生态比 Monaco 小大多数在线编辑器MonacoVSCode 同款、功能最强体积大、移动端体验差桌面端优先的复杂编辑器我一般会选 CodeMirror 6。它在移动端能正常输入体积可控而且有现成的 Markdown 语言包和快捷键扩展。Monaco 虽然功能强但在线工具如果面向「打开就能写」的场景加载一个几 MB 的编辑器内核会让首屏体验变差。2.3 存储与同步localStorage、IndexedDB 还是后端在线编辑器最怕的是「写了一半刷新没了」。存储方案分三档localStorage简单但容量只有 5MB 左右且同步阻塞。适合存草稿不适合存大量文档。IndexedDB容量大、异步适合存多篇文档和图片 blob。但 API 复杂通常用 idb 或 Dexie.js 封装。后端存储适合协作场景但需要处理用户体系、权限、冲突合并。一个务实的做法是本地用 IndexedDB 做自动保存后端只存「用户主动保存」的版本。这样即使断网也不会丢内容。2.4 最小可运行版本用 markdown-it CodeMirror 6 搭一个在线编辑器下面是一个可以直接跑起来的最小实现。用 Vite 起项目装两个核心依赖npm create vitelatest online-md-editor -- --template vanilla cd online-md-editor npm install markdown-it codemirror codemirror/lang-markdown codemirror/view codemirror/state然后写主逻辑// main.js import MarkdownIt from markdown-it; import { EditorView, basicSetup } from codemirror; import { markdown } from codemirror/lang-markdown; // 初始化 markdown-it开启表格和换行支持 const md new MarkdownIt({ html: false, // 不渲染原始 HTML防止 XSS linkify: true, // 自动识别链接 breaks: true, // 单个换行转 br解决 markdown换行 问题 typographer: true, // 智能标点 }); // 左侧编辑器 const editor new EditorView({ doc: # 标题\n\n开始写..., extensions: [basicSetup, markdown()], parent: document.querySelector(#editor), }); // 右侧预览监听编辑器变化实时渲染 editor.dispatch({ changes: { from: 0, insert: }, }); // 简单轮询同步生产环境应使用 updateListener setInterval(() { const content editor.state.doc.toString(); document.querySelector(#preview).innerHTML md.render(content); }, 300);这段代码的逻辑说明breaks: true是解决「markdown换行」问题的关键。默认 Markdown 规则里单个换行会被合并成空格必须空一行才换段。开启后单换行直接转br符合大多数人在线写作的直觉。html: false是安全底线。在线编辑器如果允许渲染原始 HTML别人可以注入脚本。除非你完全信任输入来源否则不要开。轮询同步只是演示。实际项目里应该用 CodeMirror 的EditorView.updateListener扩展在文档变化时触发渲染避免不必要的重绘。参数怎么改如果要支持数学公式加markdown-it-katex插件并在页面引入 KaTeX 的 CSS。如果要支持 Mermaid 图表加markdown-it-mermaid或自己写一个 fence 规则把mermaid块转成div classmermaid。如果要支持表格复制到 Excel需要在渲染后的table上挂一个复制按钮把表格转成 TSV 格式写入剪贴板。3. 在线 Markdown 编辑工具的核心功能实现表格、图片、公式、导出3.1 Markdown 表格转 Excel复制粘贴背后的 TSV 转换「markdown表格复制」是高频需求。很多人写完表格想直接粘到 Excel 或飞书表格里结果粘过去是一坨文本。原因是剪贴板里放的是 HTML 或纯文本Excel 不认。正确做法是在渲染后的表格上加一个「复制为表格」按钮点击时把table转成 TSVTab 分隔值然后写入剪贴板。function tableToTSV(table) { const rows table.querySelectorAll(tr); const lines []; rows.forEach(row { const cells row.querySelectorAll(th, td); const values Array.from(cells).map(cell { // 去掉单元格内的换行和多余空格避免破坏 TSV 结构 return cell.innerText.replace(/\n/g, ).trim(); }); lines.push(values.join(\t)); }); return lines.join(\n); } async function copyTable(btn) { const table btn.closest(table); const tsv tableToTSV(table); await navigator.clipboard.writeText(tsv); btn.innerText 已复制; setTimeout(() btn.innerText 复制为表格, 1500); }逻辑说明TSV 是 Excel 和大多数表格软件都能识别的纯文本格式。用\t分隔列\n分隔行。关键点是单元格内的换行必须替换成空格否则粘贴到 Excel 会错行。参数注意如果表格里有合并单元格TSV 无法表达需要降级为 HTML 格式写入剪贴板。但大多数 Markdown 表格没有合并单元格TSV 足够。3.2 图片路径的三种处理方式相对路径、Base64、图床「markdown图片路径」是在线编辑器最容易翻车的地方。你在本地写本地预览正常但把 Markdown 发给别人图片全挂。三种方案对比方案写法优点缺点相对路径简单、可版本管理换设备就挂Base64 内嵌单文件自包含文件体积暴涨、编辑器卡顿图床 URL跨设备可用依赖外部服务、可能失效在线编辑器的常见做法是粘贴图片时自动上传到图床然后把 Markdown 里的路径替换成返回的 URL。如果不想依赖图床可以在导出时把图片转成 Base64 内嵌但只建议对小图这么做。// 粘贴图片时读取文件并转 Base64 插入 editor.dom.addEventListener(paste, async (e) { const items e.clipboardData.items; for (const item of items) { if (item.type.startsWith(image/)) { const file item.getAsFile(); const reader new FileReader(); reader.onload () { const base64 reader.result; const pos editor.state.selection.main.head; editor.dispatch({ changes: { from: pos, insert:  } }); }; reader.readAsDataURL(file); } } });注意Base64 图片会让 Markdown 文件变得很大一篇带十几张图的文档可能超过 10MB。在线编辑器如果自动保存到 IndexedDB大文件会导致保存变慢。建议超过 200KB 的图片走上传流程不内嵌。3.3 数学公式与 Mermaid插件接入的两种方式「markdown数学公式插件」和「markdown preview mermaid support」是在线编辑器拉开差距的地方。数学公式用 KaTeX 比 MathJax 快渲染质量也够。接入方式import markdownItKatex from markdown-it-katex; md.use(markdownItKatex);然后在页面引入 KaTeX 的 CSSlink relstylesheet hrefhttps://cdn.jsdelivr.net/npm/katex0.16.9/dist/katex.min.cssMermaid 的接入稍微麻烦一点因为 Mermaid 是异步渲染的。思路是在 markdown-it 里把mermaid块渲染成div classmermaid然后在预览更新后调用mermaid.run()。import mermaid from mermaid; mermaid.initialize({ startOnLoad: false }); // 自定义 fence 规则 const defaultFence md.renderer.rules.fence; md.renderer.rules.fence (tokens, idx, options, env, self) { const token tokens[idx]; if (token.info.trim() mermaid) { return div classmermaid${token.content}/div; } return defaultFence(tokens, idx, options, env, self); }; // 预览更新后重新渲染 Mermaid async function renderPreview(content) { document.querySelector(#preview).innerHTML md.render(content); await mermaid.run({ querySelector: .mermaid }); }参数说明mermaid.initialize里的startOnLoad: false必须设否则 Mermaid 会在页面加载时自动扫描和我们的手动调用冲突。mermaid.run每次都会重新渲染所有.mermaid元素文档很大时会有性能问题可以只渲染新增的节点。3.4 导出 Word 与 PDF序号自动编号的坑「markdown转word工作流」是很多人的最终交付需求。在线编辑器如果只能预览不能导出价值少一半。导出 Word 的常见做法是把 Markdown 转成 HTML再用html-docx-js或docx库生成.docx。但这里有一个大坑Word 的自动编号和 Markdown 的有序列表是两套逻辑。Markdown 里写1. 第一步 2. 第二步 3. 第三步转成 HTML 是olli第一步/li.../ol。如果直接把这个 HTML 塞进 WordWord 会把它当成普通段落序号是纯文本不会自动编号。如果你在 Word 里删掉中间一项后面的序号不会自动更新。要保留 Word 的自动编号需要在生成 docx 时使用 Word 的 numbering 配置。用docx库的话需要定义numbering配置import { Document, Paragraph, TextRun, Numbering } from docx; const numbering new Numbering({ config: [{ reference: my-numbering, levels: [{ level: 0, format: decimal, text: %1., alignment: start, }], }], }); const doc new Document({ numbering, sections: [{ children: [ new Paragraph({ text: 第一步, numbering: { reference: my-numbering, level: 0 }, }), new Paragraph({ text: 第二步, numbering: { reference: my-numbering, level: 0 }, }), ], }], });这样生成的 Word 文档序号是真正的自动编号删掉一项后面的会自动更新。代价是代码复杂度上升需要把 Markdown 的列表结构解析成对应的 Paragraph 数组。如果只是偶尔导出不想写这么复杂可以用 Pandoc 做服务端转换。Pandoc 对 Markdown 到 docx 的序号处理已经比较成熟但需要后端环境。4. 在线 Markdown 编辑工具避坑5 个血泪教训4.1 换行不生效breaks 参数没开现象在编辑器里写了两行预览时变成一行。原因CommonMark 规范里单个换行是「软换行」渲染成空格。必须空一行才是新段落。解决markdown-it 初始化时设breaks: true。但要注意开了之后所有单换行都变br如果你写的是英文段落可能会觉得行距太密。折中方案是只在中文场景开或者提供开关让用户自己选。4.2 表格粘贴到 Excel 错行单元格里有换行现象复制 Markdown 表格到 Excel本来 3 列的数据变成了 6 列。原因某个单元格里写了多行文本TSV 转换时没有把换行替换掉Excel 把换行当成了新行。解决在tableToTSV里对每个单元格做replace(/\n/g, )。如果单元格内容必须保留换行那就不能用 TSV改用 HTML 格式写剪贴板。4.3 图片路径在别人电脑上打不开用了本地绝对路径现象自己电脑上预览正常发给同事后图片全是裂图。原因Markdown 里写的是或./images/a.png对方没有这个文件。解决在线编辑器应该默认把粘贴的图片转成 Base64 或上传图床。如果用户手动写路径在导出时提示「检测到本地路径是否转为内嵌图片」。4.4 Mermaid 渲染后代码块还在没有替换原始内容现象预览区同时出现了 Mermaid 图表和它的源码。原因自定义 fence 规则时只返回了div classmermaid但 markdown-it 可能还保留了原始 token 的渲染。解决确保md.renderer.rules.fence里对 mermaid 分支直接return不要调用self.renderToken。另外Mermaid 渲染是异步的如果预览更新频繁可能会出现「旧图还没渲染完新内容已经替换」的情况。加一个防抖或者用mermaid.run的 Promise 做队列。4.5 数学公式显示为源码KaTeX CSS 没加载现象$Emc^2$渲染出来还是$Emc^2$没有变成公式。原因markdown-it-katex 只负责生成 HTML 结构真正的排版靠 KaTeX 的 CSS 和字体文件。如果 CSS 没引入或者 CDN 被墙公式就是一堆乱码。解决确保link relstylesheet href...katex.min.css在页面里并且字体文件路径正确。如果面向国内用户建议把 KaTeX 的 CSS 和字体下载到本地不要依赖 CDN。5. 进阶把在线编辑器变成「写完就能发」的交付工具5.1 公众号格式化从 Markdown 到微信后台的一键复制「公众号文章markdown格式化」是一个很实际的需求。微信公众号后台不认 Markdown只认富文本。如果你用在线编辑器写完直接复制预览区的 HTML 到公众号后台样式会丢。一个可行的做法是在预览区渲染时给所有元素加上内联样式。因为公众号后台会过滤style标签但保留style属性。function inlineStyles(html) { const div document.createElement(div); div.innerHTML html; div.querySelectorAll(h1, h2, h3, p, li, blockquote, code, pre).forEach(el { const tag el.tagName.toLowerCase(); const styles { h1: font-size: 24px; font-weight: bold; margin: 20px 0 10px;, h2: font-size: 20px; font-weight: bold; margin: 18px 0 8px;, p: font-size: 16px; line-height: 1.8; margin: 10px 0;, code: background: #f5f5f5; padding: 2px 6px; border-radius: 3px; font-family: monospace;, pre: background: #f5f5f5; padding: 12px; border-radius: 6px; overflow-x: auto;, blockquote: border-left: 4px solid #ddd; padding-left: 12px; color: #666; margin: 10px 0;, }; if (styles[tag]) el.setAttribute(style, styles[tag]); }); return div.innerHTML; }逻辑说明公众号后台会保留style属性但会过滤掉style标签和 class。所以必须把样式内联到每个元素上。代码块还要注意公众号不支持pre里的语法高亮只能保留纯文本。5.2 验证导出效果三个必须检查的点导出功能写完怎么验证它真的能用我一般会检查三个点序号是否自动更新在 Word 里删掉中间一个列表项看后面的序号有没有自动变。如果没变说明用的是纯文本序号需要改 numbering 配置。图片是否内嵌把导出的 docx 发给另一台电脑看图片能不能显示。如果裂图说明图片还是外链。表格是否可编辑在 Word 里点表格看是不是真正的表格对象。如果是图片或纯文本说明转换时丢了结构。5.3 一个我常用的习惯导出前先跑一遍 lintMarkdown 写多了难免有语法错误。比如表格分隔行少了一列、链接括号没闭合、代码块没写语言。这些小问题在预览时可能看不出来但导出后就会暴露。我的习惯是在导出前跑一遍markdownlint。在线编辑器可以集成markdownlint的浏览器版本在预览区上方显示警告。这样用户在导出前就能发现「表格列数不一致」「标题级别跳跃」这类问题减少返工。import markdownlint from markdownlint; import markdownlintRuleHelpers from markdownlint-rule-helpers; const result markdownlint.sync({ strings: { content: editor.state.doc.toString() }, config: { default: true, MD013: false, // 关闭行长度限制在线编辑器不适用 }, }); // result.content 是警告数组渲染到预览区上方这个习惯帮我省了很多「导出后才发现格式乱」的后悔药。在线编辑器如果能在用户点「导出」之前就把问题指出来体验会好很多。希望帮到你。本文还有配套的精品资源点击获取