入口文件
首先,看一个项目的入口,可以从package.json中去看它的运行命令

可以看到dev那行,执行了很多脚本: npm run bootstrap && npm run build:file && cross-env NODE_ENV=development webpack-dev-server --config build/webpack.demo.js & node build/bin/template.js
npm run bootstrap
就是安装项目所需依赖, 重点就是webpack-dev-server --config build/webpack.demo.js
项目使用webpack执行build/webpack.demo.js,生成一个服务器,这个是项目的打包脚本js。

可以看到entry指向了./examples/entry.js
,那么这个是我们的入口文件了。
entry.js


可以看到这里就是一个普通Vue的项目了,引用了许多的模块,注册了一些自己编写的Vue组件,重点需要看的是router。
router.config.js

这是对项目边栏,导航、主题等进行国际化渲染的路由。

这个对应的是项目的内容,也就是我们看到的demo演示部分。那么它是怎么把 .md
的文件渲染到页面并且达到既显示demo效果,又有源代码的呢?
能做到这一点只能在编译的时候就对md
的文件进行分析,分析出code
部分,然后对code
部分进行一些处理。这里element-ui在原来mdloader的基础上,添加一些项目需要用到的需求代码。我们回到webpack-demo.js

md-loader/index.js

它的入口文件是index.js

引入了config.js
md-loader/config.js

引用了许多的依赖包,使用了markdown-it-chain
的链式调用,配置了一对东西。可以去看看这个模块的使用:预览
其实就是一个把.md
解析为一个树行结构,再通过通过一些函数对树进行解析,拼接dom,生成dom树的作用,这里面的一些api需要自己去看才能理解。 简而言之,就是解析的代码,element-ui的工程师,对它扩展了一些功能,让它把code
部分拼接成:
<demo-block>demo组件组件代码描述
</demo-blcok>
最后的拼接主要看index.js
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 的 idlet output = []; // 输出的内容let start = 0; // 字符串开始位置let commentStart = content.indexOf(startTag);let commentEnd = content.indexOf(endTag, commentStart + startTagLen);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);let demoComponentContent = genInlineComponentText(html, script);const demoComponentName = `element-demo${id}`;output.push(`<template slot="source"><${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}`;
};
这样接的vue组件就会被vue-loader来处理,这样就形成了我们看到的效果。
附上自己分离它逻辑的demo: demo链接 预览链接