【elementui源码解析】如何实现自动渲染md文档-第四篇

目录

 1.前言

2.md-loader - index.js

1)md.render()

2)定义变量

3)while 

stripTemplate 

stripScript 

genInlineComponentText 

4)pageScript

5)return

6)demo-block

3.总结 


所有章节:

  • 【elementui源码解析】如何实现自动渲染md文档-第一篇
  • 【elementui源码解析】如何实现自动渲染md文档-第二篇
  • 【elementui源码解析】如何实现自动渲染md文档-第三篇

 1.前言

前面我们分析了md-loader中的config.js、containers.js和fence.js这三个文件的作用,今天我们来看md-loader的index.js文件,前面几个文件都在这个文件里得到应用,这也是elementui实现渲染md文件最重要的文件。如果还没阅读前几篇,可以点击下面链接查看前几篇。

2.md-loader - index.js

图1和图2就是index.js的源代码。

1-6行,引入了util中的几个方法,我们在下面用到的时候再介绍。然后就是引入了之前在config.js中定义的md解析器。

图1

图2

1)md.render()

我们还是以之前的md文档为例,如图3。

图3 

图4

第9行 const content = md.render(source); 输出的内容如下。输出格式有点混乱,我将其格式化了一下在图4下面的代码框里。

可以看见 id="input-shu-ru-kuang", 这就是之前cofig.js 里的 slugify 的作用。之后在elementui的锚点的时候就是通过这个id来定位到对应的位置。

然后可以看见 <demo-block></demo-block>,这是之前在 container.js 里定义的。我们在下面会介绍这个elementui自定义的全局的组件。然后第一个div里的内容就是description(结合图5看),然后就是这段注释的内容, <!--element-demo: <div>111test</div> :element-demo-->, 这里在下面会有用处。最后可以看见一段插槽,这就是 fence.js 覆盖的渲染策略,返回了md文档的代码部分(结合图6看)。

    <h2 id="input-shu-ru-kuang">
      <a class="header-anchor" href="#input-shu-ru-kuang">¶</a> Input 输入框
    </h2>
    <p>通过鼠标或键盘输入字符</p>
    <h3 id="jin-yong-zhuang-tai">
      <a class="header-anchor" href="#jin-yong-zhuang-tai">¶</a> 禁用状态
    </h3>
    <demo-block>
      <div>
        <p>通过 <code>disabled</code> 属性指定是否禁用 input 组件</p>
      </div>
      <!--element-demo: <div>111test</div> :element-demo-->
      <template slot="highlight">
        <pre v-pre>
          <code class="html">&lt;div&gt;111test&lt;/div&gt;
          </code>
        </pre>
      </template>
    </demo-block>

图5 

图6

2)定义变量

代码11-22行如下。

我们直接看最后两行,commentstart就是获取了在 md.render() 返回的字符串中 <!--element-demo: 这一段字符串的起始位置,commentEnd获取了 :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);
  let commentEnd = content.indexOf(endTag, commentStart + startTagLen);

3)while 

这个while函数的条件是在render函数返回的字符串中找到了 <!--element-demo:  和 :element-demo-->。这是必须的,为什么呢,因为这里面包含的就是我们要在页面上执行的demo的例子,可能我这里因为例子比较简单,不太直观,假设 <!--element-demo: :element-demo--> 里面包含的字符串是如下代码,这里面包含的是一个el-input实例,md文档里必须要有这个才会在页面上显示这个例子,如果没有这个demo例子,是不会执行渲染的。

<el-input
  placeholder="请输入内容"
  v-model="input"
  :disabled="true">
</el-input>

<script>
export default {
  data() {
    return {
      input: ''
    }
  }
}
</script>
while (commentStart !== -1 && commentEnd !== -1) {
    // 截取 <!--element-demo: :element-demo--> 之前的内容
    output.push(content.slice(start, commentStart)); 

    // <!--element-demo: :element-demo--> 之间的内容
    const commentContent = content.slice(commentStart + startTagLen, commentEnd); 
    // 获取template标签里的内容
    const html = stripTemplate(commentContent);
    // 获取Script标签里的内容
    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);
}

我们看第一行,它是截取了<!--element-demo:  之前的代码片段。相当于把要渲染的实例之前的文字部分全部截取出去了,然后push到outpush里。

然后第二行 commentContent, 它是获取了<!--element-demo:   :element-demo--> 之间的代码片段, 接下来,调用了stripTemplate方法、stripScript方法和genInlineComponentText方法。

stripTemplate 

