Code前端首页关于Code前端联系我们

Vue2 项目里怎么高效用 Markdown?从渲染到编辑器全流程解析

terry 5小时前 阅读数 7 #Vue
文章标签 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 为例)

  1. 装高亮库和样式:

    npm install highlight.js --save
  2. 在 Vue 项目里全局引入样式(比如选 atom-one-light 主题):

    // main.js
    import 'highlight.js/styles/atom-one-light.css'
  3. 让 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>`
    }
  4. 页面渲染后,触发高亮:
    因为 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 的结果,就在 mountedupdated 里执行高亮:

    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 内容按段落(或换行)拆成数组,用 setTimeoutrequestIdleCallback 分批转 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 组件。

  1. 自定义 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前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门