找回密码
 立即注册
首页 业界区 业界 不用 Typora 的 html 导出功能,手搓纯 HTML5 转换器 ...

不用 Typora 的 html 导出功能,手搓纯 HTML5 转换器

荡俊屯 昨天 22:30
不用 Typora 的 html 导出功能,手搓纯 HTML5 转换器

原创 夏群林 2025.12.23
一、缘起

我日常工作使用 Typora, 一款很好的 Markdown 编辑器。建网站,写博文,用 Typora 打底稿。然后导出成 html 格式文件,所见即所得,一个静态网站就成了!
不过,Typora 自带的 HTML 导出功能存在核心缺陷:夹带 Typora  编辑器 UI 冗余代码、HTML 语义不合规、依赖非标准化样式体系、导出文件体积大、可维护性差。基于此,我们手搓纯HTML5转换器,核心目标:

  • 剔除 UI 冗余,仅保留 Markdown 内容渲染逻辑;
  • 纯 ES6+ 原生实现,无第三方库依赖;
  • 符合HTML5语义标准;
  • 还原 Typora 纯 Markdown 内容的渲染风格。
二、纯 ES6+ 实现,无第三方库

2.1 核心优势


  • 轻量化:产物仅包含核心业务逻辑,无冗余依赖代码,体积较 Typora 导出缩减 80% 以上;
  • 可控性:全流程掌控 Markdown 语法解析、HTML 重构、样式生成;
  • 无依赖风险:避免第三方库版本迭代、兼容性问题,转换器长期稳定可用。
2.2 ES6+ 核心特性应用

ES6+特性应用场景价值模块化(ES Module)拆分解析、工具、入口模块解耦代码,便于维护模板字符串HTML/CSS生成替代字符串拼接,提升可读性正则表达式增强Markdown解析、合规修复精准匹配语法,解决反向引用冲突三、整体架构设计

3.1 目录结构
  1. md2html/
  2. ├── index.html        // 交互界面(MD输入、HTML预览/导出)
  3. ├── js/
  4. │   ├── app.js        // 入口(DOM操作、转换/下载逻辑)
  5. │   ├── utils.js      // 工具函数(格式化、提示)
  6. │   └── marked.es6.js // MD核心解析(合规修复核心)
  7. └── css/  
  8.     ├── style.css     // 转换器界面样式
  9.     ├── concise.css   // 简洁版MD渲染样式
  10.     └── typora.css    // Typora纯内容渲染样式
复制代码
3.2 核心流程

flowchart TD    A[MD输入] --> B[marked.es6.js解析(合规修复)]    B --> C[utils.js格式化]    C --> D[app.js生成HTML/下载]    D --> E[预览/导出纯HTML5文件]四、核心技术点

4.1 基于正则的 Markdown 语法解析

放弃第三方库,通过精准的正则表达式匹配Markdown核心语法,是实现“纯原生”的基础。
核心思路为:按“块级语法(标题、列表、表格)→ 行内语法(加粗、链接、代码)”的顺序解析,确保语法嵌套的正确性。
示例:表格语法解析正则
  1. // 解析表格
  2. const parseTables = (md, options) => {
  3.     if (!md.includes('|') || md.includes('<table>')) return md;
  4.     const tableRegex = /^(\|.*\|)\n(\|[-:| ]*\|)\n((?:\|.*\|\n?)+)/gm;
  5.     return md.replace(tableRegex, (match, headerLine, separatorLine, bodyLines) => {
  6.         const alignments = separatorLine.split('|')
  7.             .filter(cell => cell.trim() !== '')
  8.             .map(cell => {
  9.                 const trimCell = cell.trim();
  10.                 return trimCell.startsWith(':') && trimCell.endsWith(':') ? 'center' :
  11.                        trimCell.startsWith(':') ? 'left' :
  12.                        trimCell.endsWith(':') ? 'right' : 'left';
  13.             });
  14.         const headerCells = headerLine.split('|').filter(cell => cell.trim() !== '');
  15.         let headerHtml = '<thead><tr>';
  16.         headerCells.forEach((cell, index) => {
  17.             const align = alignments[index] || 'left';
  18.             const inlineContent = parseInlineOnly(cell, options);
  19.             headerHtml += `<th >${inlineContent}</th>`;
  20.         });
  21.         headerHtml += '</tr></thead>';
  22.         const bodyRows = bodyLines.split('\n').filter(row => row.trim() !== '');
  23.         let bodyHtml = '<tbody>';
  24.         bodyRows.forEach(row => {
  25.             const cells = row.split('|').filter(cell => cell.trim() !== '');
  26.             bodyHtml += '<tr>';
  27.             cells.forEach((cell, index) => {
  28.                 const align = alignments[index] || 'left';
  29.                 const inlineContent = parseInlineOnly(cell, options);
  30.                 bodyHtml += `<td >${inlineContent}</td>`;
  31.             });
  32.             bodyHtml += '</tr>';
  33.         });
  34.         bodyHtml += '</tbody>';
  35.         return `<table>${headerHtml}${bodyHtml}</table>`;
  36.     });
  37. };