这个函数的功能是去除传入内容中的<script>和<style>标签及其内容,返回处理后的字符串。如注释写的那样,“编写例子时不一定有 template,所以采取的方案是剔除其他的内容”。首先使用trim()方法去除字符串两端的空格,然后通过正则表达式替换掉<script>和<style>标签及其内容,最后再次使用trim()方法去除可能产生的两端空格。如果处理后的字符串为空,则直接返回原字符串。

图7

stripScript 

这个函数用于从给定的content中提取<script>标签内的内容。它通过正则表达式匹配<script>标签及其内的所有内容,然后返回匹配结果的第二项(即<script>标签内的内容),如果没匹配到则返回空字符串。

图8

genInlineComponentText 

这段代码有点长,就直接放代码片段。这段代码有点难理解,但最主要的就是compileTemplate。它的作用是将Vue模板语法转换为可执行的JavaScript渲染函数。也就是我们将代码的template里的html代码交给compileTemplate处理,然后返回给我们处理后的可渲染的html代码。

然后tips和errors就是处理一些错误。

下面的script处理了传入进来的script标签里的代码,对传入的script进行处理,如果存在内容,则替换开头的export defaultconst democomponentExport =,以适应内联组件的定义方式(也就是上面的filename:inline-component)。若脚本为空,则赋予默认的空对象{}作为导出内容。

最后将模板的渲染函数、静态渲染函数及整合后的脚本导出内容包装在一个立即执行的函数表达式中,确保组件的定义是独立且不会污染全局命名空间。

最重要的这个函数返回的是立即执行表达式去渲染我们的md文档中写的template和script,这也就是为什么我们在md文档里写例子,在页面上就能执行这个组件的渲染。

function genInlineComponentText(template, script) {
  // https://github.com/vuejs/vue-loader/blob/423b8341ab368c2117931e909e2da9af74503635/lib/loaders/templateLoader.js#L46
  const finalOptions = {
    source: `<div>${template}</div>`,
    filename: 'inline-component', // TODO:这里有待调整
    compiler
  };

  // const { compileTemplate } = require('@vue/component-compiler-utils');
  const compiled = compileTemplate(finalOptions);
  // tips
  if (compiled.tips && compiled.tips.length) {
    compiled.tips.forEach(tip => {
      console.warn(tip);
    });
  }
  // errors
  if (compiled.errors && compiled.errors.length) {
    console.error(
      `\n  Error compiling template:\n${pad(compiled.source)}\n` +
        compiled.errors.map(e => `  - ${e}`).join('\n') +
        '\n'
    );
  }
  let demoComponentContent = `
    ${compiled.code}
  `;
  // todo: 这里采用了硬编码有待改进
  script = script.trim();
  if (script) {
    script = script.replace(/export\s+default/, 'const democomponentExport =');
  } else {
    script = 'const democomponentExport = {}';
  }
  demoComponentContent = `(function() {
    ${demoComponentContent}
    ${script}
    return {
      render,
      staticRenderFns,
      ...democomponentExport
    }
  })()`;
  return demoComponentContent;
}

回到while函数。demoComponentName定义了组件的名字,然后放入slot="source"的插槽。然后定义了componenetsString,定义了组件的名字和其对应的内容,在下面会渲染。然后就是将id、start、commentStart和commentEnd全部更新,去找下一个demo例子。

4)pageScript

可以看见这个if就是如过当我们上面的while函数执行了,那么componentsString肯定是有值的,那么就将生成一个默认的Vue组件脚本结构,包含这些组件,并将其赋值给pageScript。

如果没有componenetsString,那么会检查Markdown内容content的开头是否直接包含了<script>标签。如果是,则认为Markdown中直接定义了组件,并尝试提取这部分内容作为pageScript。这在elementui中的某些情况是适用的,比如color.md,这里面就没有直接使用demo例子,而是在md文档中使用了script标签,如图9。可以看见我搜索:::demo,是并没有的,而在开头就有一段script脚本,具体作用大家可以去看color.md源码。

  // 仅允许在 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);
  }

 图9

5)return

最后return这个内容大家可能就一下子清晰了,返回的内容是template模版和script,这不就是一个vue文件所需要的吗,只不过template是渲染了当前页面所有demo的html,pagescript是定义了当前页面的所有组件。

图10

6)demo-block

我们最后简单看下demo-block组件。主要看下它的html结构。

可以看见 

  • <slot name="source"></slot>, 
  • <div class="description" v-if="$slots.default"> <slot></slot> </div>​​​​​​​
  • <div class="highlight"> <slot name="highlight"></slot> </div>
