在实际项目中,Markdown 查看器并不只是“把一个 .md 文件变成网页”这么简单。很多工具打开 Markdown 后,用户看到的仍然是“有样式的源码”,而不是一份适合连续阅读的文档。OpenMD 这个项目名称里已经包含两层意思:Open 代表开放和可二次改造,MD 指向 Markdown;“为阅读而生”则说明它优先解决的不是编辑效率,而是阅读体验。这篇文章会围绕 OpenMD 这类“阅读优先”的 Markdown 查看器,从需求拆解开始,理解查看器与编辑器的差别,再搭建一个最小可运行实现,最后给出排错思路、生产环境优化和可复用检查清单。无论你是想自建本地笔记阅读工具,还是在 Linux 或 Windows 环境里寻找轻量 Markdown 阅读方案,都可以按这条主线落地。
1. 先想清楚:OpenMD 为什么把“阅读”而不是“编辑”放在第一位
1.1 Markdown 查看器与 Markdown 编辑器的本质区别
编辑器的核心是“输入”。它需要处理光标、选区、撤销栈、语法提示、实时预览,用户大多数时间在写内容。查看器的核心是“输出”。它需要处理排版、字号、行距、目录、代码高亮、图片显示和阅读节奏,用户大多数时间在读内容。
很多工具把两种场景混在一起,结果就是编辑区不够灵活,预览区也不够专注。OpenMD 把“查看器”单独拿出来,意味着它放弃了编辑器的复杂交互,克制地把 Markdown 渲染成一份接近于排版成品的文档。
两者的差异可以整理成一张速查表:
| 对比维度 | Markdown 编辑器 | Markdown 查看器 |
|---|---|---|
| 核心目标 | 高效写、改、维护内容 | 清晰读、查、浏览文档 |
| 用户角色 | 作者、维护者 | 读者、审阅者、最终用户 |
| 主要交互 | 输入、补全、快捷键、保存 | 滚动、跳转、缩放、复制 |
| 复杂度重心 | 光标管理、编辑状态、同步滚动 | 渲染安全、排版稳定、主题适配 |
| 典型形态 | 在线编辑器、IDE 插件、笔记软件 | 阅读页、预览面板、静态文档站 |
| 对阅读体验的要求 | 有即可 | 决定产品价值 |
这个区别很关键。如果你在做 OpenMD 这类产品,功能优先级就不是“支持多少快捷键”,而是“打开一份 Markdown 后,用户是否愿意读下去”。
1.2 一个阅读型 Markdown 查看器要做好的四件事
从工程角度看,OpenMD 要解决的核心任务可以拆成四件事。
第一,正确解析 Markdown 语法。标题、段落、列表、引用、代码块、表格、链接、图片都需要按标准语法转换。这里的关键不是“能变成 HTML”,而是不同实现之间的行为差异,比如换行规则、嵌套列表、代码块内保留空格等。
第二,安全渲染结果。Markdown 允许内嵌 HTML,如果直接把解析结果写入页面,就可能执行原始 HTML 中的脚本。查看器必须对渲染结果做过滤,保证普通 Markdown 文档不会对阅读环境构成风险。
第三,优化阅读排版。这是“为阅读而生”的核心。正文宽度、字号、行高、段落间距、标题层级、代码块底色、表格边框、图片最大宽度、暗色模式,这些都要单独设计。
第四,处理扩展能力。文档中经常出现代码块、数学公式、目录、多级标题。代码高亮需要额外的 CSS,公式需要额外插件,目录需要生成锚点。没有这些,技术文档打开后很容易出现代码挤成一团、公式原样显示、目录无法跳转的问题。
1.3 适用场景:本地笔记、Linux 轻量阅读和 Windows 工具选择
OpenMD 类工具的第一批用户通常是这么一群人:写技术笔记的人、维护 README 的人、阅读源码中 Markdown 文档的人,以及不想打开重量级编辑器只看文件内容的人。
在 Linux 环境下,图形化编辑器并不一定齐全,用户可能只需要一个能双击打开 .md 文件的轻量查看器。在 Windows 环境下,用户会先想到“有哪些编辑器支持 Markdown”,但实际很多时候他们只是要“打开别人传过来的文件看一眼”。Sublime 用户通常会装 Markdown Preview 插件,本质上也是把一个 Markdown 查看器嵌进编辑器。这类需求长期存在,说明 Markdown 文件的阅读场景和编辑场景是分离的。
如果你正在阅读一份从网上下载的 .md 文件,第一反应也应该是“怎么打开”,而不是“要不要新建一个项目”。OpenMD 这类工具的存在,就是要把这种打开体验做得像阅读 PDF 一样自然。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备:从零初始化一个可运行的 Markdown 查看器
2.1 技术路线先做取舍:浏览器、Node 还是桌面应用
Markdown 查看器可以用很多技术路线实现,常见的有三类。
第一类是纯 Web 页面。用一个 HTML 页面加载 Markdown 内容,适合部署在本地服务器或作为静态阅读页。优点是实现简单、跨平台,缺点是不能直接读取任意本地文件,需要手动选择文件或通过浏览器 File API 打开。
第二类是 Node 命令行工具。把 Markdown 解析成 HTML 文件或输出到终端,适合做构建流程的一部分,也适合做自动化验证。缺点是不适合普通用户日常阅读。
第三类是桌面应用。用 Electron、Tauri 等框架封装 Web 页面,可以读取本地文件、注册文件关联、双击 .md 文件直接打开。体验最好,但工程成本也最高。
本文先以“本地 Web 应用 + Node 脚本验证”的方式实现最小闭环,不涉及具体的桌面打包方案。这样更利于理解 Markdown 查看器的核心链路,后续如果要封装成 OpenMD 这类桌面工具,再把读取本地文件的能力加进来即可。
2.2 环境检查:Node、npm 和基础依赖
建议先检查本机的 Node 环境。很多 Markdown 解析库的新版本要求较新的 Node 版本,直接运行老环境会出现兼容性问题。
bash复制node -v
npm -v
如果 node -v 返回的版本低于 18,建议先升级 Node,或者把依赖版本锁到与当前环境兼容的版本。不要看到报错才想起来查环境。
核心依赖选择如下:
| 依赖 | 作用 | 说明 |
|---|---|---|
| marked | 把 Markdown 解析为 HTML | 成熟、轻量,支持扩展渲染器 |
| dompurify | 过滤 HTML 中的危险内容 | 配合 jsdom 在 Node 中运行 |
| jsdom | 为 Node 提供 DOM 环境 | 只在 Node 脚本和测试中使用 |
| highlight.js | 代码语法高亮 | 需额外引入主题 CSS |
下面是示例 package.json。版本号并不一定要严格等于这些,安装前建议确认当前可用版本。
json复制{
"name": "openmd-mini",
"version": "0.1.0",
"description": "A minimal Markdown reader focused on reading experience",
"type": "module",
"scripts": {
"build": "node reader-core.js",
"verify": "node verify-render.js"
},
"dependencies": {
"dompurify": "^3.0.0",
"highlight.js": "^11.0.0",
"jsdom": "^24.0.0",
"marked": "^12.0.0"
}
}
创建项目后安装依赖:
bash复制npm init -y
npm install marked dompurify jsdom highlight.js
安装完成后,在项目根目录建立下面几个文件:reader-core.js 负责读取和渲染 Markdown,verify-render.js 负责验证渲染结果,read.css 负责阅读排版,index.html 作为浏览器入口。
2.3 目录结构要先对齐,否则后面配置容易混乱
一个最小可运行的 OpenMD 类查看器,目录可以这样组织:
text复制openmd-mini/
├── node_modules/
├── package.json
├── reader-core.js
├── verify-render.js
├── read.css
└── index.html
不要把渲染逻辑散落到各个页面里。读取、解析、安全过滤、样式这四个部分各管一件事,后续排查问题时才能按层定位。很多项目做到后面越来越难维护,不是因为 Markdown 语法复杂,而是因为读取、解析、渲染和样式全部混在一个文件里。
3. 核心流程:一份 .md 文件如何变成可读页面
3.1 读取本地文件:路径、编码和图片基准
Markdown 查看器第一个要处理的不是渲染,而是读取。如果文件读取失败,后面所有逻辑都不需要执行。
下面这段代码用 Node 的 fs/promises 读取 Markdown 文件,并把文件所在目录作为图片相对路径的基准。
javascript复制// reader-core.js
import fs from 'node:fs/promises';
import path from 'node:path';
async function loadMarkdown(filePath) {
const resolved = path.resolve(filePath);
try {
const content = await fs.readFile(resolved, 'utf8');
return {
content,
base: path.dirname(resolved)
};
} catch (err) {
console.error(`无法读取文件: ${resolved}`);
throw err;
}
}
这里有一个很容易踩的坑:Markdown 文件中的图片路径通常是相对路径。如果图片在 Markdown 文件同目录的 images 文件夹里,而查看器页面却运行在另一个目录,图片就会加载失败。正确做法是把 Markdown 文件所在目录作为基准目录,再去解析图片路径。
另外,文件编码也很关键。大多数 Markdown 文件是 UTF-8 编码,但有些历史文件可能是 GBK。读取时不指定编码,或者文件本身不是 UTF-8,就会出现中文乱码。生产级工具应该在读取前先判断编码,至少要有错误提示,而不是把乱码内容渲染给用户。
3.2 解析与安全过滤:不要直接把 Markdown 输出到 innerHTML
Markdown 标准语法本身是相对安全的,但 Markdown 允许内嵌 HTML。如果某个文档里包含 <img src=x onerror=...> 这类内容,直接写入页面就会触发脚本执行。阅读器不是沙箱,但它至少不应该主动执行文档里未经过滤的脚本。
所以完整的渲染链路是:先用 marked 把 Markdown 解析成 HTML,再用 DOMPurify 过滤 HTML,最后才写入阅读区域。
javascript复制import { marked } from 'marked';
import { JSDOM } from 'jsdom';
import createDOMPurify from 'dompurify';
import hljs from 'highlight.js';
function renderMarkdown(content) {
marked.use({
renderer: {
heading({ tokens, depth }) {
const text = this.parser.parseInline(tokens);
const id = text.trim().toLowerCase().replace(/[^\p{L}\p{N}]+/gu, '-');
return `<h${depth} id="${id}">${text}</h${depth}>`;
},
code({ text, lang }) {
const language = lang && hljs.getLanguage(lang) ? lang : 'plaintext';
const safeLang = language.replace(/[^\w-]/g, '');
const highlighted = hljs.highlight(text, { language: safeLang }).value;
return `<pre><code class="hljs language-${safeLang}">${highlighted}</code></pre>`;
}
}
});
const rawHtml = marked.parse(content);
const window = new JSDOM('').window;
const purify = createDOMPurify(window);
const cleanHtml = purify.sanitize(rawHtml, {
FORBID_TAGS: ['style', 'script'],
FORBID_ATTR: ['onerror', 'onclick']
});
return cleanHtml;
}
代码里的 heading 渲染器同时解决了两件事:给标题生成 id,方便目录锚点跳转;保留标题层级。code 渲染器则把代码块交给 highlight.js 处理。
这里要注意,浏览器环境里 DOMPurify 可以直接使用,不需要 jsdom。上面代码把 jsdom 引入,是为了让同一个渲染逻辑能够在 Node 脚本中被调用,方便做自动化验证。实际页面HTML中,应该使用浏览器端的 DOMPurify,避免多余依赖。
3.3 代码高亮、公式和表格的扩展处理
代码高亮需要两部分:一部分是上面的渲染器,另一部分是 highlight.js 的主题 CSS。很多人在代码里配置了高亮,但页面仍然没有颜色,原因就是没有引入主题样式。
数学公式是另一个容易踩坑的点。标准 Markdown 语法并不包含公式,$...$ 或 \(...\) 默认只会被当作普通文本。要支持公式,需要引入 KaTeX 或 MathJax 这类插件。OpenMD 这类阅读器如果要支持技术文档,公式插件基本是刚需。
表格在标准 Markdown 中已经支持基本语法,但样式需要自己定义。另外,用户经常想把 Markdown 表格复制到 Excel,直接复制 Markdown 表格文本通常无法被 Excel 正确识别。更稳妥的做法是把表格渲染成真正的 HTML 表格,从页面复制到 Excel 时选择带有表格结构的区域,而不是复制源码。
3.4 完整最小渲染函数的使用方式
上面这些函数组合起来,就构成了一个最小渲染闭环:
输入是 Markdown 文本,处理过程是解析、过滤、高亮,输出是干净的 HTML 字符串。
bash复制node -e "import('./reader-core.js').then(async m => { const { content } = await m.loadMarkdown('./README.md'); console.log(m.renderMarkdown(content)); })"
这一步能确认解析链路是否可用。生产项目可以把它封装成命令行工具,也可以接一个本地 HTTP 服务,让浏览器读取接口返回的 HTML。关键在于,读取、解析和渲染必须是独立模块,后续任意一环出错都能单独验证。
4. 阅读体验:从“能渲染”到“适合长时间阅读”
4.1 中文换行、段落间距和阅读密度的常见误区
Markdown 查看器最容易忽略的是中文排版的细节。
先看换行问题。很多用户以为在 Markdown 里按一次 Enter 就会换行,但绝大多数实现中,单个换行会被当成空格处理,只有空一行才会产生新段落。如果内容来自复制粘贴,经常会出现“明明分了行,渲染后却连在一起”的现象。这是 Markdown 语法行为,不是查看器 bug。如果确实要做软换行,需要在一行末尾加两个空格,或者使用反斜杠。
再看阅读密度。阅读型查看器的正文行高建议不小于 1.7,正文字号建议在 16px 到 18px 之间。很多人只是把默认浏览器样式套上去,行高 1.2 的字在长文阅读时非常累。段落之间也要有清晰间距,否则所有文字像一整块墙。
这在中文内容里尤其重要。中文没有词间空格,如果行高和段落间距不足,读者很容易发生跳行。与其追求一屏显示更多内容,不如让每一屏内容都容易被眼睛追踪。
4.2 阅读页核心 CSS:从标题到表格都要有明确规则
下面这段 CSS 可以作为阅读器的底色。它没有引入任何框架,只处理阅读相关的排版规则。
css复制:root {
--reading-width: 46rem;
--text-color: #24292f;
--bg-color: #ffffff;
--code-bg: #f6f8fa;
--border-color: #d0d7de;
--line-height: 1.75;
}
.reader {
max-width: var(--reading-width);
margin: 0 auto;
padding: 2rem 1.25rem;
font-size: 16px;
line-height: var(--line-height);
color: var(--text-color);
background: var(--bg-color);
}
.reader h1 {
font-size: 1.9em;
margin: 1.2em 0 0.5em;
}
.reader h2 {
font-size: 1.5em;
margin: 1.1em 0 0.4em;
padding-bottom: 0.3em;
border-bottom: 1px solid var(--border-color);
}
.reader h3 {
font-size: 1.25em;
margin: 1em 0 0.3em;
}
.reader p {
margin: 0.8em 0;
}
.reader img {
max-width: 100%;
height: auto;
}
.reader pre {
overflow-x: auto;
padding: 1rem;
background: var(--code-bg);
}
.reader code {
font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
font-size: 0.9em;
}
.reader table {
border-collapse: collapse;
width: 100%;
display: block;
overflow-x: auto;
}
.reader th,
.reader td {
border: 1px solid var(--border-color);
padding: 0.5em 0.75em;
}
.reader blockquote {
margin: 1em 0;
padding: 0.5em 1em;
color: #57606a;
border-left: 4px solid var(--border-color);
background: var(--code-bg);
}
@media (prefers-color-scheme: dark) {
:root {
--text-color: #c9d1d9;
--bg-color: #0d1117;
--code-bg: #161b22;
--border-color: #30363d;
}
}
这里有几个设计点值得说明。
正文宽度限制在 46rem,大约 736 像素,是长文阅读比较舒适的宽度。过宽的文字会让视线移动距离变大,过窄又会让换行太频繁。代码块和表格使用 overflow-x: auto,避免长代码或宽表格把整个页面撑破。暗色模式直接用 prefers-color-scheme,可以在系统切换到暗色主题时自动生效。
4.3 自动目录与锚点跳转
长文档必须要有目录。没有目录的 Markdown 阅读器,读者只能从头滚到尾。
前面的 heading 渲染器已经为 h1、h2、h3 设置了 id。目录生成逻辑只需要扫描这些标题,再把它们渲染成链接列表。
javascript复制function buildToc(cleanHtml) {
const template = document.createElement('template');
template.innerHTML = cleanHtml;
const headings = template.content.querySelectorAll('h1, h2, h3');
return [...headings].map((heading) => {
return {
level: heading.tagName,
id: heading.id,
text: heading.textContent
};
});
}
生成目录后,点击链接时跳转目标要对应到标题 id。如果同一个标题在文中出现两次,生成的 id 就会重复,点击可能跳到第一个而不是当前位置。生产级实现需要在渲染阶段对重复 id 做后缀处理,比如第二次出现时变成 id-1、id-2。
注意:不要只看“目录生成出来了”,还要点击每一项目录验证滚动位置是否正确。重复标题是长文档中最常见的问题。
5. 运行验证:用一份测试文档检查查看器是否合格
5.1 准备一份覆盖主要语法的测试文档
很多 Markdown 阅读器在简单文本上表现正常,一到复杂文档就暴露问题。建议准备一份覆盖标题、代码块、表格、图片、公式、链接的测试文档。
下面这份 Markdown 可以直接保存为 test.md:
markdown复制# OpenMD 测试文档
这是一段正文,用来验证 **加粗**、`行内代码` 和 [链接](https://example.com)。
## 代码块
```js
function hello(name) {
return `Hello, ${name}`;
}
```
## 表格
| 功能 | 预期 |
| --- | --- |
| 标题 | 层级清晰 |
| 图片 | 不超出阅读区宽度 |
| 代码 | 高亮并可横向滚动 |
## 公式(需要插件)
行内公式:`\(a^2 + b^2 = c^2\)`
块级公式:
`\[
\int_0^1 x^2 dx = \frac{1}{3}
\]`
这份文档故意同时包含普通文本、代码、表格和公式。如果这些内容都能正常显示,说明查看器已经达到基本可用状态。
5.2 验证步骤与预期结果
运行查看器后,按下面顺序检查。
第一步,检查标题。页面里应该看到从 h1 到 h3 的层级差别,目录区域应该出现对应的跳转链接。
第二步,检查代码块。JavaScript 代码应该有语法高亮,并且代码过长时容器可以横向滚动,不会撑破页面。
第三步,检查表格。表格应该有边框,单元格间距合理,默认状态下不会溢出阅读区。
第四步,检查图片。如果插入了一张很宽的图片,它应该被限制在阅读宽度以内,而不是溢出页面。
第五步,检查公式。在没有安装公式插件时,公式内容正常显示为文本即可;安装了公式插件后,公式应该渲染为数学排版结果。
5.3 用 Node 脚本做基础断言
人工检查容易被遗漏,可以加一个简单的验证脚本做基础断言。
javascript复制// verify-render.js
import { marked } from 'marked';
import { JSDOM } from 'jsdom';
import createDOMPurify from 'dompurify';
const md = `# 标题\n\n正文内容`;
const window = new JSDOM('').window;
const purify = createDOMPurify(window);
const html = purify.sanitize(marked.parse(md));
if (!html.includes('<h1') || !html.includes('标题')) {
throw new Error('标题渲染失败');
}
if (!html.includes('正文内容')) {
throw new Error('正文渲染失败');
}
console.log('基础渲染验证通过');
这个脚本的价值在于把“验证”变成可重复执行的动作。每次修改渲染逻辑后,运行 npm run verify,如果输出 基础渲染验证通过,再继续下一项工作。不要只验证程序能启动,还要验证输入、输出、异常分支和日志是否符合预期。
6. 常见问题排查:先看输入,再看路径,最后看插件顺序
6.1 按事件链路划分排查顺序
Markdown 查看器的排查链路一般可以分成五层:输入文件、读取路径、解析结果、渲染样式、插件资源。
排查时按顺序来,不要一上来就怀疑解析库。
第一层确认输入文件没有损坏,编码是 UTF-8,内容确实是 Markdown 而不是其他文本格式。第二层确认读取路径正确,图片相对路径的基准目录用对了。第三层确认 marked 解析结果是否符合预期,可以先在命令行打印 HTML,看标题、表格、代码块是否生成。第四层确认样式是否生效,重点看 CSS 是否加载、选择器是否覆盖了默认样式。第五层确认插件资源是否加载成功,代码高亮主题、公式插件的脚本和样式都要检查。
6.2 高频问题对照表
下面这张表列出了阅读型 Markdown 查看器最常见的几类问题。
| 问题现象 | 常见原因 | 检查方式 | 处理建议 |
|---|---|---|---|
| 文件打开后中文乱码 | 文件不是 UTF-8 编码 | 用编辑器或 file -I 查看编码 |
读取时按正确编码转码,或提示用户转存为 UTF-8 |
| 图片不显示 | 图片路径相对于查看器页面,而不是 Markdown 文件 | 浏览器开发者工具里查看 img src | 将 Markdown 文件目录设为 base,再拼接图片路径 |
| 公式原样显示 | 没有加载公式插件 | 查看页面中是否有公式插件脚本 | 引入 KaTeX 或 MathJax,并配置 Delimiters |
| 表格没有边框 | CSS 中没有定义表格样式 | 检查 table 元素的 computed style | 为 table、th、td 设置边框和间距 |
| 目录点击后不跳转 | 标题 id 缺失或重复 | 点击后看 URL hash 与目标 id 是否一致 | 渲染标题时自动生成唯一 id |
| 代码块没有高亮 | highlight.js 主题 CSS 未引入 | 检查网络面板和代码块 class | 引入高亮主题 CSS,并确认语言已注册 |
| 单个换行没有生效 | 误把单个 Enter 当成换行 | 查看 Markdown 源码里的行尾 | 空一行分段,或使用两个空格做软换行 |
| 表格复制到 Excel 后错乱 | 直接复制 Markdown 表格源码 | 检查剪贴板内容 | 从渲染后的页面复制表格,或使用 TSV/HTML 转换 |
6.3 一个可复用的排查模板
遇到阅读器问题,可以按下面的模板记录和排查。
text复制现象:页面打开后出现 X
输入:把 Markdown 原文复制到最小测试文件中
期望:应该显示为 Y
实际:显示为 Z
分层检查:
1. 文件读取是否成功
2. marked 生成的 HTML 是否包含关键标签
3. DOMPurify 是否过滤掉了内容
4. CSS 是否覆盖目标元素
5. 插件脚本和样式是否加载
很多问题在“把输入文件尽量缩小”之后就能定位。如果一份长文档有问题,先删掉一半内容继续验证,直到找到最小复现样例。最小复现样例对排查效率的提升远大于一条一条看代码。
7. 继续迭代:从个人工具到长期维护的阅读器
7.1 扩展方向:公式、多主题、护眼模式和离线资源
最小查看器跑通后,下一步不是堆功能,而是围绕“阅读体验”做扩展。
数学公式插件是技术文档的第一优先级。如果你经常阅读论文、算法笔记或理工科内容,没有公式支持的 Markdown 查看器基本不可用。引入 KaTeX 时要注意,公式渲染发生在 DOM 插入之后,否则公式会保持原样。
多主题和护眼模式是阅读型工具的常见需求。建议用 CSS 变量管理颜色体系,这样扩展主题时不需要重写全部样式,只需要替换一组变量。
离线资源也值得提前考虑。很多 Markdown 查看器为了省事,直接从 CDN 加载高亮 CSS 和字体。这在联网环境中没问题,但离线打开本地文档时,页面会因为资源加载失败而变得很丑。本地阅读工具应尽量把字体、CSS、插件脚本打到本地资源中。
7.2 Markdown 转 Word 与自动化工作流
用户对 Markdown 的需求不只是“阅读”,还有“输出”。最常见的诉求是把 Markdown 转成 Word 文档,或者把表格转成 Excel。
直接复制 Markdown 表格到 Excel 很容易失败,因为 Markdown 表格语法本身不是 Excel 能识别的格式。正确做法是先把 Markdown 表格转成 HTML 表格,再从页面复制到 Excel,或者使用 Pandoc 一类的转换工具。
如果要把“Markdown 转 Word”变成自动化工作流,比如用 Coze 这类工作流平台搭建一个转换机器人,重点不是丢给模型直接处理,而是先定好模板:标题层级怎么映射、代码块是否保留、表格是否转成原生 Word 表格。模板定了,自动化流程才有稳定输出。
建议:查看器可以专注阅读,转换工作交给专门工具。不要把转换逻辑塞进阅读器的渲染函数里,否则两个需求都会变得不清晰。
7.3 发布前检查清单和练习建议
如果你要把自己实现的 Markdown 查看器发布出去,发布前检查清单至少应该包含这些内容:
- 确认输入文件编码按 UTF-8 处理,异常编码有明确提示。
- 确认图片相对路径基于 Markdown 文件目录,而不是页面目录。
- 确认渲染结果经过 DOMPurify 过滤,禁止原始脚本直接执行。
- 确认标题 id 唯一,目录跳转正常。
- 确认代码高亮主题 CSS 已引入,代码块支持横向滚动。
- 确认表格有边框,宽表格不会撑破页面。
- 确认公式插件按需加载,不加载时不会影响文章主体阅读。
- 确认暗色模式能自动生效,图片底色和文字颜色没有冲突。
- 确认离线资源可访问,不依赖外网 CDN。
- 确认长文档打开和滚动流畅,至少用一份几百 KB 的 Markdown 文件压测。
练习建议是循序渐进的。第一次只做“解析加显示”,把 test.md 渲染成完整页面。第二次加入安全过滤、代码高亮和目录。第三次加入暗色模式和公式插件。第四次再考虑桌面打包。
如果只能记住一句话:查看器的价值不在解析,而在阅读。把 Markdown 正确渲染出来只是起点,让一篇长文在屏幕前能被流畅读完,才是 OpenMD 这类产品真正要解决的问题。下一步建议拿一份自己的笔记,按文中的最小项目跑一遍,然后尝试加入目录、公式和暗色主题。你会发现,阅读体验是一层层调出来的,而不是解析库能直接给的。
