• elementui源码解析markdown处理


    一些参考网址:

    markdown-it插件的分析和源码分析参考地址:https://zhuanlan.zhihu.com/p/64290806
    element-ui的源码地址:https://github.com/ElemeFE/element
     

    一:先从package.json找到本地运行官网的指令是在scripts里的dev指令

     
    "dev": "npm run build:file && cross-env NODE_ENV=development webpack-dev-server --config build/webpack.demo.js & node build/bin/template.js",

    找到对应md解析的module的处理loader

    处理markdown文件是先用build/md-loader/index.js处理了,然后再用vue-loader处理的;

    md组件文件都在 examples/docs里,下面拿examples/docs/zh-cn/button.md组件的解析为例子

     

    二: 进入build/md-loader/index.js里查看时如何解析的md文件的

    // build/md-loader/index.js
    const {
      stripScript,
      stripTemplate,
      genInlineComponentText
    } = require('./util'); // 提取script内容和纯内容部分,以及拼接好模板的方法
    const md = require('./config'); // 暴露出已经处理好markdownit插件的md
    
    module.exports = function(source) {
      const content = md.render(source); // config里对render的插件的处理
      ...
    };

    第一步是在  build/md-loader/config.js里处理的

    // build/md-loader/config.js
    const Config = require('markdown-it-chain'); // 支持链式调用 markdown-it const anchorPlugin = require('markdown-it-anchor'); // 配置标题目录跳转的插件 const slugify = require('transliteration').slugify; // 把中文翻译成拼音的插件 const containers = require('./containers'); // 内容块容器化处理插件 const overWriteFenceRule = require('./fence'); //修改默认的fence规则的渲染函数,加上展开的源码的部分 const config = new Config(); config .options.html(true).end() .plugin('anchor').use(anchorPlugin, [ { level: 2, slugify: slugify, permalink: true, permalinkBefore: true } ]).end() .plugin('containers').use(containers).end(); // 主要是这个插件里处理md里写的elementui组件的渲染的代码 const md = config.toMd(); overWriteFenceRule(md); module.exports = md;

    主要的md里编写的elementui组件处理插件是 build/md-loader/containers.js里处理的,把  :::demo  和 ::: 之间的非描述部分,放到了 <!--element-demo: ${content}:element-demo-->,后面在  build/md-loader/index.js里处理;

    // build/md-loader/containers.js
    const mdContainer = require('markdown-it-container'); // 这个插件可以让你支持内容块,识别 markdown的::: module.exports = md => { // 用在markdown-it里,识别 ::: demo md.use(mdContainer, 'demo', { validate(params) { return params.trim().match(/^demo\s*(.*)$/); // *是匹配0次以上 }, /** * tokens 符合上面规则的所有内容 * idx 某一条的id */ 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] : ''; // 紧跟在demo后面的描述的内容 const content = tokens[idx + 1].type === 'fence' ? tokens[idx + 1].content : ''; // html的匹配内容 // 返回把内容放到已经写好的组件demo-block里;把html的内容放到!--element-demo里,便于后面处理抽取 // demo-block组件已经是在entry.js里作为全局组件注册过了可以直接使用 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'); };

    在  build/md-loader/index.js 里处理解析放入到 <!--element-demo: ${content}:element-demo--> 里的内容,提取里面的template部分和script部分,放到 

     <section class="content element-doc"> 里;
    // build/md-loader/index.js
    const {
      stripScript,
      stripTemplate,
      genInlineComponentText
    } = require('./util');
    const md = require('./config'); // 暴露出已经处理好markdownit插件的md
    
    module.exports = function(source) {
      const content = md.render(source);
    
      // md插件对里面的内容放到了<!--element-demo里,在这里做提取处理
      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 commentStart = content.indexOf(startTag); // html开始的下标值
      let commentEnd = content.indexOf(endTag, commentStart + startTagLen); // html结束的位置,从开始的标识结束的后面计算
      // 循环处理html的内容,必须是有开始和结尾标识的
      while (commentStart !== -1 && commentEnd !== -1) {
        output.push(content.slice(start, commentStart)); // 拿到前面非html内容的描述等文字部分
    
        const commentContent = content.slice(commentStart + startTagLen, commentEnd); // 拿到除去前后标识的html内容部分
        const html = stripTemplate(commentContent); // 拿到非script和style的内容部分
        const script = stripScript(commentContent); // 拿到script部分
        let demoComponentContent = genInlineComponentText(html, script); // 把md提取的内容拼接成vue的template编译的js
        const demoComponentName = `element-demo${id}`;
        output.push(`<template slot="source"><${demoComponentName} /></template>`); // 把内容放到demo-block的source的slot中
        componenetsString += `${JSON.stringify(demoComponentName)}: ${demoComponentContent},`; // 把处理好的template都作为组件放到components里
    
        // 重新计算下一次的位置
        id++;
        start = commentEnd + endTagLen;
        commentStart = content.indexOf(startTag, start);
        commentEnd = content.indexOf(endTag, commentStart + startTagLen);
      }
    
      // 仅允许在 demo 不存在时,才可以在 Markdown 中写 script 标签
     // componentsString是在上面提取的所有的 组件demo作为组件拼接的字符串
    // 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} `; };

    在 build/md-loader/fence.js里重写 md.renderer.rules.fence 规则,把md里的elementui组件放大demo-block的highlight插槽里,用于展示组件源码;

    // 覆盖默认的 fence 渲染策略
    module.exports = md => {
      const defaultRender = md.renderer.rules.fence;
      md.renderer.rules.fence = (tokens, idx, options, env, self) => {
        const token = tokens[idx];
        // 判断该 fence 是否在 :::demo 内
        const prevToken = tokens[idx - 1];
        const isInDemoContainer = prevToken && prevToken.nesting === 1 && prevToken.info.trim().match(/^demo\s*(.*)$/);
        if (token.info === 'html' && isInDemoContainer) {
          // html的加上高亮标签
          return `<template slot="highlight"><pre v-pre><code class="html">${md.utils.escapeHtml(token.content)}</code></pre></template>`;
        }
        return defaultRender(tokens, idx, options, env, self);
      };
    };

    全局组件demo-block的组件展示和组件源码插槽代码如下:

    // examples/components/demo-block.vue
    <
    template> <div class="demo-block" :class="[blockClass, { 'hover': hovering }]" @mouseenter="hovering = true" @mouseleave="hovering = false"> <div class="source"> <!-- 组件渲染展示的位置 --> <slot name="source"></slot> </div> <div class="meta" ref="meta"> <div class="description" v-if="$slots.default"> <slot></slot> </div> <div class="highlight"> <!-- 组件源码展示的位置 --> <slot name="highlight"></slot> </div> </div> <div 。。。。
  • 相关阅读:
    web print
    datediff
    Typical AJAX
    sql中构建sql语句,再exec这条SQL语句
    ASP.NET中将数据输出到WORD、EXCEL、TXT、HTM
    sql中添加单引号
    sql语句批处理
    日期分组
    异步线程
    电脑内存大有什么好处?
  • 原文地址:https://www.cnblogs.com/chun321/p/15623147.html
Copyright © 2020-2023  润新知