<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
      class="demo-block-control"
      ref="control"
      :class="{ 'is-fixed': fixedControl }"
      @click="isExpanded = !isExpanded"
    >
      <transition name="arrow-slide">
        <i :class="[iconClass, { hovering: hovering }]"></i>
      </transition>
      <transition name="text-slide">
        <span v-show="hovering">{{ controlText }}</span>
      </transition>
    </div>
  </div>
</template>

我们结合elementui的demo来看一下。我用对应颜色标记了代码和渲染的部分,然后大家结合上面代码观察,再想一想之前篇章中介绍的返回的内容,大家应该一下就清晰了。

3.总结 

我们的elementui-md渲染部分到这里就结束了,其实关于elementui的源码还有很多地方值得学习的,大家可以自己下去看看elementui源码,比如其中关于tpl文件的应用,关于国际化的应用、关于scss文件的处理等等。以后会慢慢攥写更多文章,希望大家多多支持。一起进步~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/721044.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MyBatis逆向工程和MyBatisX插件的使用

文章目录 1.ORM思维2.逆向工程3.MyBatisX插件的使用 1.ORM思维 ORM&#xff08;Object-Relational Mapping&#xff0c;对象-关系映射&#xff09;是一种将数据库和面向对象编程语言中的对象之间进行转换的技术。它将对象和关系数据库的概念进行映射&#xff0c;最后我们就可以…

发生了什么?法国市值蒸发超1.8万亿元

KlipC报道&#xff1a;一周前&#xff0c;法国总统马克龙意外宣布提前举行国会议员选举&#xff0c;引发了法国政坛遭受巨震。仅仅一周内&#xff0c;法国股市遭受重大打击&#xff0c;其市值蒸发了约2580亿美元&#xff08;约1.8万亿人民币&#xff09;。 多家大型金融机构多…

【Go语言精进之路】构建高效Go程序:了解string实现原理并高效使用

&#x1f525; 个人主页&#xff1a;空白诗 &#x1f525; 热门专栏&#xff1a;【Go语言精进之路】 文章目录 引言一、Go语言的字符串类型1.1 字符串的定义1.2 字符串的零值可用1.3 字符串的不可变性1.4 字符串的拼接1.5 字符串的常用方法1.6 实际使用示例 二、字符串的内部表…

【Git】多人协作 -- 详解

一、多人协作&#xff08;1&#xff09; ⽬前&#xff0c;我们所完成的工作如下&#xff1a; 基本完成 Git 的所有本地库的相关操作&#xff0c;git 基本操作&#xff0c;分支理解&#xff0c;版本回退&#xff0c;冲突解决等等。 申请码云账号&#xff0c;将远端信息 clone…

【牛客面试必刷TOP101】Day33.BM70 兑换零钱(一)和BM71 最长上升子序列(一)

文章目录 前言一、BM70 兑换零钱(一)题目描述题目解析二、BM71 最长上升子序列(一)题目描述题目解析总结 前言 一、BM70 兑换零钱(一) 题目描述 描述&#xff1a; 给定数组arr&#xff0c;arr中所有的值都为正整数且不重复。每个值代表一种面值的货币&#xff0c;每种面值的货币…

数据安全未来之路,天空卫士荣誉领榜《中国数据安全50强(2024)》

《中国数据安全50强&#xff08;2024&#xff09;》 数世咨询首份《中国数据安全50强&#xff08;2024&#xff09;》报告发布。天空卫士凭借其卓越的技术创新、市场领导力、业务收入能力和企业发展能力&#xff0c;在众多竞争者中脱颖而出&#xff0c;荣登50强榜单&#xff0…

yolov5模型pt转engine

目录 1. 环境准备1.1 安装tensorrt1.1.1 pip安装1.1.2 压缩包安装 2. pt转engine 1. 环境准备 1.1 安装tensorrt 1.1.1 pip安装 pip install tensorrt 1.1.2 压缩包安装 很可能会失败&#xff0c;最保险的方法是下载tensorRT的压缩包&#xff0c;比如&#xff1a;下载Tenso…

分享:2024年(第12届)“泰迪杯”数据挖掘挑战赛省级奖项获奖名单公示

本次竞赛有评选省奖的省份有广东省、广西壮族自治区、河北省、湖北省。各省奖项依据“泰迪杯”全国评审专家组统一评阅的最终成绩区分省份后从高到低依序按比例产生。 广东省 省级奖项获奖名单公示 奖项设置&#xff1a; 一等奖&#xff1a;约占该省份队伍总数的5%&#xff0…

【正则表达式】入门

