Vue2 项目里怎么高效用 Markdown?从渲染到编辑器全流程解析
做前端项目时,不少同学在 Vue2 技术栈里碰到 Markdown 相关需求——比如写博客要渲染文章、做后台系统要做 Markdown 编辑器、搞组件库文档要嵌入交互 Demo……但从“把 Markdown 转成网页”到“做个好用的编辑器”,再到“优化性能和 SEO”,每个环节都有不少细节要踩坑,这篇文章用问答形式,把 Vue2 + Markdown 从基础到进阶的实操要点拆明白,帮你少走弯路。
Vue2 里怎么把 Markdown 文本渲染成网页内容?
想在页面上展示 Markdown 写的文档、文章,核心是把 .md
格式的纯文本转换成 HTML,Vue2 项目里常用这三种思路:
用现成的 Vue 组件(如 vue-markdown
)
这类组件把 Markdown 解析和 Vue 的响应式结合得很丝滑,以 vue-markdown
为例:
-
第一步:装依赖
npm install vue-markdown --save
-
第二步:在组件里引入 + 使用
<template> <div> <vue-markdown :source="articleMd"></vue-markdown> </div> </template> <script> import VueMarkdown from 'vue-markdown' export default { components: { VueMarkdown }, data() { return { articleMd: `# 我的第一篇 Vue 博客 这里用 **Markdown** 写内容,列表: - 功能 1 - 功能 2` } } } </script>
它会自动把
source
里的 Markdown 转成带样式的 HTML,如果需要自定义(比如代码高亮、链接跳转规则),可以传options
参数,比如关闭自动转义:<vue-markdown :source="md" :options="{ escapes: { block: [] } }"></vue-markdown>
。
手动用解析库(如 marked.js
)+ v-html
如果想更灵活控制解析过程,用 marked.js
自己处理更自由。
-
装依赖:
npm install marked --save
-
组件里解析 + 渲染:
<template> <div v-html="renderedHtml"></div> </template> <script> import marked from 'marked' export default { data() { return { rawMd: '# 标题\n内容' } }, computed: { renderedHtml() { // 自定义解析规则,比如给链接加 target="_blank" const renderer = new marked.Renderer() renderer.link = function(href, title, text) { return `<a href="${href}" title="${title}" target="_blank">${text}</a>` } return marked(this.rawMd, { renderer }) } } } </script>
但注意:
v-html
直接渲染 HTML 有 XSS 风险,生产环境要对内容过滤(比如用DOMPurify
库清洗恶意脚本)。
用 webpack loader(如 vue-markdown-loader
)
如果是写静态文档(比如组件库说明),可以在构建时把 .md
文件转成 Vue 组件。
-
装 loader:
npm install vue-markdown-loader --save-dev
-
配 webpack(或 vue-cli 的
chainWebpack
):// vue.config.js module.exports = { chainWebpack: config => { config.module .rule('md') .test(/\.md$/) .use('vue-loader') .loader('vue-loader') .end() .use('vue-markdown-loader') .loader('vue-markdown-loader') .options({ // 配置:比如启用代码高亮 highlight: code => require('highlight.js').highlightAuto(code).value }) } }
-
然后在项目里直接导入
.md
文件当组件用:<template> <div> <MyDoc /> </div> </template> <script> import MyDoc from './docs/guide.md' export default { components: { MyDoc } } </script>
这种方式适合静态内容,构建时就把 Markdown 转成 HTML,页面加载更快。
Vue2 项目需要 Markdown 编辑器,选哪个插件方便?
场景不同,选的编辑器也不一样,从“轻量写文档”到“复杂富文本 + Markdown”,推荐这几个常用选项:
mavon-editor
:轻量又全能
个人博客、小后台系统选它准没错,功能覆盖实时预览、大纲导航、代码高亮、图片上传,而且文档特别全。
-
快速上手:
npm install mavon-editor --save
<template> <mavon-editor v-model="articleContent" :toolbars="customToolbars"></mavon-editor> </template> <script> import { mavonEditor } from 'mavon-editor' import 'mavon-editor/dist/css/index.css' // 引入默认样式 export default { components: { mavonEditor }, data() { return { articleContent: '', customToolbars: { bold: true, // 加粗 italic: true, // 斜体 header: true, // 标题 image: true // 图片上传(要自己写上传逻辑) } } } } </script>
图片上传得自己处理:通过
@image-upload
事件,把用户选的文件传给后端,拿到 URL 后替换到 Markdown 里。<mavon-editor @image-upload="handleImageUpload" ></mavon-editor> methods: { async handleImageUpload(file, done) { // 1. 调后端接口传 file const res = await uploadFileApi(file) // 2. done 回调把图片 URL 插入编辑器 done(res.data.imgUrl) } }
TOAST UI Editor:功能强大但略重
如果要做多人协作、版本管理、复杂表格这类高级功能,选 TOAST UI Editor(以前叫 tui-editor),它支持 Markdown 和富文本双模式切换,生态成熟。
-
安装 + 基本使用:
npm install @toast-ui/vue-editor --save
<template> <tui-editor :initialValue="initMd" :options="{ previewStyle: 'vertical' }" ></tui-editor> </template> <script> import { Editor } from '@toast-ui/vue-editor' import '@toast-ui/editor/dist/toastui-editor.css' // 核心样式 import '@toast-ui/editor/dist/toastui-editor-preview.css' // 预览样式 export default { components: { 'tui-editor': Editor }, data() { return { initMd: '# 标题\n内容' } } } </script>
缺点是体积大(打包后增加几百 KB),如果项目对体积敏感,优先选
mavon-editor
。
vue2-editormd
:对程序员友好
基于经典的 editormd
封装,内置代码高亮、流程图、时序图支持,适合写技术文档、接口说明。
- 用法类似上面的库,装依赖后引入组件,配置工具栏,它的优势是代码块功能特别全,支持一键复制代码、切换主题。
Markdown 渲染后的代码块怎么实现高亮?
代码块高亮是技术类博客、文档的刚需,核心逻辑是:让 Markdown 转成的代码块 HTML,能被高亮库识别并染色。
步骤拆解(以 highlight.js
+ vue-markdown
为例)
-
装高亮库和样式:
npm install highlight.js --save
-
在 Vue 项目里全局引入样式(比如选
atom-one-light
主题):// main.js import 'highlight.js/styles/atom-one-light.css'
-
让 Markdown 渲染器给代码块加“语言标识”:
用vue-markdown
时,默认会给代码块加class="language-xxx"
(xxx 是代码语言,js、html),如果用marked.js
,要手动配置 renderer:import marked from 'marked' const renderer = new marked.Renderer() renderer.code = function(code, lang) { // lang 是代码块的语言(```js 里的 js) return `<pre><code class="language-${lang}">${code}</code></pre>` }
-
页面渲染后,触发高亮:
因为 Markdown 转 HTML 是异步的,所以要等 DOM 更新后再执行高亮,用vue-markdown
时,可以监听@rendered
事件:<vue-markdown :source="md" @rendered="handleHighlight" ></vue-markdown> import hljs from 'highlight.js' methods: { handleHighlight() { // 找到所有代码块,执行高亮 document.querySelectorAll('pre code').forEach(block => { hljs.highlightElement(block) }) } }
如果是用
v-html
渲染marked.js
的结果,就在mounted
或updated
里执行高亮:mounted() { this.$nextTick(() => { this.highlightCode() }) }, methods: { highlightCode() { document.querySelectorAll('pre code').forEach(block => { hljs.highlightElement(block) }) } }
其他高亮库对比
highlight.js
:开箱即用,支持 100 + 语言,有几十种主题,适合快速实现。prism.js
:更轻量,支持按需加载语言和主题,但需要手动注册语言(比如只需要 js 高亮,就只装prism-js
)。
Vue2 中怎么给 Markdown 内容自定义样式?
默认的 Markdown 渲染样式可能和项目 UI 不搭,得改样式,分两种思路:
直接改渲染后的 HTML 元素样式
Markdown 转 HTML 后,结构是固定的(比如标题是 h1 - h6,列表是 ul>li,代码块是 pre>code),可以在 Vue 的样式里写:
<style scoped> /* 作用域样式,用 /deep/ 穿透 */ /deep/ h1 { color: #2f54eb; border-bottom: 1px solid #eee; padding-bottom: 8px; } /deep/ p { line-height: 1.8; margin: 16px 0; } /deep/ pre { background: #f7fafc; padding: 16px; border-radius: 4px; } </style>
但要注意:scoped
样式里的 /deep/
(或 >>>
)是为了让样式穿透到子组件(这里是 Markdown 渲染出的 HTML),如果不用 scoped
,直接写全局样式也能生效,但要注意命名冲突。
自定义 Markdown 渲染器,给元素加 class
如果想更精细控制(比如不同场景的 h1 用不同 class),可以在解析 Markdown 时,给 HTML 元素插自定义 class,以 marked.js
为例:
import marked from 'marked' // 自定义 renderer,给 h2 加 class="article-subheading" const renderer = new marked.Renderer() renderer.heading = function(text, level) { const className = level === 2 ? 'article-subheading' : '' return `<h${level} class="${className}">${text}</h${level}>` } // 渲染时用这个 renderer const html = marked(markdownContent, { renderer })
然后在样式里写:
.article-subheading { color: #666; font-size: 24px; margin: 24px 0 16px; }
这种方式适合做主题切换、多场景复用(比如博客详情和列表预览用不同标题样式)。
大型 Vue2 项目中,大量 Markdown 渲染怎么优化性能?
如果页面要渲染几万字的 Markdown(比如长文档、技术手册),直接转 HTML 会阻塞主线程,导致页面卡,这时候得做性能优化:
分片渲染:把大文本拆成小块分批渲染
原理:把 Markdown 内容按段落(或换行)拆成数组,用 setTimeout
或 requestIdleCallback
分批转 HTML + 渲染,让浏览器有时间处理动画、事件等。
示例逻辑:
<template> <div ref="mdContainer"></div> </template> <script> import marked from 'marked' export default { data() { return { bigMd: '...(几万字内容)...' } }, mounted() { this.splitAndRender() }, methods: { splitAndRender() { const chunks = this.bigMd.split('\n\n') // 按空行拆分成段落 const container = this.$refs.mdContainer let index = 0 const renderChunk = () => { if (index >= chunks.length) return // 渲染当前块 const html = marked(chunks[index]) container.innerHTML += html index++ // 下一帧再渲染下一块(给浏览器喘息时间) requestAnimationFrame(renderChunk) } renderChunk() } } } </script>
服务端渲染(SSR)+ 静态生成
如果用 Nuxt.js 这类 SSR 框架,可以把 Markdown 转 HTML 的过程放到服务端,这样客户端拿到的是现成的 HTML,减少前端计算压力。
Nuxt.js 项目里,在 asyncData
里处理 Markdown:
// pages/article/_id.vue export default { async asyncData({ params }) { const mdContent = await import(`~/content/articles/${params.id}.md`) const html = marked(mdContent.default) return { html } } }
然后在模板里直接渲染:
<template> <div v-html="html"></div> </template>
缓存:避免重复解析相同内容
如果多个页面用到相同的 Markdown 片段(比如公共文档),可以用 Vuex 或全局对象缓存解析后的 HTML:
// utils/mdCache.js const cache = new Map() export function getCachedHtml(mdKey, mdContent) { if (cache.has(mdKey)) { return cache.get(mdKey) } const html = marked(mdContent) cache.set(mdKey, html) return html }
组件里用:
import { getCachedHtml } from '@/utils/mdCache.js' export default { computed: { renderedHtml() { return getCachedHtml('doc-guide', this.mdContent) } } }
虚拟滚动:只渲染可视区域内容
Markdown 转成的 HTML 特别长,结合虚拟滚动库(如 vue-virtual-scroll-list
),只渲染用户能看到的部分,原理是计算滚动位置,动态加载对应区域的 HTML 片段。
Markdown 和 Vue 组件怎么结合?比如在 Markdown 里嵌入 Vue 组件?
场景很常见:写技术文档时,想在 Markdown 里插个“在线 Demo 组件”(比如点按钮展示效果),或者插个图表组件,实现思路是自定义 Markdown 解析规则,识别特定标记并替换成 Vue 组件。
步骤拆解(以 marked.js
+ 动态组件为例)
假设我们约定:用 ::: component-name props={...} :::
这种语法表示要插入 Vue 组件。
- 自定义
marked.js
的 renderer,解析特殊语法:import marked from 'marked'
const renderer = new marked.Renderer() // 重写 paragraph 方法,识别以 ::: 开头的行 renderer.paragraph = function(text) { if (text.startsWith(':::')) { // 匹配语法:::: MyButton props={label:"点我"} ::: const match = text.match(/::: (\w+) props=({.*})
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。