【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 的 idlet 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-inputplaceholder="请输入内容"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#L46const finalOptions = {source: `<div>${template}</div>`,filename: 'inline-component', // TODO:这里有待调整compiler};// const { compileTemplate } = require('@vue/component-compiler-utils');const compiled = compileTemplate(finalOptions);// tipsif (compiled.tips && compiled.tips.length) {compiled.tips.forEach(tip => {console.warn(tip);});}// errorsif (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><divclass="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><divclass="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.mzph.cn/web/29875.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微纳米气泡发生器是微纳米气泡产生装置 未来市场需求将不断释放

微纳米气泡发生器是微纳米气泡产生装置 未来市场需求将不断释放 微纳米气泡发生器即微纳米气泡发生设备&#xff0c;是一种将水和气体混合并产生微纳米气泡的设备。微纳米气泡是指直径在100μm以下的气泡&#xff0c;分为纳米气泡和微米气泡。   微纳米气泡发生器主要由发生设…

录屏录音两不误!电脑录屏录音软件推荐(3款)

在数字化时代&#xff0c;电脑录屏录音软件已成为教学、演示、会议记录等领域不可或缺的工具。它们能够捕捉屏幕上的每一个动作&#xff0c;同时录制音频&#xff0c;为用户提供直观、生动的视听材料。本文将详细介绍三种常用的电脑录屏录音软件&#xff0c;帮助读者了解并掌握…

误删的文件不在回收站如何找回?6个恢复秘诀分享!

“我刚刚误删了一些文件&#xff0c;但是在回收站中没有看到这部分文件&#xff0c;这种情况下还有方法可以找回误删的文件吗&#xff1f;在线等一个答案&#xff01;” 在数字化时代&#xff0c;文件的安全和完整性对于个人和企业都至关重要。然而&#xff0c;有时候由于疏忽或…

【Android】使用SeekBar控制数据的滚动

项目需求 有一个文本数据比较长&#xff0c;需要在文本右侧加一个SeekBar&#xff0c;然后根据SeekBar的上下滚动来控制文本的滚动。 项目实现 我们使用TextView来显示文本&#xff0c;但是文本比较长的话&#xff0c;需要在TextView外面套一个ScrollView&#xff0c;但是我…

利用K8S技术栈打造个人私有云

1.三个节点&#xff1a;master&#xff0c;slave&#xff0c;client 在Kubernetes集群中&#xff0c;三个节点的职责分别如下&#xff1a; Master节点&#xff1a; docker&#xff1a;用于运行Docker容器。 etcd&#xff1a;一个分布式键值存储系统&#xff0c;用于保存Kuberne…

42、基于神经网络的训练堆叠自编码器进行图像分类(matlab)

1、训练堆叠自编码器进行图像分类的原理及流程 基于神经网络的训练堆叠自编码器进行图像分类的原理和流程如下&#xff1a; 堆叠自编码器&#xff08;Stacked Autoencoder&#xff09;是一种无监督学习算法&#xff0c;由多个自编码器&#xff08;Autoencoder&#xff09;堆叠…

宝塔软件默认安装位置

自带的JDK /usr/local/btjdk/jdk8Tomcat 各个版本都在bttomcat这个文件夹下面&#xff0c;用版本区分。tomcat_bak8是备份文件 /usr/local/bttomcat/tomcat8nginx /www/server/nginxnginx配置文件存放目录 /www/server/panel/vhost/nginxredis /www/server/redismysql /…

nextjs(持续学习中)

return ( <p className{${lusitana.className} text-xl text-gray-800 md:text-3xl md:leading-normal}> Welcome to Acme. This is the example for the{’ } Next.js Learn Course , brought to you by Vercel. ); } 在顶级 /public 文件夹下提供静态资产 **默认 /…

央国企财务专家的“专家课”——中国总会计师协会联合实在智能举办RPA专项培训

近日&#xff0c;中国总会计师协会正式举办了为期五天的「财务数字化思维与实用IT技能提升」专项培训&#xff0c;吸引了来自中铁十五局集团有限公司、中国航空工业规划设计院、中核核电运行管理有限公司、中国北方车辆有限公司、一汽物流有限公司等国企、事业单位及民营企业共…

【权威出版/投稿优惠】2024年水利水电与能源环境科学国际会议(WRHEES 2024)