复制代码
4.2 HTML语义合规化重构


例如,Typora 导出的<ul>/<ol>被
包裹,换行生成空标签,违反HTML语义规范。
核心逻辑:解析列表时仅生成子元素,段落解析排除列表标签,避免误判。
  1. // marked.es6.js 核心修复代码
  2. const parseLists = (md) => {
  3.     // 生成纯<li>,移除多余换行
  4.     let listItems = md.replace(/^( {0,3})(-|\*|\+)\s+(.*?$)/gm, (_, indent, marker, text) => {
  5.         const inlineContent = parseInlineOnly(text).replace(/\n+/g, ' ');
  6.         return `${indent}<li>${inlineContent}</li>`;
  7.     });
  8.     // 包裹列表容器,清理换行干扰
  9.     return listItems.replace(/((?: {0,3}<li>[\s\S]*?<\/li>\s*)+)/gm, (_, items) => {
  10.         const cleanItems = items.replace(/\n+/g, '').replace(/>\s+</g, '><');
  11.         return /[-*+]/.test(_) ? `<ul>${cleanItems}</ul>` : `<ol>${cleanItems}</ol>`;
  12.     });
  13. };
  14. // 段落解析排除列表标签,避免空<p>
  15. const parseParagraphs = (md) => {
  16.     const paraRegex = /^(?!<(ul|ol|li)>)(?!<\/(ul|ol|li)>)([^<\n]+?)(?=\n{2,}|$)/gm;
  17.     return md.replace(paraRegex, (_, __, ___, content) => {
  18.         const trimmed = content?.trim().replace(/\n+/g, ' ') || '';
  19.         return trimmed ? `<p>${trimmed}</p>` : '';
  20.     });
  21. };
复制代码
4.3 仅保留 Typora  内容渲染核心样式

Typora 导出样式包含编辑器 UI 规则,冗余且非标准化,予以剥离。基于Typora原生的Markdown渲染风格,构建模块化的CSS体系:
  1. /* typora.css 核心代码 */
  2. :root {
  3.     --typora-text-color: #333;
  4.     --typora-heading-color: #2c3e50;
  5.     --typora-code-bg: #f8f8f8;
  6.     --typora-table-border: #ddd;
  7. }
  8. /* 暗黑模式适配 */
  9. @media (prefers-color-scheme: dark) {
  10.     :root {
  11.         --typora-text-color: #e0e0e0;
  12.         --typora-code-bg: #2d2d2d;
  13.         --typora-table-border: #444;
  14.     }
  15. }
  16. /* 列表合规兜底 */
  17. ul>li, ol>li { margin: 0.4em 0; }
  18. ul>br, ol>br, ul>p, ol>p { display: none; }
  19. /* 表格对齐类 */
  20. .text-left { text-align: left !important; }
  21. .text-center { text-align: center !important; }
  22. .text-right { text-align: right !important; }
复制代码
4.4 原生 ES6+ 模块化整合
  1. // app.js 核心逻辑
  2. import { parseMarkdown } from './marked.es6.js';
  3. import { formatHtml, showAlert } from './utils.js';
  4. // 转换逻辑
  5. dom.convertBtn.addEventListener('click', () => {
  6.     const mdContent = dom.mdInput.value.trim();
  7.     if (!mdContent) return showAlert('请输入Markdown内容');
  8.    
  9.     // 核心流程:解析(合规)→ 格式化 → 预览/导出
  10.     const htmlFragment = parseMarkdown(mdContent); // 生成即合规
  11.     const finalHtml = formatHtml(`<!DOCTYPE html>
  12. <html lang="zh-CN">
  13. <head><meta charset="UTF-8"><link rel="stylesheet" href="./css/typora.css"></head>
  14. <body>${htmlFragment}</body></html>`);
  15.    
  16.     dom.preview.innerHTML = htmlFragment;
  17.     dom.htmlCode.value = finalHtml;
  18.     dom.downloadBtn.disabled = false;
  19. });
复制代码
五、使用说明


  • 按指定目录结构存放文件,确保js/和css/子文件夹路径正确;
  • 用支持ES6模块的浏览器(Chrome/Firefox/Edge)打开index.html;
  • 输入Markdown内容,勾选“保留 Typora 原格式”,点击“转换为 HTML5”;
  • 预览区查看效果,点击“下载 HTML 文件”获取符合 HTML5 规范的文件。
六、总结


  • 核心价值:精准解决 Typora 导出的编辑器UI样式冗余痛点,生成纯内容的符合 HTML5 规范的文件;
  • 技术亮点:纯 ES6+ 原生实现;
  • 优势:轻量化、可控性强、跨环境兼容,可直接部署使用。
本方案源代码开源,按照 MIT 协议许可。地址: xiaql/md2html5: A typora to pure html5 converter

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册