
在 Vue2 项目中实现 MD 文档转 Word,核心思路是解析 MD 内容为结构化数据 → 基于结构化数据构建 Word 文档(docx 格式) → 前端下载生成的 Word 文件。以下是完整的实现方案:
需要安装以下 npm 包完成核心功能:
marked | |
highlight.js | |
docx | |
file-saver | |
@types/marked |
// # npmnpm install marked highlight.js docx file-saver --save// # yarnyarn add marked highlight.js docx file-savermarked 支持将 MD 解析为 Token(而非直接转 HTML),基于 Token 构建 Word 文档更可控(避免 HTML 转 Word 丢失样式)。
新建 src/utils/mdParser.js:
import marked from'marked';import hljs from'highlight.js';// 初始化 marked 配置(开启代码高亮、换行等)marked.setOptions({highlight: (code, lang) => {// 代码块语法高亮处理if (lang && hljs.getLanguage(lang)) {return hljs.highlight(code, { language: lang }).value; }return hljs.highlightAuto(code).value; },breaks: true, // 换行符转换为 <br>gfm: true, // 支持 GFM 规范(表格、删除线等)});/** * 解析 MD 内容为结构化 Token * @param {string} mdContent MD 文本内容 * @returns {Array} 结构化 Token 数组 */exportconstparseMdToTokens = (mdContent) => {if (!mdContent) return [];// 解析 MD 为 Token(marked.lexer 直接返回 Token 数组)const tokens = marked.lexer(mdContent);// 过滤空 Token,简化后续处理return tokens.filter(token => token.type !== 'space');};docx 库通过构建 Document/Paragraph/Table 等对象生成 Word 文档,需根据 MD Token 类型(标题、段落、代码块、表格等)映射为对应的 Word 元素。
新建 src/utils/wordGenerator.js:
import { Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell, WidthType, HeadingLevel } from'docx';import { saveAs } from'file-saver';/** * 根据 MD Token 构建 Word 文档内容 * @param {Array} tokens MD 解析后的 Token 数组 * @returns {Document} docx 文档对象 */constbuildWordContent = (tokens) => {const doc = newDocument({title: 'MD 转换的 Word 文档',creator: 'Vue2 MD2Word',styles: {default: {paragraph: {spacing: {after: { size: 100 }, // 段落间距 }, }, }, }, });// 遍历 Token 构建 Word 元素 tokens.forEach(token => {switch (token.type) {// 标题(h1-h6)case'heading': {const headingLevel = HeadingLevel[`HEADING_${token.depth}`] || HeadingLevel.HEADING_1;const titlePara = newParagraph({text: token.text,heading: headingLevel,spacing: { after: { size: 200 } }, // 标题下方间距 }); doc.addParagraph(titlePara);break; }// 普通段落case'paragraph': {const para = newParagraph({children: [newTextRun({text: token.text,size: 22, // 字体大小(1pt=20 单位) }), ], }); doc.addParagraph(para);break; }// 代码块case'code': {const codePara = newParagraph({children: [newTextRun({text: token.text,font: 'Consolas', // 代码字体size: 20,color: '#333333', }), ],shading: { color: '#f5f5f5' }, // 代码块背景色 }); doc.addParagraph(codePara);break; }// 表格(GFM 表格)case'table': {const table = newTable({rows: token.rows.map((row, rowIndex) => {returnnewTableRow({children: row.map(cell => {returnnewTableCell({children: [newParagraph({text: cell,// 表头样式 ...(rowIndex === 0 ? {shading: { color: '#e9ecef' },bold: true, } : {}), }), ],width: { size: 100 / token.rows[0].length, type: WidthType.PERCENTAGE }, }); }), }); }), }); doc.addTable(table);break; }// 无序列表项case'list_item': {const listPara = newParagraph({text: `• ${token.text}`, // 手动添加列表符号spacing: { before: { size: 50 } }, }); doc.addParagraph(listPara);break; }// 其他类型(如换行、分隔线等),可按需扩展default: {if (token.text) { doc.addParagraph(newParagraph(token.text)); }break; } } });return doc;};/** * 生成并下载 Word 文件 * @param {Array} tokens MD Token 数组 * @param {string} fileName 下载的文件名(不含后缀) */exportconstgenerateWordFromMdTokens = async (tokens, fileName = 'md2word') => {try {const doc = buildWordContent(tokens);// 将 docx 文档打包为 Blobconst blob = awaitPacker.toBlob(doc);// 下载文件saveAs(blob, `${fileName}.docx`);returntrue; } catch (error) {console.error('生成 Word 失败:', error);returnfalse; }};在 Vue2 组件中实现「上传 MD 文件 → 读取内容 → 解析 Token → 生成 Word → 下载」的完整流程。
新建 src/components/MD2Word.vue:
<template> <div class="md2word-container"> <h3>MD 转 Word 工具</h3> <!-- 上传 MD 文件 --> <input type="file" accept=".md,.markdown" @change="handleFileUpload" class="file-input" /> <!-- 手动输入 MD 内容(可选) --> <textarea v-model="mdContent" placeholder="或直接粘贴 MD 内容..." class="md-textarea" ></textarea> <!-- 转换按钮 --> <button @click="handleConvert" :disabled="!mdContent"> 转换为 Word </button> </div></template><script>import { parseMdToTokens } from '@/utils/mdParser';import { generateWordFromMdTokens } from '@/utils/wordGenerator';export default { name: 'MD2Word', data() { return { mdContent: '', // MD 文本内容 }; }, methods: { // 处理 MD 文件上传 handleFileUpload(e) { const file = e.target.files[0]; if (!file) return; // 读取文件内容 const reader = new FileReader(); reader.onload = (event) => { this.mdContent = event.target.result; // 清空文件选择框(避免重复选择同一文件不触发 change) e.target.value = ''; }; reader.readAsText(file, 'utf-8'); }, // 转换 MD 为 Word async handleConvert() { // 1. 解析 MD 为 Token const tokens = parseMdToTokens(this.mdContent); // 2. 生成并下载 Word const success = await generateWordFromMdTokens(tokens); if (success) { this.$message?.success('Word 生成成功!'); // 若有 ElementUI 可提示 } else { this.$message?.error('Word 生成失败,请检查 MD 格式!'); } }, },};</script><style scoped>.md2word-container { padding: 20px; max-width: 800px; margin: 0 auto;}.file-input { margin-bottom: 15px;}.md-textarea { width: 100%; height: 300px; padding: 10px; border: 1px solid #ddd; border-radius: 4px; margin-bottom: 15px; resize: vertical;}button { padding: 8px 20px; background: #409eff; color: white; border: none; border-radius: 4px; cursor: pointer;}button:disabled { background: #ccc; cursor: not-allowed;}</style>上述示例仅处理了标题、段落、代码块、表格、无序列表,可扩展支持:
token.type === 'list' 且 token.ordered,生成带数字的列表项;image 类型 Token,通过 docx 的 ImageRun 插入图片;link 类型 Token,生成带超链接的 TextRun;strong/em 类型 Token,设置 bold/italic 属性。TextRun 的 font 属性设置(如 '微软雅黑''Arial');color 属性设置 16 进制颜色(如 '#ff0000');Paragraph 的 spacing 属性调整。若生成的 Word 中中文乱码,需确保:
docx 库版本 ≥ 7.0.0(新版本对中文支持更好);'微软雅黑''宋体')。若 MD 文件过大,可优化:
App.vue);.md 文件或手动粘贴 MD 内容;该方案纯前端实现 MD 转 Word,无需后端参与,适配 Vue2 生态,可根据实际需求扩展格式支持和样式定制。
更多详细内容,请微信搜索“前端爱好者“, 戳我 查看 。