2024 International Conference on Water Resources, Hydropower, Energy and Environmental Science 2024年水利水电与能源环境科学国际会议 【会议信息】 会议简称&#xff1a;WRHEES 2024 大会时间&#xff1a;点击查看 截稿时间&#xff1a;点击查看 大会地点&#xff1a;…

【Linux】进程间通信3——system V共享内存

1.system V进程间通信 管道通信本质是基于文件的&#xff0c;也就是说操作系统并没有为此做过多的设计工作&#xff0c;而system V IPC是操作系统特地设计的一种通信方式。但是不管怎么样&#xff0c;它们的本质都是一样的&#xff0c;都是在想尽办法让不同的进程看到同一份由操…

压力应变桥信号变送光电隔离放大模块PCB焊接式 差分信号输入0-10mV/0-20mV/0-±10mV/0-±20mV转0-5V/0-10V/4-20mA

概述&#xff1a; IPO压力应变桥信号处理系列隔离放大器是一种将差分输入信号隔离放大、转换成按比例输出的直流信号混合集成厚模电路。产品广泛应用在电力、远程监控、仪器仪表、医疗设备、工业自控等行业。该模块内部嵌入了一个高效微功率的电源&#xff0c;向输入端和输出端…

【深度解析】1688货源拿货价比拼多多贵?为何商家仍选1688

对电商卖家来说&#xff0c;首先需要解决的问题就是货源。 虽然知道1688是个大型综合性的货源平台&#xff0c;但很多卖家还是觉得1688上面的货源很贵&#xff0c;甚至在平台严查无货源的背景下&#xff0c;还是坚持从1688拿货。那么为什么有些拼多多的商品会比1688还便宜呢&a…

openipc:/etc/init.d/目录下的所有启动文件启动分析

openipc /etc/init.d/目录下的所有文件 启动文件解析参考&#xff1a;https://blog.csdn.net/qq_37212828/article/details/107227965 1. rcS文件&#xff0c;是本目录在开机时最先启动的文件&#xff1a; 它会依次启动/etc/init.d/目录下依S开头的文件 #!/bin/sh # Start a…

探索线性回归模型

目录 一 线性回归的理论基础 什么是线性回归? 线性回归的假设 最小二乘法 二 线性回归的数学推导 线性回归参数的推导 多元线性回归 三 线性回归的模型构建 数据准备 训练模型 模型评估 四 线性回归的代码实现 基本实现 多元线性回归 五 线性回归的应用场景 预…

openh264 运动估计搜索原理源码分析

运动估计搜索 运动估计搜索是视频编码中的一个重要步骤&#xff0c;它用于确定视频序列中两个帧之间的运动向量&#xff08;MV&#xff09;。这些运动向量用于预测帧之间的运动&#xff0c;从而减少编码所需的数据量。以下是运动估计搜索的一些关键概念和步骤&#xff1a; 运动…

贪心算法——赶作业(C++)

慢慢来&#xff0c;沉稳一点。 2024年6月18日 题目描述 A同学有n份作业要做&#xff0c;每份作业有一个最后期限&#xff0c;如果在最后期限后交作业就会扣分&#xff0c;现在假设完成每份作业都需要一天。A同学想安排作业顺序&#xff0c;把扣分降到最低&#xff0c;请帮他实…

易备防勒索备份方案与成功案例

随着信息化的发展&#xff0c;数据安全的重要性愈加突出。据 Hiscox 全球网络安全统计&#xff0c;在勒索软件攻击事件当中&#xff0c;64%以上的用户是中小企业。因此&#xff0c;制定完善的灾备策略&#xff0c;是抵御网络威胁的终极方案。而在诸多数据备份方案中&#xff0c…

【Linux】进程控制1——进程创建和进程终止

1.进程创建 1.1.再谈fork 在linux中fork函数时非常重要的函数&#xff0c;它从已存在进程中创建一个新进程。新进程为子进程&#xff0c;而原进程为父进程。 #include <unistd.h> pid_t fork(void);//pid_t为整形 返回值&#xff1a;子进程中的fork()返回0&#xff…

内置类型不够用?试试Python内置类型子类化!

目录 1、经典继承法:直接子类化内置类型 🧬 1.1 了解Python内置类型 1.2 实现子类化的基础步骤 步骤1:定义子类 步骤2:添加自定义行为 步骤3:使用子类 1.3 实战:子类化列表list示例 1.4 优化:重写魔法方法实现自定义行为 2、高级技巧:元类介入定制 🪐 2.1 …