vue2使用MarkDown的回显与生成自定义目录

最终实现效果图:

1.回显markdown

1.1安装mark
npm install marked -s
1.2使用
//导入
import {
		marked
	} from 'marked'
//
data(){
    return{
        textDatas: "",
    }
},
    methods: {
        getData() {//获取数据
            axios({
                url: "http://localhost:8889/articles/view/1585140205713166338",
                method: "post"
            }).then(res => {
                if (res.status == 200) {
                    this.textDatas = res.data.data.body.content
                }
            })
        }
    },
        computed: {
            content() {//使用计算属性渲染
                var reg =new RegExp("<pre","g")//这里是处理使用prism插件后显示行号
                var content=marked(this.textDatas)
                var str=content.replace(reg,`<pre class='line-numbers'`)
                return str
            }
        }
<template>
	<div class="content">
		<div>
			<div v-html="content" class="markdown-body html-content"></div>
		</div>
	</div>
</template>

此时页面应渲染出来了,但是没有markdown的样式!

 

1.3下载样式并导入
#下载样式
npm install github-markdown-css
#导入
import "github-markdown-css"
#并使用类名
<div v-html="content"  class="markdown-body"></div>

扩展:如果还是觉得默认的效果不满意,可以去这里:Themes Gallery — Typora 选择你想要的主题换上去。使用也很简单,选择自己想要的模板,点击下载样式就好了。

1.4代码高亮(1)

官网地址:Prism

//安装prismjs插件
npm install prismjs -S
 
//安装prismjs的编译器插件
npm install babel-plugin-prismjs -D

安装完毕会生成一个babel.config.js

//修改对应配置,重启项目
module.exports = {
	presets: [
		'@vue/cli-plugin-babel/preset'
	],
	plugins: [
		["prismjs",
			{
				languages: ["javascript", "css", "markup",
					"html", "php", "dart", "bash", "nginx", "sql", "c",
					"cpp", "python", "go", "java", "bash", "json"
				],
				plugins: [
					"line-numbers",
					"show-language",
					"copy-to-clipboard"
				], //配置显示行号插件
				theme: "okaidia", //主体名称 coy tomorrow 或者看官网选主题
				css: true
			}
		]
	]
}
//导入
import prism from "prismjs";

//调用
prism.highlightAll()

注意prism.highlightAll()并不会自己生效,所以我们需要监听对应显示的数据调用该方法。  

watch: {
			textDatas() {
				this.$nextTick(() => {
					setTimeout(() => {
						prism.highlightAll()
						// hljs.highlightAll()
					}, 0)
				})
			}
		},
1.5代码高亮(2)

使用highlight.js

官网:highlight.js

npm install highlight.js -S
import hljs from 'highlight.js'

//引入一种语法的高亮 【注意】要引入这个css 不然可能会失效
import 'highlight.js/styles/atom-one-light.css' 
// 高亮语法
hljs.highlightAll()

 这里的hljs.highlightAll()不确实是否要监听!!!

 

生成目录

 

	import $ from 'jquery'
<el-tree class="tree" ref="tree" node-key="uuid" :data="treeData" :props="defaultProps" default-expand-all>
    <div class="custom-tree-node" slot-scope="{ node, data }">

        <div @click="toDiv(data.uuid)">{{ data.text }}</div>
        <!-- 	<a class="anchor" :href="`#${data.uuid}`">
{{ data.text }}
</a> -->
    </div>
</el-tree>
data() {
    return {
        treeData: [],
        defaultProps: {
            label: 'text',
            children: 'children',
        }
    }
},

 方法借鉴于:https://segmentfault.com/a/1190000040462508

