背景需求:
随着业务增多,公共组件变多,无法直观知道其中的方法使用和业务场景,轻量级不需要重新新建一个项目
技术基础:
1.仿造element-ui md-loader
需求场景:
- 当前项目公共组件比较多,需要一个文档来描述
- 希望轻量化,没有额外资源部署一个文档项目,即内嵌在开发项目中
期望效果
一个md文件一个页面
生成如下,并且一个md文件可以同时写多个demo
思路来源
使用 element-ui源码中的md-loader,抽取出来后,增强一些其他功能即可。
其中重点是实现md文件中的 :::demo ,vuepress·也是这个思路。
根据 :::demo 将 md文件中的vue片段用 demo-block 组件包裹,然后传递给 vue-loader生成html 即可。
demo-block 组件是自己定义的一个vue组件容器,可在源码中找到。
过程
代码准备
- 将element-ui 中的md-loader拷贝到自己项目中.
- 安装所需要的依赖
"markdown-it": "^13.0.1",
"markdown-it-anchor": "^8.6.4",
"markdown-it-chain": "^1.3.0",
"markdown-it-container": "^3.0.0", "transliteration": "^2.3.5"
- 配置md文件的loader
// vue.config.js
chainWebpack: config => {
// 移除 prefetch 插件
config.plugins.delete("preload");
config.plugins.delete("prefetch");
// 自定义插件
config.module
.rule("md-loader")
.test(/\.md$/)
.use("vue-loader")
.loader("vue-loader")
.end()
.use("md-loader")
.loader(path.join(__dirname, "./md-loader/index.js"))
.end();
}
- 准备好文件页面用到的初始组件,具体可以参考element-ui
- 因为我的与 element-ui 一样使用的是路由切换页面的,所以配置了动态路由
import Vue from "vue";
import VueRouter from "vue-router";
export default [
{
name: "更新日志",
path: "/changelog",
},
{
name: "开发规范",
children: [
{
path: "/git",
name: "代码提交规范",
},
{
path: "/docs-rule",
name: "文档编写规范",
},
],
},
{
name: "components组件",
groups: [
{
groupName: "form",
list: [
{
path: "/s_basicType",
title: "BasicType 数据类型",
},
{
path: "/s_calculationStatus",
title: "CalculationStatus 重算状态",
},
{
path: "/s_dataDimension",
title: "DataDimension 数据来源",
},
{
path: "/s_employingUnit",
title: "EmployingUnit 用工单位通用",
},
{
path: "/s_employingUnitBind",
title: "EmployingUnitBind 用工单位通用",
}
],
},
{
groupName: "业务组件",
list: [
{
path: "/s_upload",
title: "Upload 上传图片",
},
{
path: "/s_calculationStatus",
title: "业务组件2",
},
{
path: "/s_dataDimension",
title: "业务组件3",
},
{
path: "/s_employingUnit2",
title: "业务组件4"
}
],
}
]
},
{
name: "Element官网",
href: "https://element.eleme.cn/#/zh-CN/component/installation",
},
{
name: "xxxUI官网",
href: "http://xx.xx.xx:8080/#/zh-CN/",
},
{
name: "Iconfont官网",
href: "https://www.iconfont.cn/",
},
{
name: "Vue官网",
href: "https://cn.vuejs.org/",
}
];
最后直接重新运行即可。
增强
md中的css直接作用于页面
由于element-ui 的文档中的css是单独写在文件中的,在md中的css并不会作用于页面,那么我是做了一点修改。
解析出demo中的css后,统一加入 style 标签中。
md-loader\index.js
const {
stripScript,
stripTemplate,
genInlineComponentText,
stripStyle
} = require("./util");
const md = require("./config");
module.exports = function(source) {
const content = md.render(source);
const startTag = "<!--element-demo:";
const startTagLen = startTag.length;
const endTag = ":element-demo-->";
const endTagLen = endTag.length;
let componenetsString = "";
let id = 0; // demo 的 id
const output = []; // 输出的内容
let start = 0; // 字符串开始位置
let commentStart = content.indexOf(startTag);
let commentEnd = content.indexOf(endTag, commentStart + startTagLen);
const styleList = [];
while (commentStart !== -1 && commentEnd !== -1) {
output.push(content.slice(start, commentStart));
const commentContent = content.slice(
commentStart + startTagLen,
commentEnd
);
const html = stripTemplate(commentContent);
const script = stripScript(commentContent);
const style = stripStyle(commentContent);
styleList.push(`<style>${style}</style>`);
const demoComponentContent = genInlineComponentText(html, script);
const demoComponentName = `element-demo${id}`;
output.push(
`<template slot="source"><${demoComponentName} class="${demoComponentName}"/></template>`
);
componenetsString += `${JSON.stringify(
demoComponentName
)}: ${demoComponentContent},`;
// 重新计算下一次的位置
id++;
start = commentEnd + endTagLen;
commentStart = content.indexOf(startTag, start);
commentEnd = content.indexOf(endTag, commentStart + startTagLen);
}
// 仅允许在 demo 不存在时,才可以在 Markdown 中写 script 标签
// todo: 优化这段逻辑
let pageScript = "";
if (componenetsString) {
pageScript = `<script>
export default {
name: 'component-doc',
components: {
${componenetsString}
}
}
</script>`;
} else if (content.indexOf("<script>") === 0) {
// 硬编码,有待改善
start = content.indexOf("</script>") + "</script>".length;
pageScript = content.slice(0, start);
}
output.push(content.slice(start));
return `
<template>
<section class="content element-doc">
${output.join("")}
</section>
</template>
${pageScript}
${styleList}
`;
};
当前只实现了匹配普通style,如果需要匹配 lang=“scss” ,只要修改下匹配style的正则即可。
组件注册
首先是 md文件的注册
// src\components\.components\Guide.vue
其次是项目公共文件的注册。由于公共组件是内嵌到 demo-block 组件中,所以只能选择全局注册,但是注册入口不选择在 main.js。 选中在 src\components\.components\Guide.vue
// src\components\.components\Guide.vue
const components = require.context('../', false, /\.vue/)
components.keys().forEach(fileName => {
// 组件实例
const reqCom = components(fileName)
// 截取路径作为组件名
const reqComName = fileName.replace(/\.\//, '').replace(/\.vue/, '')
// 组件挂载
Vue.component(reqComName, reqCom.default || reqCom)
})
saas-risk-admin实施效果