没有你的日子里,我真的好想你
枯了,年前1月20送修的电脑,一个月了才拿回来🤕
这篇文章加强认知的同时,使用一下star法则,加强表达能力
situation 情景
公司的组件库出第二版了,技术栈是vue2.6+ts,项目写到快要验收的阶段,问题出现了。组件库的文档在直接使用markdown-loader解析md的情况下,出现了md和demo不对应的情况。(有些开发者(好吧就是我自己。。)组件写完,文档写完,发现还要写.vue的demo组件,在本身还有繁杂业务需求的情况下,开始焦躁不已,demo也就草草了事了),对一些复杂组件,一个不和文档对应的demo,跟没有一样。因此,我们把目光瞄向了文档demo对应的element-ui。
task 任务
任务就是创建出一套md和demo对应的组件文档。
action 执行
确认过眼神,我遇上对的loader。看到element用md作为vue-router的路由组件的时候,element-ui的md-loader应该就是我们想要的东西。
md中使用
::: demo
```html
<action-sheet>...</actionsheet>
export default { ... }
\```
:::
包裹的东西最后会转成我们的demo,因此我们只要写一份md就能得到demo和文档了。
md-loader
一个webpack loader说白了其实就是一个接受source的function,函数处理source之后,返回一个新的source交给下个loader处理。
来康康md-loader的处理顺序
1、获取处理好的md
这边分几种情况
- 普通的md语法 交由markdown-it来解析,得到标签包裹好的文本
- 对于被:::demo所包裹的sfc语法,进行两步操作。
- 将这部分代码用
<!--element-demo: :element-demo-->
包裹,这个属于一个占位符,在我们遍历demo时便是以这个来标识demo代码的起点和终点的 - 再使用
<demo-block>...</demo-block>
包裹 - 得到一份新的md
- 针对新的md,改变markdown对demo块的处理方式,并放入
<template slot="highlight">
中,作为代码展示部分
if (token.info === 'html' && isInDemoContainer) { return `<template slot="highlight"><pre v-pre><code class="html">${md.utils.escapeHtml(token.content)}</code></pre></template>`; }
md.render(source)
得到最后的代码块
- 将这部分代码用
2、处理代码块,将其变成vue-loader能够处理的模样
其实,如果md中只有一个demo,根本没有必要进行这步处理,将模板直接塞入source,交给vue-loader处理就好了。可是理想很美好,现实很骨感,每个md一般会有多个demo块,如果共用一份状态,必须混合状态,且状态名不能重复,非常不方便,因此md-loader是这样处理的。
const demoComponentName = `element-demo${id}`;
output.push(`<template slot="source"><${demoComponentName} /></template>`);
对每个块插入一个element-demo${id}
组件,然后这个组件,就是我们接下来要生成的了,确保了每个demo都是独立互不干扰的。最后这些组件会通过插槽的形式,插入demo-block组件提供的槽位中。
生成element-demo${id}组件的render函数
依赖的两个库分别是@vue/component-compiler-utils
和vue-template-compiler
,
let demoComponentContent = genInlineComponentText(html, script);
通过genInlineComponentText函数生成我们所需的组件代码- 将合法的options传给
@vue/component-compiler-utils
中提供的compileTemplate
函数
const finalOptions = {
source: `<div>${template}</div>`,
filename: 'inline-component', // TODO:这里有待调整
compiler
};
const compiled = compileTemplate(finalOptions);
let demoComponentContent = `
${compiled.code}
`;
demoComponentContent = `(function() {
${demoComponentContent}
${script}
return Object.assign({
render,
staticRenderFns
}, democomponentExport)
})()`;
return demoComponentContent;
- 最后的demoComponentContent就是我们要的script了~
举个例子,最后的demoComponentContent的样子类似这样
"element-demo0": (function () {
var render = function (_c) { return _c(....) }
var staticRenderFns = []
const democomponentExport = {
data () {
return {
value1: false,
actions1: [
{ value: 1, label: '项目1' },
{ value: 2, label: '项目2' },
{ value: 3, label: '项目3' }
]
}
},
...options
}
return Object.assign({
render,
staticRenderFns
}, democomponentExport)
})
result 结果
将生成的模板,和得到的element-demo${id}组件script一起,插入到新的模板中
// md-loader
return `
<template>
<section class="content wand-doc" :class="'wand-doc-' + $route.fullPath.substring(1)">
${output.join('')}
</section>
</template>
${pageScript}
`;
下一步便由vue-loader处理了,生成整个路由组件的render函数并在路由访问时执行并挂载到页面上!最后我们看到的文档和demo就是一一对应的了,看起来非常得直观。
完成!