使用Webpack解析Markdown文件

本文介绍了如何使用Webpack解析Markdown文件,以便在Vue项目中同时显示代码和效果。通过自定义loader,实现了对`container`标记的支持,利用`fence.js`扩展渲染策略,并在`index.js`中集成代码展示。借助Markdown的灵活性,简化前端组件库的文档编写与展示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

扒一扒 Element UI 中对于 md 文件的编译,来实现在页面上同时展示代码和效果。

传送门

Github地址

M-UI

搭建一个简单的前端组件库

可能有人要问,什么是 Markdown 文件(传送门:Markdown 教程)?为什么这里要讲到如何解析 Markdown 文件?我们接着往下看:

Markdown 编写的文档可以导出 HTML 、Word、图像、PDF、Epub 等多种格式的文档。

注意,Markdown 可以导出 HTML ,我们在网页中需要展示代码,如果我们自己往文件中填入 pre、code 内容,需要消耗大量时间和代码,而 Markdown 文件已经提供了这种转换,我们只需要在其基础上做进一步的解析,将其与 Vue 内容融合在一起,就能完美展示。而且 Vue 也已经有了支持 Markdown 转换的 loader -> Vue-markdown-loader

npm install vue-markdown-loader -D
// rules
// 解析md文件我们需要在 Webpack 的配置汇总加上如下规则
{
   
   
    test: /\.md$/,
    loader: 'vue-markdown-loader'
}

此处对于 Vue-markdown-loader 只做简单介绍,详情请参考 npm/vue-markdown-loader,因为笔者最后使用的不是 Vue-markdown-loader,而是参照 Element UI 自定义的loader实现的。

ELement UI 源码传送门

Element UI - Github

md-loader

在看代码的时候,我们先了解一下用到的插件及用途(个人理解,如有不准确的地方欢迎指正):

markdown-it // 将文本解析为数据 
markdown-it-chain // 配置化 markdown-it
markdown-it-anchor // 为各级标题添加锚点
markdown-it-container // 用于创建自定义的块级容器
transliteration // 中文转拼音
cheerio // 服务器版jQuery
highlight.js // 代码块高亮实现
striptags // 利用cheerio实现两个方法,module.exports 去除标签以及内容,export.fetch 获取标签中的文本内容

md文件内的代码模板

我们观察 md 文件内容可以发现,里面展示代码的部分会使用 :::demo ... :::demo 将代码块包裹起来,这并不是 md 的标记格式,所以推断此处的渲染逻辑改变了。那么怎么将自定义的 demo 标记让 markdown-it 识别呢,使用 markdown-container 插件。

container.js
const mdContainer = require('markdown-it-container');
module.exports = md => {
   
   
  md.use(mdContainer, 'demo', {
   
   
    validate(params) {
   
   
      return params.trim().match(/^demo\s*(.*)$/);
    },
    render(tokens, idx) {
   
   
      const m = tokens[idx].info.trim().match(/^demo\s*(.*)$/);
      if (tokens[idx].nesting === 1) {
   
    // 判断是否是开始标签
        const description = m && m.length > 1 ? m[1] : ''; // 获取描述内容
        const content = tokens[idx + 1].type === 'fence' ? tokens[idx + 1].content : ''; // 判断下一个是否是 fence 类型并获取 fence 内容
        // 内容和描述以 demo-block 组件作为模板
        return `<demo-block>
        <div>${
     
     md.render(description)}</div>
        <!--element-demo: ${
     
     content}:element-demo-->
        `; // 将内容放到 demo-block 组件中,后续交由 Vue 操作
      }
      return '</demo-block>';
    }
  });

  md.use(mdContainer, 'tip');
  md.use(mdContainer, 'warning');
};

这里使用的 demo 作为新的标记名称,这种标记的类型为 container_demo_open、container_demo_close

示例:
::: demo
// ```html 因为md文件会识别标记,所以使用注释
<m-row>
  <m-button type="primary" icon="m-icon-search">搜索</m-button>
  <m-button type="success" icon="m-icon-edit">编辑</m-button>
  <m-button type="success" icon="m-icon-download" align="right">下载</m-button>
</m-row>
// ```
::: demo

经过 markdown-it 编译后会转换为 ->

[ Token {
    type: 'container_demo_open',
    tag: 'div',
    attrs: null,
    map: [ 41, 53 ],
    nesting: 1,
    level: 0,
    children: null,
    content: '',
    markup: ':::',
    info: 'demo',
    meta: null,
    block: true,
    hidden: false },
  Token {
    type: 'fence',
    tag: 'code',
    attrs: null,
    map: [ 43, 53 ],
    nesting: 0,
    level: 1,
    children: null,
    content:
     '<m-row>\n  <m-button disabled>默认按钮</m-button>\n  <m-button type="primary" disabled>主要按钮</m-button>\n  <m-button type="success" disabled>成功按钮</m-button>\n  <m-button type="info" disabled>信息按钮</m-button>\n  <m-button type="warning" disabled>警告按钮</m-button>\n  <m-button type="danger" disabled>危险按钮</m-button>\n</m-row>\n',
    markup: '```',
    info: 'html',
    meta: null,
    block: true,
    hidden: false },
  Token {
    type: 'container_demo_close',
    tag: 'div',
    attrs: null,
    map: null,
    nesting: -1,
    level: 0,
    children: null,
    content: '',
    markup: ':::',
    info: '',
    meta: null,
    block: true,
    hidden: false }
]

demo 标记内的内容都以 demo-block 组件作为模板:

<template>
    <div class="demo-block"">
        <div class="source">
            <!-- 代码展示区域占位符 -->
            <slot name
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值