1.获取目录
// 根据内容获取目录
getCatalog() {
    const h = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'a']
    var elements = $(':header')
    let hElements = []
    for (const key of elements) {
        if (h.indexOf(key.localName) > -1) {
            let text
            if (key.children && key.children.length) {
                text = this.getText(key.children)
            } else {
                text = key.innerHTML
            }
            hElements.push({
                hLevel: parseInt(key.localName[1]),
                text,
                id: key.localName,
                uuid: key.id,
            })
        }
    }
    // console.log('hElements:', hElements)
    let result = this.toTree(hElements)
    this.treeData = result

    // 目录默认选中第一个
    this.$nextTick(() => {
        if (!result) return
        this.$refs.tree.setCurrentKey(result[0].uuid)
    })
    // console.log('result:', result)
}
2.生成树tree
toTree(flatArr) {
    var tree = []
    var copyArr = flatArr.map(function(item) {
        return item
    })

    // 根据指定级别查找该级别的子孙级,并删除掉已经查找到的子孙级
    var getChildrenByLevel = function(currentLevelItem, arr, level) {
        if (!currentLevelItem) {
            return
        }
        // 将level值转成负数,再进行比较
        var minusCurrentLevel = -currentLevelItem.hLevel
        var children = []
        for (var i = 0, len = arr.length; i < len; i++) {
            var levelItem = arr[i]
            if (-levelItem.hLevel < minusCurrentLevel) {
                children.push(levelItem)
            } else {
                // 只找最近那些子孙级
                break
            }
        }
        // 从数组中删除已经找到的那些子孙级,以免影响到其他子孙级的查找
        if (children.length > 0) {
            arr.splice(0, children.length)
        }
        return children
    }

    var getTree = function(result, arr, level) {
        // 首先将数组第一位移除掉,并添加到结果集中
        var currentItem = arr.shift()

        currentItem.level = level
        result.push(currentItem)
        while (arr.length > 0) {
            if (!currentItem) {
                return
            }
            // 根据当前级别获取它的子孙级
            var children = getChildrenByLevel(currentItem, arr, level)
            // 如果当前级别没有子孙级则开始下一个
            if (children.length == 0) {
                currentItem = arr.shift()
                currentItem.level = level
                if (currentItem) {
                    result.push(currentItem)
                }
                continue
            }
            currentItem.children = []
            // 查找到的子孙级继续查找子孙级
            getTree(currentItem.children, children, level + 1)
        }
    }
    getTree(tree, copyArr, 1)
    return tree
},
    getText(arr) {
        let result = null
        if (!arr.length) return
        for (let i = 0; i < arr.length; i++) {
            if (arr[i].children && arr[i].children.length) {
                result = this.getText(arr[i].children)
            } else {
                result = arr[i].innerHTML
            }
        }
        return result
    },
3.点击指定位置页面跳转
toDiv(uuid) {
console.log(uuid);
$("html,body").animate({
scrollTop: document.getElementById(uuid).offsetTop
}, 500);
},

 注意样式可能会影响页面滚动!!!

 

.content {
    display: flex;
    height: 100%;
    position: relative;
}
.tree {
    width: 400px;
    height: 100vh;
    position: sticky;
    top: 0;
    overflow-y: scroll;
    border-right: 1px solid gainsboro;
}
.html-content {
    width: 800px;
    height: 100%;
}
.markdown-body {
    text-align: left;
}

最终效果:

完整案例

<template>
	<div class="content">

		<el-tree class="tree" ref="tree" node-key="uuid" :data="treeData" :props="defaultProps" default-expand-all>
			<div class="custom-tree-node" slot-scope="{ node, data }">

				<div @click="toDiv(data.uuid)">{{ data.text }}</div>
				<!-- 	<a class="anchor" :href="`#${data.uuid}`">
					{{ data.text }}
				</a> -->
			</div>
		</el-tree>
		<div>
			<div v-html="content" class="markdown-body html-content"></div>
		</div>
	</div>
</template>

