创见博客
如何实现一个处理文件的 Loader:以 Markdown 转 HTML 为例
七崽爱吃小饼干2026/09/20阅读 0

背景

前几篇的 clean-log-loader、insert-comment-loader 处理的都是 JS 文件。但 Loader 的能力远不止于此:任何能被 import 的文件类型,都可以交给自定义 loader 处理——Markdown、SVG、模板、配置……这篇就用 Markdown 转 HTML 讲清「处理文件的 loader」是怎么工作的。

关键认知:Loader 产出的必须是 JS 模块

Webpack 的模块系统只认识 JavaScript。当你写:

js
import post from './post.md';

Webpack 并不理解 .md,它会先找到匹配 .md 的 loader,把文件内容交给它,然后期望 loader 返回一段合法的 JS 代码。所以文件 loader 的核心任务是「翻译」:

Markdown 文本 ──loader──▶ module.exports = '<h1>...</h1>'

浏览器最终拿到的是字符串,而不是 Markdown 本身。

实现

安装一个 Markdown 解析器:

bash
npm install -D marked

新建 loaders/markdown-loader.js:

js
const { marked } = require('marked');

const schema = {
  type: 'object',
  properties: {
    wrapper: { type: 'string' },
  },
  additionalProperties: false,
};

module.exports = function markdownLoader(source) {
  const { wrapper } = this.getOptions(schema) || {};

  let html = marked.parse(source);

  if (wrapper) {
    html = `<${wrapper}>${html}</${wrapper}>`;
  }

  return `module.exports = ${JSON.stringify(html)};`;
};

逐段解读

  1. source 是文件内容 和 JS loader 一样,source 就是 .md 的原始文本。区别只在于我们不再做语法分析,而是整体编译。

  2. 编译成 HTML marked.parse(source) 输出 HTML 字符串。

  3. options 控制包裹元素 wrapper 允许把结果再包一层,比如 article。依然用 this.getOptions(schema) 做校验。

  4. 包装成 JS 模块 最关键的一步:return \module.exports = ${JSON.stringify(html)};`。这里必须用 JSON.stringify` 把 HTML 转义成合法的 JS 字符串字面量,否则引号、换行、反斜杠都会破坏语法。

配置与使用

js
{
  test: /\.md$/i,
  use: [
    {
      loader: path.resolve(__dirname, 'loaders/markdown-loader.js'),
      options: { wrapper: 'article' },
    },
  ],
}

然后在代码里像普通模块一样使用:

js
import post from './post.md';

const article = document.createElement('div');
article.className = 'article';
article.innerHTML = post;
document.body.appendChild(article);

构建后,post.md 会被编译成一段导出 HTML 字符串的模块,post 就是渲染好的内容。

与 JS Loader 的区别

维度JS Loader文件 Loader
输入JS 源码任意文件内容
处理解析、增删改 AST编译、转换、序列化
输出JS 源码一段导出数据的 JS 模块
常见搭配babel-loadermarkdown-loader、svg-inline-loader

注意事项

  • 导出格式要与导入方式匹配:示例导出 CommonJS,import 会被 Webpack 正确互操作。若目标环境严格区分 ESM,可改成 export default。
  • 注意 XSS:innerHTML 渲染用户可控的 Markdown 有风险,生产环境应对 HTML 做净化(如 DOMPurify)。
  • 解析器配置:marked 支持 GFM、代码高亮等配置,可作为 loader 的 options 继续扩展。
  • 缓存:Loader 默认是可缓存的,只要输入和 options 不变,Webpack 会复用结果。
  • sourcemap:这里做了「内容替换」,行号意义不大,通常无需额外处理;若做行级转换,记得透传 this.sourceMap。

小结

处理文件的 loader,本质上是一个「格式翻译器」:把非 JS 文件编译成一段可 import 的 JS 模块。理解这一点后,你可以用它接入 Markdown、模板引擎、配置文件,甚至让 import 一份 .sql 或 .graphql 变成可能。

套路只有三步:读取 source → 转换内容 → 包装成 JS 模块导出。

评论
0/100