开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情
作为一名前端工程师,日常工作中免不了使用各种组件库。每次使用时,我都在想,这示例组件是怎么写的?
难道和我一样,也是将示例代码复制粘贴一份嘛?
于是,我就去深入地研究了一下,果然,只有我在使用这种质朴的做法。由于笔者工作中使用 Vue,很多 Vue 的组件库是基于 Vuepress 或 Vitepress 编写。所以,这里就以 Vuepress 的一个插件 Vuepress-plugin-demo-container 为例,深入研究一下其工作原理。
基本原理
首先,我们来看一下该插件的源码结构:
src
| - common // 工具函数、渲染函数、容器函数
| - - | - container.js
| - - | - render.js
| - - | - util.js
| - i18n // 国际化语言配置
| | - default_lang.json // 语言配置
| - DemoBlock.vue // 示例组件容器
| - enhanceAppFile.js // 注册组件
| - index.js // 默认导出文件
我们从index.js文件出发,了解插件的整体思路:
/**
* 提供 ::: demo xxx ::: 语法,用于构建 markdown 中的示例
*/
const path = require('path')
const renderDemoBlock = require('./common/render')
const demoBlockContainers = require('./common/containers')
module.exports = (options = {}, ctx) => {
return {
enhanceAppFiles: path.resolve(__dirname, './enhanceAppFile.js'),
chainMarkdown(config) {
config.plugin('containers')
.use(demoBlockContainers(options))
.end();
},
extendMarkdown: md => {
const id = setInterval(() => {
const render = md.render;
if (typeof render.call(md, '') === 'object') {
md.render = (...args) => {
let result = render.call(md, ...args);
const { template, script, style } = renderDemoBlock(result.html);
result.html = template;
result.dataBlockString = `${script}\n${style}\n${result.dataBlockString}`;
return result;
}
clearInterval(id);
}
}, 10);
}
}
}
从 index.js 文件中,我们可以看出整体分为三部分:
enhanceAppFiles:此选项接受指向增强文件的绝对文件路径或返回该路径的函数,你可以通过此选项做一些应用级别的配置。应用级别就是指安装一些附加的 Vue 插件、注册全局组件,或者增加额外的路由钩子等。在本插件中,就是指向了一个包裹示例demo、源码的组件,该组件通过Vue.component进行全局注册。chainMarkdown:使用markdown-it-chain修改内部的markdown配置。在本插件中主要用于识别::: demo语法,构造 html 字符串。extendMarkdown:修改用于markdown文件渲染的配置。在本插件中,主要用于处理上一步得到的 html 字符串,并通过 vue-template-compiler 将其编译为 render Function
工作流程
从以上分析,我们也可以得知该插件的工作流程为:
- 识别
:::demo语法,得到 html 字符串 - 处理 html 字符串,得到渲染函数
- 通过 vue-loader 处理渲染函数,最终渲染到页面上
更详细的流程图如下所示:
总结
本篇文章梳理了vuepress-plugin-demo-container的整体工作流程。接下来会详细论述每一步的流程,真正理解其背后的原理。