阅读 116

vuepress-plugin-demo-container 之 containers.js

上一篇文章介绍了 vuepress-plugin-demo-container的整体工作流程,本篇来详细介绍如何识别自定义代码块并构建HTML字符串

enhanceAppFiles

前文说到 enhanceAppFiles 是用来增加一些应用级别的配置,在本插件中,就是用来注册一个包裹示例代码的组件:

import DemoBlock from './DemoBlock.vue' export default ({ Vue }) => {   Vue.component('DemoBlock', DemoBlock) } 复制代码

展开、关闭、复制源代码都是依托于该组件实现,该组件的源代码可点击 DemoBlock.vue 查看

containers.js 详解

该文件就是用来识别自定义代码块的文件,首先列出文件源码:

const mdContainer = require('markdown-it-container'); // options 就是在config.js中plugins参数的配置 module.exports = options => {   const {     component = 'demo-block'   } = options;   const componentName = component     .replace(/^\S/, s => s.toLowerCase())     .replace(/([A-Z])/g, "-$1").toLowerCase();        return md => {     md.use(mdContainer, 'demo',       {        // 验证:::之后是否为 demo         validate(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] : '';             const content = tokens[idx + 1].type === 'fence' ? tokens[idx + 1].content : '';             const encodeOptionsStr = encodeURI(JSON.stringify(options));             return `<${componentName} :options="JSON.parse(decodeURI('${encodeOptionsStr}'))">             <template slot="demo"><!--pre-render-demo:${content}:pre-render-demo--></template>             ${description ? `<div slot="description">${md.render(description).html}</div>` : ''}             <template slot="source">`           }           return `</template></${componentName}>`;         }       });   }; } 复制代码

代码并不长,我们一点一点来看。

options.vuepress/config.jsplugins  参数的配置,如下所示:

  plugins: [     [       require('../../src'),       {         component: 'DemoBlock',         locales: [   ...         ]       }     ]   ], 复制代码

componentlocales 都会被传入到options中。在对options做解构的同时,给component属性赋予默认值demo-block。在 plugins参数中没有传入component属性的情况下,该属性就是 demo-block

  const componentName = component     .replace(/^\S/, s => s.toLowerCase())     .replace(/([A-Z])/g, "-$1").toLowerCase(); 复制代码

该处的代码是为了实现 DemoBlockdemo-block 的转换

render函数解析

下面,就是最为重要的渲染流程。首先来了解一下 markdown 转换为 HTML 的流程

markdown解析流程.png 总体可以概括为两步:

  1. markdown 经过词法解析得到 token

  2. token 渲染为 HTML

几个相关的token属性为:

  1. nesting:嵌套层级,1 对应 HTML 中的开始标签,-1 对应 HTML 中的结束标签,0表示中间值

  2. info:::: 后的信息

  3. type:token 类型,fence 表示代码块 以下面代码块为例,将其转为token

::: demo 此处放置代码示例的描述信息,支持 `Markdown` 语法,**描述信息只支持单行** `` `html <template>   <div class="red-center-text">       <p>{{ message }}</p>       <input v-model="message" placeholder="Input something..."/>   </div> </template> <script> export default {   data() {     return {       message: 'Hello Vue'     }   } } </script> <style> .red-center-text {    color: #ff7875;   text-align: center; } </style> `` ` ::: 复制代码

转化后得到的token为:

[   Token {     type: 'container_demo_open',     tag: 'div',     attrs: null,     map: [ 2, 26 ],     nesting: 1,     level: 0,     children: null,     content: '',     markup: ':::',     info: ' demo 此处放置代码示例的描述信息,支持 `Markdown` 语法,**描述信息只支持单行**',     meta: null,     block: true,     hidden: false   },   Token {     type: 'fence',     tag: 'code',     attrs: null,     map: [ 3, 26 ],     nesting: 0,     level: 1,     children: null,     content: '<template>\n' +       '  <div>\n' +       '      <p>{{ message }}</p>\n' +       '      <input v-model="message" placeholder="Input something..."/>\n' +       '  </div>\n' +       '</template>\n' +       '<script>\n' +       'export default {\n' +       '  data() {\n' +       '    return {\n' +       "      message: 'Hello Vue'\n" +       '    }\n' +       '  }\n' +       '}\n' +       '</script>\n' +       '<style>\n' +       '.red-center-text { \n' +       '  color: #ff7875;\n' +       '  text-align: center;\n' +       '}\n' +       '</style>\n',     markup: '```',     info: 'html',     meta: null,     block: true,     hidden: false   },   Token {     type: 'container_demo_close',     tag: 'div',     attrs: null,     map: null,     nesting: -1,     level: 0,     children: null,     content: '',     markup: ':::',     info: '',     meta: null,     block: true,     hidden: false   } ] 复制代码

render函数过程

  1. 遍历token数组。如果 nesting = 1  就构建HTML开标签。标签名就是上文解构出的 componentName

  2. 找到fencetoken,取出content属性,该属性就是代码块中的所有代码。并将代码以注释的形式放在插槽中,将其传入到DemoBlock.vue 中。

  3. 如果 nesting = -1,构建 HTML 闭合标签。

  4. 返回整个字符串

总结

这一步的主要作用就是识别 :::demo 代码块,构建HTML字符串。下一步就是处理HTML字符串,例如,将 vue template 转化为 render 函数、合并script、style等。

vuepress-plugin-demo-container源码解析系列


作者:Lyx
链接:https://juejin.cn/post/7171266648068849695
来源:https://www.77cxw.com/



文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