<script>
	import "github-markdown-css"
	import hljs from 'highlight.js'
	import prism from "prismjs";
	import 'highlight.js/styles/atom-one-light.css' //引入一种语法的高亮
	import {
		marked
	} from 'marked'
	import axios from 'axios'
	import $ from 'jquery'
	export default {
		data() {
			return {
				treeData: [],
				defaultProps: {
					label: 'text',
					children: 'children',
				},
				textDatas: "",
				textData: "# JavaScript高级程序设计\n## 内容简介\n   本书是JavaScript经典图书的新版。第4版全面、深入地介绍了JavaScript开发者必须掌握的前端开发技术,涉及JavaScript的基础特性和高级特性。书中详尽讨论了JavaScript的各个方面,从JavaScript的起源开始,逐步讲解到新出现的技术,其中重点介绍ECMAScript和DOM标准。在此基础上,接下来的各章揭示了JavaScript的基本概念,包括类、期约、迭代器、代理,等等。另外,书中深入探讨了客户端检测、事件、动画、表单、错误处理及JSON。本书同时也介绍了近几年来涌现的重要新规范,包括Fetch API、模块、工作者线程、服务线程以及大量新API。\n## 作者简介\n马特·弗里斯比(Matt Frisbie),Stealth Startup公司CTO,曾担任谷歌公司软件工程师,精通前端技术,拥有十余年Web开发经验,除本书外另著有AngularJS等前端主题图书。毕业于伊利诺伊大学厄巴纳-尚佩恩分校。\n## 目录\n### 第 1章 什么是JavaScript 1\n#### 1.1 简短的历史回顾 1\n#### 1.2 JavaScript实现 2\n#### 1.3 JavaScript版本 9\n#### 1.4 小结 10\n### 第 2章 HTML中的JavaScript 11\n#### 2.1 script元素 11\n#### 2.2 行内代码与外部文件 18\n#### 2.3 文档模式 18\n#### 2.4 noscript元素 19\n测试代码\n``` javascript\nvar a = 1;\n\nfunction fun(){\n  console.log(11111)\n}\nfun()\nconosole.log(a)\n```\n\n#### 2.5 小结 20\n### 第3章 语言基础 21\n#### 3.1 语法 21\n#### 3.2 关键字与保留字 23\n#### 3.3 变量 24\n#### 3.4 数据类型 30\n#### 3.5 操作符 56\n#### 3.6 语句 73\n#### 3.7 函数 80\n#### 3.8 小结 82\n### 第4章 变量、作用域与内存 83\n#### 4.1 原始值与引用值 83\n#### 4.2 执行上下文与作用域 87\n#### 4.3 垃圾回收 94\n#### 4.4 小结 101"
			}
		},
		mounted() {
			this.getData()

		},
		computed: {
			content() {
				var reg =new RegExp("<pre","g")
				var content=marked(this.textDatas)
				var str=content.replace(reg,`<pre class='line-numbers'`)
				return str
			}
		},
		watch: {
			textDatas() {
				this.$nextTick(() => {
					setTimeout(() => {
						prism.highlightAll()
						// hljs.highlightAll()
					}, 0)
				})
			}
		},
		methods: {
			getData() {
				axios({
					url: "http://localhost:8889/articles/view/1585140205713166338",
					method: "post"
				}).then(res => {
					if (res.status == 200) {
						
						this.textDatas = res.data.data.body.content
						this.$nextTick(() => {
							setTimeout(() => {
								this.getCatalog()
							}, 0)
						})
					}
				})
			},
			// 根据内容获取目录
			getCatalog() {
				const h = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'a']
				var elements = $(':header')
				let hElements = []
				for (const key of elements) {
					if (h.indexOf(key.localName) > -1) {
						let text
						if (key.children && key.children.length) {
							text = this.getText(key.children)
						} else {
							text = key.innerHTML
						}
						hElements.push({
							hLevel: parseInt(key.localName[1]),
							text,
							id: key.localName,
							uuid: key.id,
						})
					}
				}
				// console.log('hElements:', hElements)
				let result = this.toTree(hElements)
				this.treeData = result

				// 目录默认选中第一个
				this.$nextTick(() => {
					if (!result) return
					this.$refs.tree.setCurrentKey(result[0].uuid)
				})
				// console.log('result:', result)
			},
			toTree(flatArr) {
				var tree = []
				var copyArr = flatArr.map(function(item) {
					return item
				})

				// 根据指定级别查找该级别的子孙级,并删除掉已经查找到的子孙级
				var getChildrenByLevel = function(currentLevelItem, arr, level) {
					if (!currentLevelItem) {
						return
					}
					// 将level值转成负数,再进行比较
					var minusCurrentLevel = -currentLevelItem.hLevel
					var children = []
					for (var i = 0, len = arr.length; i < len; i++) {
						var levelItem = arr[i]
						if (-levelItem.hLevel < minusCurrentLevel) {
							children.push(levelItem)
						} else {
							// 只找最近那些子孙级
							break
						}
					}
					// 从数组中删除已经找到的那些子孙级,以免影响到其他子孙级的查找
					if (children.length > 0) {
						arr.splice(0, children.length)
					}
					return children
				}

				var getTree = function(result, arr, level) {
					// 首先将数组第一位移除掉,并添加到结果集中
					var currentItem = arr.shift()

					currentItem.level = level
					result.push(currentItem)
					while (arr.length > 0) {
						if (!currentItem) {
							return
						}
						// 根据当前级别获取它的子孙级
						var children = getChildrenByLevel(currentItem, arr, level)
						// 如果当前级别没有子孙级则开始下一个
						if (children.length == 0) {
							currentItem = arr.shift()
							currentItem.level = level
							if (currentItem) {
								result.push(currentItem)
							}
							continue
						}
						currentItem.children = []
						// 查找到的子孙级继续查找子孙级
						getTree(currentItem.children, children, level + 1)
					}
				}
				getTree(tree, copyArr, 1)
				return tree
			},
			getText(arr) {
				let result = null
				if (!arr.length) return
				for (let i = 0; i < arr.length; i++) {
					if (arr[i].children && arr[i].children.length) {
						result = this.getText(arr[i].children)
					} else {
						result = arr[i].innerHTML
					}
				}

				return result
			},
			toDiv(uuid) {
				console.log(uuid);
				// document.getElementById(uuid).scrollIntoView({
				// 	behavior: 'smooth',
				// })
				$("html,body").animate({
					scrollTop: document.getElementById(uuid).offsetTop
				}, 500);
			},
		}
	}
