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

2024-06-16 06:12

本文主要是介绍【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文件的处理等等。以后会慢慢攥写更多文章,希望大家多多支持。一起进步~

这篇关于【elementui源码解析】如何实现自动渲染md文档-第四篇的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1065666

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P