0
学习dev环境是如何运作markdown-it.docschina.org/的 组件页面是以md格式编写的,代码中使用md-loader来解析这些文件
webpack
{
test: /\.md$/,
use: [
{
loader: 'vue-loader',
options: {
compilerOptions: {
preserveWhitespace: false
}
}
},
{
loader: path.resolve(__dirname, './md-loader/index.js')
}
]
}
step1. 用md-loader解析成vue文件 step2. 用vue-loader来解析生成的文件
md-loader
- markdown-it 是 Markdown 解析器 文档: markdown-it.docschina.org/
- markdown-it-chain 是markdown-it 可以chain 式操作
- markdown-it-anchor 为标题添加锚点
- transliteration 中文转拼音 demo : dzcpy.github.io/translitera…
//slugify 的用法
import { transliterate as tr, slugify } from 'transliteration';
tr('你好, world!');
// Ni Hao , world!
slugify('你好, world!');
// ni-hao-world
- markdown-it-container Plugin for creating block-level custom containers for markdown-it markdown parser. 这里用来处理 :::demo ... :::
zhuanlan.zhihu.com/p/65174076 blog.csdn.net/Ariana_l/ar…
const mdContainer = require('markdown-it-container')
module.exports = md => {
/* 由于 demo是 以下格式所有要特殊处理
:::demo 标题
内容
:::
tip 和warning 都是 以下格式,没有标题,所以无需处理
:::xx
:::
*/
md.use(mdContainer, 'demo', {
validate(params) {
// params: ::: 后的哪一行的内容, 当:::为结束时 params 为空
console.log('==========:', params)
return params.trim().match(/^demo\s*(.*)$/)
},
render(tokens, idx) {
const m = tokens[idx].info.trim().match(/^demo\s*(.*)$/)
if (tokens[idx].nesting === 1) {
// 判断是否是开始标签
const description = m && m.length > 1 ? m[1] : ''
// 下一个类型是否是fence (代码块)
const content = tokens[idx + 1].type === 'fence' ? tokens[idx + 1].content : ''
return `<demo-block>
${description ? `<div>${md.render(description)}</div>` : ''}
<!--element-demo: ${content}:element-demo-->
`
}
return '</demo-block>'
},
})
md.use(mdContainer, 'tip')
md.use(mdContainer, 'warning')
}
- fence处理
处理成
<template slot="highlight">xxx</template>以方便放到demo-block中
生成最终的vue文件
上面的步骤中已经将代码段的描述(放到了div中), 代码内容(放到了 :::demo 结果中的<!--element-demo:xxx:element-demo--> 中,代码重新进行了fence.
剩下的问题是处理源码,把源码转换成内部组件
将一段代码转换成组件源码
``html
step1 : 提取template 和 script
step2 : 将template 转换成render
```js
const finalOptions = {
source: `<div>${template}</div>`,
filename: 'inline-component', // TODO:这里有待调整
compiler
};
const compiled = compileTemplate(finalOptions);
compiled.code 是个string 包含以下内容
// compiled.code
var render = function() {
var _vm = this
var _h = _vm.$createElement
var _c = _vm._self._c || _h
return _vm._m(0)
}
var staticRenderFns = [
// ....
]
render._withStripped = truef
最终生成的组件代码是:
{
render:function.
staticRenderFns,//静态渲染函数
====script中的内容====
methods: {
hello() {
alert('Hello World!');
}
}
========
}
> 最终生成的
```vue
<template>
<section class="content element-doc">
${output.join('')}
<template slot="source"><element-demo1 /></template>
<template slot="highlight"><pre v-pre> ....
</section>
</template>
<script>
export default {
name: 'component-doc',
components: {
${componentsString}
element-demo1:{
render,
staticRenderFns ...
}
}
}
</script>
问题
为什么非要转换成render 直接这样可以吗?
element-demo1:{
template:"...template中的code..",
....script中的code....
}
原因
这个外部变量无法处理,这个变量属于组件的私有变量,但在组件眼里又是外部
解决办法==>闭包
const { stripScript, stripTemplate, genInlineComponentText } = require('./util')
const md = require('./config')
// source : 文件内容
module.exports = function(source, map, meta) {
// console.log('===resourcePath:\n', this.resourcePath)
// console.trace()
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
let output = [] // 输出的内容
let start = 0 // 字符串开始位置
let privateTemplates = []
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)
let script = stripScript(commentContent)
// let demoComponentContent = genInlineComponentText(html, script)
const demoComponentName = `element-demo${id}`
output.push(`<template slot="source"><${demoComponentName} /></template>`)
var iHtml = html.replace(/^<template>\s*/, '')
iHtml = iHtml.replace(/<\/template>$/, '')
privateTemplates.push(`<template id="${demoComponentName}"><div>${iHtml}</div></template>`)
// if (script) {
// script = script.replace(/export\s+default\s*\{\s*/, '')
// script = script.replace(/\s*\};?\s*$/, '')
// } else {
// script = ''
// }
let result = script.match(/([\s\S]*)(export\s+default)\s*{([\s\S]+)(\};?)$/)
let preCode = result && result[1] ? result[1] : ''
let mainCode = result && result[3] ? result[3] : ''
let componentCode = `(function() { ${preCode} return {
template:'#${demoComponentName}',${mainCode}
} })()`
// console.log('=====script======', script)
// componenetsString += `${JSON.stringify(demoComponentName)}: ${demoComponentContent},`
componenetsString += `${JSON.stringify(demoComponentName)}: ${componentCode},`
// 重新计算下一次的位置
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))
let res = `
<template>
<section class="content element-doc">
${output.join('')}
</section>
</template>
${privateTemplates.join('')}
${pageScript}
`
if (privateTemplates.length) {
//
}
// console.log('===========\n', res)
return res
}