</script>

<style>
	.content {
		display: flex;
		height: 100%;
		position: relative;
	}
	.tree {
		width: 400px;
		height: 100vh;
		position: sticky;
		top: 0;
		overflow-y: scroll;
		border-right: 1px solid gainsboro;
	}
	.html-content {
		width: 800px;
		height: 100%;
	}
	.markdown-body {
		text-align: left;
	}
</style>

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

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

相关文章

操作系统科普与入门之进程篇

文章目录 ⭐前言一、浅谈OS的各个管理模块对应的硬件资源二、从OS的各个管理模块浅谈进程管理2.1 什么是进程&#xff1f;2.2 我知道进程是啥啦&#xff0c;那么OS怎么知道进程的呢&#xff1f; 三、OS是如何进行进程管理&#xff1f;3.1 进程状态转换3.1.1 创建态3.1.2 运行态…

深度学习5 神经网络

生物神经网络是指人的大脑&#xff0c;这是人工神经网络的技术原型。根据生物神经网络的原理&#xff0c;人们用计算机复现了简化的神经网络。当然&#xff0c;人工神经网络是机器学习的一大分支。 1.基本组成 1.1神 经 元 神经元是神经网络的基本组成。激活函数又称作激励函…

所有权与生命周期:Rust 内存管理的哲学

所有权与生命周期&#xff1a;Rust内存管理的哲学 博主寄语引言&#xff1a;编程语言的内存管理困境与 Rust 的解决方案。所有权基本概念&#xff1a;资源的绝对主权生命周期的理解与应用&#xff1a;编译时的守护神借用与引用的精妙设计&#xff1a;安全与效率的和谐共舞Rust …

VL02N 创建过账时删除订单号显示

VL02N 删除订单号显示 VL02N 交货过账 删除 交货单 & 物料凭证 & 会计凭证 上的订单号值 目录 VL02N 删除订单号显示 目录 交货单订单号值删除物料凭证订单号值删除会计凭证订单号删除 删除BSEG表的订单号值删除ACDOCA表的订单号值 交货单订单号值删除 增强点L…

WSL-Ubuntu20.04训练环境配置

1.YOLOv8训练环境配置 训练环境配置的话就仍然以YOLOv8为例&#xff0c;来说明如何配置深度学习训练环境。这部分内容比较简单&#xff0c;主要是安装miniAnaconda以及安装torch和torchvision. 首先是miniAnaconda的安装(参考官网的教程Miniconda — Anaconda )&#xff0c;执行…

记录些Spring+题集(1)

接口防刷机制 接口被刷指的是同一接口被频繁调用&#xff0c;可能是由于以下原因导致&#xff1a; 恶意攻击&#xff1a;攻击者利用自动化脚本或工具对接口进行大量请求&#xff0c;以消耗系统资源、拖慢系统响应速度或达到其他恶意目的。误操作或程序错误&#xff1a;某些情…

蓝牙定位系统有什么优势?有哪些强大功能?

蓝牙定位系统研发出来后&#xff0c;为企业和员工带来了很大的便利&#xff0c;极大推动了厂区安全稳定的发展。该系统由于实用性广泛&#xff0c;例如&#xff1a;消防、医院、养老院、化工厂等地都可以看到他的身影&#xff0c;快速精准的定位&#xff0c;不仅省时省力而且还…

烟雾识别技术在火灾预防中的应用:思通数科大模型的力量

引言 火灾是导致生命财产损失的重大灾害之一。早期检测和快速响应是预防火灾和减少损失的关键。结合思通数科大模型的烟雾识别技术&#xff0c;为实时检测和精确定位烟雾来源提供了一种高效的解决方案。本文将探讨这一技术如何有效预防火灾并保障人员安全。 烟雾识别技术概述 …