参考视频&#xff1a;10分钟快速掌握正则表达式_哔哩哔哩_bilibili 这个网站用来测试写的正则表达式效果&#xff1a;regex101: build, test, and debug regex 示例&#xff1a; 限定符 ? 表示前一个字符可有可无 比如这里输入&#xff1a;de? 匹配结果可以得到d和de * 前…

【Android WebView】WebView基础

一、简介 WebView是一个基于webkit引擎、展现web页面的控件。Android的Webview在低版本和高版本采用了不同的webkit版本内核&#xff0c;4.4后直接使用了Chrome。 二、重要类 以WebView类为基础&#xff0c;WebSettings、WebViewClient、WebChromeClient为辅助共同完成安卓段加…

微服务中的相关概念

Eureka Eureka 是由 Netflix 开发的一个服务发现和注册中心&#xff0c;广泛应用于微服务架构中。Eureka 主要用于管理和协调分布式服务的注册和发现&#xff0c;确保各个服务之间能够方便地找到并通信。它是 Netflix OSS&#xff08;Netflix Open Source Software&#xff09…

指针和数组

同一指针相减的绝对值得到的是之间元素的个数 #include"stdio.h" #include"string.h" int main() {int arr[10]{0};printf("%d\n",&arr[9]- &arr[0]);return 0; } 不同类型的指针减去指针没有意义 地址加地址&#xff0c;指针加指针没…

UE5 C++ 跑酷游戏练习 Part1

一.修改第三人称模板的 Charactor 1.随鼠标将四处看的功能的输入注释掉。 void ARunGANCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) {// Set up action bindingsif (UEnhancedInputComponent* EnhancedInputComponent CastChecked&…

【Linux基础IO】常见的对文件操作的函数、文件描述符fd、访问文件的本质分析

目录 fopen函数 chdir函数 fclose函数 fwrite和fread函数 open函数 umask函数 write函数 read函数 close函数 文件描述符fd 进程访问文件的本质分析 fopen函数 参数mode&#xff1a; w方式打开文件&#xff1a;1、如果被打开文件不存在&#xff0c;系统会在使用fopen函…

玄机平台流量特征分析-常见攻击事

前言 熟悉常见的攻击流量特征&#xff0c;我们就可以通过主机的一个流量情况来判断主机遭受了何种攻击。这里来看看玄机平台的一道题目。 步骤1.1 这里需要我们找出恶意扫描者&#xff0c;也就是黑客的ip。下载好附件之后用wiresharke打开&#xff0c;直接筛选http协议的流量…

CSS【实战】抽屉动画

效果预览 技术要点 实现思路 元素固定布局&#xff08;fixed&#xff09;在窗口最右侧外部js 定时器改变元素的 right 属性&#xff0c;控制元素移入&#xff0c;移出 过渡动画 transition transition: 过渡的属性 过渡的持续时间 过渡时间函数 延迟时间此处改变的是 right …

C# Winform 侧边栏,切换不同页面

在项目中我们经常遇到需要在主界面上切换不同子页面的需求&#xff0c;常用做法是左侧显示子页面菜单&#xff0c;用户通过点击左侧菜单&#xff0c;实现右边子页面的展示。 实例项目实现&#xff1a; 项目左侧侧边栏实现FlowLayoutPanel使用显示不同子窗体 实例链接&#xf…

行业模板|DataEase应用平台对接大屏模板推荐

DataEase开源数据可视化分析工具于2022年6月发布模板市场&#xff08;https://templates-de.fit2cloud.com&#xff09;&#xff0c;并于2024年1月新增适用于DataEase v2版本的模板分类。模板市场旨在为DataEase用户提供专业、美观、拿来即用的大屏模板&#xff0c;方便用户根据…

利用深度学习进行纹理分析以改善计算机视觉

利用深度学习进行纹理分析以改善计算机视觉 人工智能的一个独特应用领域是帮助验证和评估材料和产品的质量。在IBM&#xff0c;我们开发了创新技术&#xff0c;利用原生移动、专用微传感器技术和AI来提供实时工作的解决方案&#xff0c;利用商用智能手机技术&#xff0c;并提供…

.NET周刊【6月第3期 2024-06-18】

国内文章 记一次 .NET某游戏币自助机后端 内存暴涨分析 https://www.cnblogs.com/huangxincheng/p/18243233 文章讨论了程序中非托管内存暴涨的问题。作者描述了友人发现内存问题并请他帮助分析的背景&#xff0c;利用WinDbg工具分析Linux平台上的内存泄漏情况。文章介绍了如…