{"id":29575965,"url":"https://github.com/viggo95/loader","last_synced_at":"2026-05-19T10:36:35.487Z","repository":{"id":170526399,"uuid":"131498053","full_name":"Viggo95/loader","owner":"Viggo95","description":"loader探索和进阶","archived":false,"fork":false,"pushed_at":"2018-04-29T14:43:52.000Z","size":10,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-03-28T13:17:28.759Z","etag":null,"topics":["loader","webpack"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Viggo95.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-04-29T13:46:25.000Z","updated_at":"2019-03-30T04:35:19.000Z","dependencies_parsed_at":null,"dependency_job_id":"fc801cee-348e-4000-8019-c53d16c3d391","html_url":"https://github.com/Viggo95/loader","commit_stats":null,"previous_names":["screetbloom/loader","viggo95/loader"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Viggo95/loader","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Viggo95%2Floader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Viggo95%2Floader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Viggo95%2Floader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Viggo95%2Floader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Viggo95","download_url":"https://codeload.github.com/Viggo95/loader/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Viggo95%2Floader/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32042293,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-20T00:18:06.643Z","status":"online","status_checked_at":"2026-04-20T02:00:06.527Z","response_time":94,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["loader","webpack"],"created_at":"2025-07-19T11:18:22.804Z","updated_at":"2026-04-20T10:02:31.932Z","avatar_url":"https://github.com/Viggo95.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# loader\n尝试去实现一个任意后缀的loader ，先尝试一个markdown的loader  \n\n关于 webpack loader\n---\n首先得理解 webpack 的 loader 的功能和原理。          \n\n\u003e loader 用于对模块的源代码进行转换。loader 可以使你在 import 或\"加载\"模块时预处理文件。因此，loader 类似于其他构建工具中“任务(task)”，并提供了处理前端构建步骤的强大方法。loader 可以将文件从不同的语言（如 TypeScript）转换为 JavaScript，或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 import CSS文件！（摘自 webpack 官方中文文档）\n\n    \n由此可见，loader 就像是一个“处理器”，输入特定的内容，处理后进行输出。当必要时，可以把一些合适的 loader 串起来使用，使前一个 loader 的输出变成后一个 loader 的输入，最终得到自己想要的结果。\n\u003cbr\u003e\n对于 markdown-loader 来说，它需要进行的处理就是：将 markdown 文件内容解析并包装成一个与 vue 单文件组件内容相似的，然后传给它后面的 vue-loader, 所以一个最简单的 vue markdown-loader 如下：\n```js\nmodule.exports = function (src) {\n  const res = (\n    `\u003ctemplate\u003e\\n` +\n    `\u003ch1\u003ehello world\u003c/h1\u003e\\n` +\n    `\u003c/template\u003e`\n  )\n  return res\n}\n```\n当然，这个 loader 看起来有点儿智障，因为不管传给它什么，最后输出来的都一样的玩意儿。但它确实做一个 loader 通常做的事…… \n\u003cbr\u003e\n下面进入正题，要做一个处理 markdown 的 loader 其逻辑要复杂得多，但实质与上面的差不多，首先我们需要安装一些必要的包。\n\n安装需要的包\n---\n```bash\nyarn install  markdown-it markdown-it-anchor markdown-it-container markdown-it-emoji markdown-it-table-of-contents\n```\n包名称|功能说明\n-|-\nmarkdown-it\t| 渲染 markdown 基本语法\nmarkdown-it-anchor |\t为各级标题添加锚点\nmarkdown-it-container |\t用于创建自定义的块级容器\nmarkdown-it-emoji |\t渲染 emoji\nmarkdown-it-table-of-contents  | 自动生成目录\nhighlight.js  |\t代码高亮\n\n编写 loader\n---\n在这个 loader 里，传入的是 markdown 文件的源文件，也就是没作任何解析的内容，我们需要对它进行一些处理，包括解析基本语法、渲染 emoji、添加锚点等处理，并定义一些自定义的块，比较关键的就是，最后要把这些内容包裹到 \u003ctemplate\u003e 标签中，不然接下来处理它们的 vue-loader 处理不了。\n\u003cbr\u003e\n贴上 loader 的代码\n```js\nconst fs = require('fs')\nconst path = require('path')\nconst hash = require('hash-sum')\nconst LRU = require('lru-cache')\nconst hljs = require('highlight.js')\n\n// markdown-it 插件\nconst emoji = require('markdown-it-emoji')\nconst anchor = require('markdown-it-anchor')\nconst toc = require('markdown-it-table-of-contents')\n\n// 自定义块\nconst containers = require('./containers')\n\nconst md = require('markdown-it')({\n  html: true,\n  // 代码高亮\n  highlight: function (str, lang) {\n    if (lang \u0026\u0026 hljs.getLanguage(lang)) {\n      try {\n        return '\u003cpre class=\"hljs\"\u003e\u003ccode\u003e' +\n          hljs.highlight(lang, str, true).value +\n          '\u003c/code\u003e\u003c/pre\u003e'\n      } catch (__) {}\n    }\n\n    return '\u003cpre v-pre class=\"hljs\"\u003e\u003ccode\u003e' + md.utils.escapeHtml(str) + '\u003c/code\u003e\u003c/pre\u003e'\n  }\n})\n  // 使用 emoji 插件渲染 emoji\n  .use(emoji)\n  // 使用 anchor 插件为标题元素添加锚点\n  .use(anchor, {\n    permalink: true,\n    permalinkBefore: true,\n    permalinkSymbol: '#'\n  })\n  // 使用 table-of-contents 插件实现自动生成目录\n  .use(toc, {\n    includeLevel: [2, 3]\n  })\n  // 定义自定义的块容器\n  .use(containers)\n\nconst cache = LRU({ max: 1000 })\n\nmodule.exports = function (src) {\n  const isProd = process.env.NODE_ENV === 'production'\n\n  const file = this.resourcePath\n  const key = hash(file + src)\n  const cached = cache.get(key)\n\n  // 重新模式下构建时使用缓存以提高性能\n  if (cached \u0026\u0026 (isProd || /\\?vue/.test(this.resourceQuery))) {\n    return cached\n  }\n\n  const html = md.render(src)\n\n  const res = (\n    `\u003ctemplate\u003e\\n` +\n    `\u003cdiv class=\"content\"\u003e${html}\u003c/div\u003e\\n` +\n    `\u003c/template\u003e\\n`\n  )\n  cache.set(key, res)\n  return res\n}\n```\n**containers.js 中代码**\n```js\nconst container = require('markdown-it-container')\n\nmodule.exports = md =\u003e {\n  md\n    .use(...createContainer('tip', 'TIP'))\n    .use(...createContainer('warning', 'WARNING'))\n    .use(...createContainer('danger', 'WARNING'))\n    // explicitly escape Vue syntax\n    .use(container, 'v-pre', {\n      render: (tokens, idx) =\u003e tokens[idx].nesting === 1\n        ? `\u003cdiv v-pre\u003e\\n`\n        : `\u003c/div\u003e\\n`\n    })\n}\n\nfunction createContainer (klass, defaultTitle) {\n  return [container, klass, {\n    render (tokens, idx) {\n      const token = tokens[idx]\n      const info = token.info.trim().slice(klass.length).trim()\n      if (token.nesting === 1) {\n        return `\u003cdiv class=\"${klass} custom-block\"\u003e\u003cp class=\"custom-block-title\"\u003e${info || defaultTitle}\u003c/p\u003e\\n`\n      } else {\n        return `\u003c/div\u003e\\n`\n      }\n    }\n  }]\n}\n```\n尝试使用\n---\n写好了 loader，就可以在 webpack 里使用了，在配置的 module.rules 数组中加入如下规则：\n```bash\n{\n  test: /\\.md$/,\n  use: [\n    {\n      loader: 'vue-loader', // 这里的使用的最新的 v15 版本\n      options: {\n        compilerOptions: {\n          preserveWhitespace: false\n        }\n      }\n    },\n    {\n      // 这里用到的就是刚写的那个 loader\n      loader: require.resolve('./mloader')\n    }\n  ]\n},\n```\n然后，就可以在自己的组件中引入 markdown 文件了。假如你有一个名叫 test.md 的文件里有下面这样的内容:\n```bash\n# hello world\n\n[[toc]]\n\n## 二级标题1\n\ntest test :+1:\n\n## 二级标题2\n\ntest  test :cry:\n\n## 二级标题3\n\n### 三级标题1\n333333333333333\n\n### 三级标题2\n444444444444\n\n\n:::tip\n友情提示\n:::\n\n:::danger\n页面崩溃……\n:::\n\n```\n在 vue 项目中使用：\n```bash\n\u003ctemplate\u003e\n  \u003cmy-markdown/\u003e\n\u003c/template\u003e\n\n\u003cscript\u003e\nexport default {\n  components: {\n    'my-markdown': () =\u003e import('./test.md')\n  }\n}\n\u003c/script\u003e\n```\n**vue-loader就会将以components形式引入的该md后缀的文件当成当前的子组件的组件来渲染展示**\n\n结尾\n---\n可以看到，了解其中原理之后，实现一个简单的loader还是可能的；当然我们的目标是希望最终能写个 loader 处理/加载自定义扩展名的文件\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fviggo95%2Floader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fviggo95%2Floader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fviggo95%2Floader/lists"}