防火墙---带宽管理

防火墙的带宽管理&#xff1a;是指对防火墙设备的带宽进行管理和控制&#xff0c;以确保网络流量的合理分配和优化网络性能 带宽管理&#xff1a;是指限制网络流量的速率或控制网络流量的优先级&#xff0c;以确保网络的性能和可用性 核心&#xff1a; 带宽限制&#xff1a;…

You are running Vue in development mode.和undefined is not iterable白屏问题

遇到的报错信息如下&#xff0c; 你正在开发模式下运行 Vue。 确保在部署生产环境时打开生产模式 但是我是关闭了的Vue.config.productionTip false 最后发现是服务器问题

微信小程序基本语法

官网 https://developers.weixin.qq.com/miniprogram/dev/framework/ 视频教程&#xff1a;尚硅谷微信小程序开发教程&#xff0c;2024最新微信小程序项目实战&#xff01; 仿慕尚花坊项目源码&#xff1a;https://gitee.com/abcdfdewrw/flower-workshop 目录 一&#xff0c;初…

论 Suspense 组件在 Vue 3 中的重要性

大家好,我是CodeQi! 一位热衷于技术分享的码仔。 你是否曾经遇到过在加载大量数据时,界面卡顿或是空白的问题? 如果你正在开发一个复杂的前端项目,那么一定需要处理很多异步数据请求。而异步请求太多就会导致用户看到空白屏幕时间变长,这对用户体验非常不友好。🤔 在…

redis 配置文件参数详解

1、redis.conf 通用类 Redis的配置文件是一个文本文件&#xff0c;通常名为redis.conf。以下是一些常见配置项的解释和示例&#xff1a; 1、bind 127.0.0.1&#xff1a;绑定的主机地址 2、 protected-mode ,默认是开启状态&#xff0c;一般不需要修改&#xff0c;可以保证服务…

vue3 中 lottie-web 封装组件

用到的JSON文件在“我的资源”里&#xff0c;下面这个链接直达 下面的代码中用到的JSON数据源 Lottie.vue <script setup> import { ref, onMounted } from vue import lottie from lottie-web// 设置组件参数 const props defineProps({renderer: {type: String,def…

【Redis】哨兵(sentinel)

文章目录 一、哨兵是什么&#xff1f;二、 哨兵sentinel文件参数三、 模仿主机redis宕机四、哨兵运行流程和选举原理SDOWN主观下线ODOWN客观下线 五、 使用建议 以下是本篇文章正文内容 一、哨兵是什么&#xff1f; 哨兵巡查监控后台master主机是否故障&#xff0c;如果故障了…

鸿蒙Harmony--文本组件Text属性详解

金樽清酒斗十千&#xff0c;玉盘珍羞直万钱。 停杯投箸不能食&#xff0c;拔剑四顾心茫然。 欲渡黄河冰塞川&#xff0c;将登太行雪满山。 闲来垂钓碧溪上&#xff0c;忽复乘舟梦日边。 行路难&#xff0c;行路难&#xff0c;多歧路&#xff0c;今安在&#xff1f; 长风破浪会有…

手机m4a怎么转换成mp3,手机端即可完成格式转换

M4A&#xff08;MPEG-4 Audio&#xff09;是一种无损压缩的音频格式&#xff0c;通常用于苹果设备和 iTunes 上&#xff0c;因为它能提供较高的音质同时占用较小的存储空间。 然而&#xff0c;MP3 作为最普及的音频格式之一&#xff0c;兼容性更强&#xff0c;几乎所有的播放设…

【MySQL进阶篇】索引

1、索引概述 索引&#xff08;Index&#xff09;是帮助MySQL高效获取数据的数据结构&#xff08;有序&#xff09;。在数据之外&#xff0c;数据库系统还维护着满足特定查找算法的数据结构&#xff0c;这些数据结构以某种方式引用&#xff08;指向&#xff09;数据&#xff0c…

Leetcode算法题(链表的中间节点+返回倒数第k个节点+合并两个有序链表)

题目1&#xff1a; 本题力扣链接&#xff1a;https://leetcode.cn/problems/middle-of-the-linked-list/solutions/164351/lian-biao-de-zhong-jian-jie-dian-by-leetcode-solut/ 思路1&#xff1a;单指针法 首先我们对链表进行遍历&#xff0c;记录链表的总长度N&#xff0c;…