vue使用marked.js实现markdown转html并提取标题生成目录
Posted yhQuan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue使用marked.js实现markdown转html并提取标题生成目录相关的知识,希望对你有一定的参考价值。
<template> <div class="wrapper"> <div class="container"> <div class="menu"> <ul class="menu-list"> <li v-for="(nav, index) in navList" :key="index" :class="{on: activeIndex === index}" @click="currentClick(index)"> <a href="javascript:;" :key="nav.index" @click="pageJump(nav.index)">{{nav.title}} </a> <div v-if="nav.children.length > 0 && activeIndex === index" class="menu-children-list"> <ul class="nav-list"> <li v-for="(item, index) in nav.children" :key="index"> <a href="javascript:;" :key="item.index" @click.stop="pageJump(item.index)">{{item.title}} </a> </li> </ul> </div> </li> </ul> </div> <div class="content" v-html="compiledMarkdown" ref="helpDocs" @scroll="docsScroll"></div> </div> </div> </template>
<script> import marked from ‘marked‘; let rendererMD = new marked.Renderer(); marked.setOptions({ renderer: rendererMD, gfm: true, tables: true, breaks: false, pedantic: false, sanitize: false, smartLists: true, smartypants: false }); export default { props: [‘mdContent‘], data() { return { navList: [], activeIndex: 0, docsFirstLevels: [] } }, mounted() { this.navList = this.handleNavTree(); this.getDocsFirstLevels(0); }, methods: { getDocsFirstLevels(times) { // 解决图片加载会影响高度问题 setTimeout(() => { let firstLevels = []; Array.from(document.querySelectorAll(‘h1‘), element => { firstLevels.push(element.offsetTop - 60) }) this.docsFirstLevels = firstLevels; if (times < 8) { this.getDocsFirstLevels(times + 1); } }, 500); }, docsScroll() { if (this.titleClickScroll) { return; } let scrollTop = this.$refs.helpDocs.scrollTop let currentIdx = null; let nowActive = this.docsFirstLevels.some((currentValue, index) => { if(currentValue >= scrollTop) { currentIdx = index return true } }) currentIdx = currentIdx - 1 if (nowActive && currentIdx === -1) { currentIdx = 0 } else if (!nowActive && currentIdx === -1) { currentIdx = this.docsFirstLevels.length - 1 } this.currentClick(currentIdx) }, pageJump(id) { this.titleClickScroll = true; this.$refs.helpDocs.scrollTop = this.$el.querySelector(`#data-${id}`).offsetTop - 40; setTimeout(() => this.titleClickScroll = false, 100); }, currentClick(index) { this.activeIndex = index }, getTitle(content) { let nav = []; let tempArr = []; content.replace(/(#+)[^#][^ ]*?(?: )/g, function(match, m1, m2) { let title = match.replace(‘ ‘, ‘‘); let level = m1.length; tempArr.push({ title: title.replace(/^#+/, ‘‘).replace(/([^)]*?)/, ‘‘), level: level, children: [], }); }); // 只处理一级二级标题,以及添加与id对应的index值 nav = tempArr.filter(item => item.level <= 2); let index = 0; return nav = nav.map(item => { item.index = index++; return item; }); }, // 将一级二级标题数据处理成树结构 handleNavTree() { let navs = this.getTitle(this.content) let navLevel = [1, 2]; let retNavs = []; let toAppendNavList; navLevel.forEach(level => { // 遍历一级二级标题,将同一级的标题组成新数组 toAppendNavList = this.find(navs, { level: level }); if (retNavs.length === 0) { // 处理一级标题 retNavs = retNavs.concat(toAppendNavList); } else { // 处理二级标题,并将二级标题添加到对应的父级标题的children中 toAppendNavList.forEach(item => { item = Object.assign(item); let parentNavIndex = this.getParentIndex(navs, item.index); return this.appendToParentNav(retNavs, parentNavIndex, item); }); } }); return retNavs; }, find(arr, condition) { return arr.filter(item => { for (let key in condition) { if (condition.hasOwnProperty(key) && condition[key] !== item[key]) { return false; } } return true; }); }, getParentIndex(nav, endIndex) { for (var i = endIndex - 1; i >= 0; i--) { if (nav[endIndex].level > nav[i].level) { return nav[i].index; } } }, appendToParentNav(nav, parentIndex, newNav) { let index = this.findIndex(nav, { index: parentIndex }); nav[index].children = nav[index].children.concat(newNav); }, findIndex(arr, condition) { let ret = -1; arr.forEach((item, index) => { for (var key in condition) { if (condition.hasOwnProperty(key) && condition[key] !== item[key]) { return false; } } ret = index; }); return ret; }, }, computed: { content() { return this.mdContent }, compiledMarkdown: function() { let index = 0; rendererMD.heading = function(text, level) { if (level <= 2) { return `<h${level} id="data-${index++}">${text}</h${level}>`; } else { return `<h${level}>${text}</h${level}>`; } }; rendererMD.code = function(code, language) { code = code.replace(/ /g,"<br>") code = code.replace(/ /g,"<br>"); return `<div class="text">${code}</div>`; }; return marked(this.content); } } } </script>
参考链接:
https://github.com/markedjs/marked
https://www.jianshu.com/p/d182ea991609
https://hk.saowen.com/a/bf975e4296e33a14e2d0ad50aa7cbf24fbfb4a9fb851de171b4c71da54eb95e5
以上是关于vue使用marked.js实现markdown转html并提取标题生成目录的主要内容,如果未能解决你的问题,请参考以下文章