Markdown文档-展示与美化方法
本着简单开发使用
采用插件:vue-markdown-loader
npm安装
使用 npm 安装 vue-markdown-loader 插件:
cnpm install vue-markdown-loader
我本次安装的版本:"vue-markdown-loader": "^2.5.0",
插件效果:生成组件式的代码,引用后直接使用
配置webpack
需要配置 webpack 来使用 vue-markdown-loader 插件。在 vue.config.js
文件中添加以下配置:
module.exports = {
chainWebpack: config => {
config.module
.rule('md')
.test(/\.md/)
.use('vue-loader')
.loader('vue-loader')
.end()
.use('vue-markdown-loader')
.loader('vue-markdown-loader/lib/markdown-compiler')
.options({
raw: true
});
}
}
优化html结构
一般情况下,我们可以在选项中进行配置,但是由于我对此不够了解,所以没有在官方文档中找到相关方法。因此,我打算获取 DOM 并修改其 innerHTML。
添加结构
准备结构
首先,准备以下结构:
<div class="doc-container">
<div class="markdown-body">
<component :is="componentsName" ref="componentRef" />
</div>
</div>
请注意:
- 必须添加
markdown-body
这个 class。 - 使用
<component is="Xxx" />
来渲染组件。这是因为vue-markdown-loader
会将markdown
文件转换为Vue
文件,直接渲染markdown
文件就好了!此外,还可以在一个公共页面(例如 index)中获取 DOM,统一处理innerHTML
。
导入文件
导入需要展示的 markdown 文件,例如:
import HeaderMd from '@/header/README.md';
import ButtonMd from '@/button/README.md';
......
markdown文件内容
随便整点花生米(markdown)文件内容,例如:
- # 按钮
- ## markDown的使用
- ### markDown的使用
- 你使用的是makeDown的文件
- ```js
- let a = 0
- ```
- ### markDown的使用
- 你使用的是makeDown的文件
- ```js
- let a = 0
- ```
得到的html渲染结构
页面展示效果
看的出来,这种怎么写都不会好看起来,如果h3
与pre
标签包裹在一起变成卡片式的样式,那么看着要舒服很多。
接下来就开始进行优化结构。
优化结构
开始优化结构
1、先获取DOM
2、每一个markdown
页面的DOM,利用new Map()
根据key='pathName'
,存起来好一些。
如果路由下次进来,取出Map
中的数据,直接插入到别的标签中,而不是再次的进入,重新获取DOM,重新优化标签,然而缺点就是,在文件中更新了markdown的信息,不能及时的同步到页面中,需要刷新一下,暂时没有想到其他好方法。
----------data----------------
componentRefMap: new Map(),
---------methods--------------
getComponentRef(pathName) {
if (pathName !== '') {
this.$nextTick(() => {
this.componentRefMap.set(pathName, this.$refs.componentRef.$el);
// 下面要说的优化标签
this.optimizationTags(this.componentRefMap);
// 下面要说的添加操作
this.eventListenerTages();
});
}
},
优化标签
得益于结构的简单化,参考vant的vite.config配置,使用方法如下:
optimizationTags(dataMap, hasValue = false) {
let refContent = dataMap.get(this.pathName);
let group = refContent.innerHTML
.replace(/<h1/g, ':::<h1')
.replace(/<h2/g, ':::<h2')
.replace(/<h3/g, ':::<h3')
.split(':::');
let groupHtml = group
.map((fragment) => {
if (fragment.indexOf('<h3') !== -1) {
h3Setioncount++;
let fragmentHtml = `<div class="doc-card">${fragment}</div>`;
return fragmentHtml;
}
return fragment;
})
.join('');
dataMap.get(this.pathName).innerHTML = groupHtml;
},
解释一下:
将 h1、h2 和 h3 标签分隔开,并存储在数组 group
中。然后,对数组进行处理,如果其中的片段包含 <h3
标签,就在外部添加一个包裹 div,并赋予其类名 doc-card
。最后,将处理后的 HTML 内容重新赋值给对应的 DOM 元素。
看的出来 h1
h2
h3
标签都被切开了,没记错的话,我们取一段解释,结构中展示了 h2+h3+pre+h1
,那么切开就是,h2
h3+pre
h1
三个fragment
,我们就可以针对h3
所在的fragment
进行处理。
展示效果
这样我们就可以进行样式的书写了,我们也可以自定义更多自己喜欢的东西。
添加操作
类似地,我们可以在标签中添加操作,并进行相应的处理。例如,为每个 <pre>
标签添加一个操作按钮。
let group = refContent.innerHTML
.replace(/<h1/g, ':::<h1')
......
.replace(/<pre>/g, '<pre><div class="operate"><div class="copy">复制</div></div>')
.split(':::');
把操作都放到operate类中,再获取DOM,进行操作就简单一点
复制操作
eventListenerTages() {
let nodeOperateArr = document.querySelectorAll(".operate");
nodeOperateArr.forEach((nodeOperate) => {
nodeOperate.onclick = (event) => {
if (event.target.className == "copy") {
// 复制操作
let textInnerText = nodeOperate.nextSibling.innerText;
// 复制操作的具体实现...
......
alert("复制成功");
}
};
});
},
上述代码通过获取所有具有类名 .operate
的 DOM 元素,为其添加点击事件监听器。当点击事件发生时,利用event.target.className
获取到,你点击的子标签的类名。就可以根据需要执行相应的操作,例如复制文本内容。
使用nextSibling
,轻松的拿下copy节点的下一个pre的节点内容
类比一下:操作的方式,也可以进行书写了
添加锚点
同样地,我们只需要添加锚点来实现页面内的平滑滚动。
optimizationTags(){
....
let groupHtml = group
.map((fragment) => {
// 设计锚点的数据
let h2Setioncount = 0;
let h3Setioncount = 0;
......
if (fragment.indexOf('<h2') !== -1) {
h2Setioncount++;
h3Setioncount = 0;
let fragmentHtml = fragment.replace(/<h2/g, `<h2 id=${this.pathName}_${h2Setioncount}-${h3Setioncount}`);
return fragmentHtml;
}
if (fragment.indexOf('<h3') !== -1) {
h3Setioncount++;
let fragmentHtml = `<div class="doc-card" id=${this.pathName}_${h2Setioncount}-${h3Setioncount}>${fragment}</div>`;
return fragmentHtml;
}
return fragment;
})
.join('');
}
生成的结构:
滑动的难处
由于我使用的是hash路由,传统的锚点#maodian
方式在这种情况下不起作用,
例如: http://example.com/hello#top
。
而在我的情况下,URL的格式为http://example.com/#/hello#top
,这种情况下top
无法直接使用作为锚点。
为了解决这个问题,我们采用参数传递的方式,手动进行滚动操作。
通过获取URL中的查询参数section
来指定滚动到的目标位置,然后使用JS的scrollIntoView()
方法进行平滑滚动。
smoothScrollSection(valueSection) {
this.$nextTick(() => {
let value = valueSection || this.$route.query.section;
try {
let NodeSection = document.getElementById(value);
NodeSection.scrollIntoView({ behavior: 'smooth' });
} catch (error) {
console.error("没有这个锚点");
alert("请检查结构的正确性");
}
});
},
上述代码用于实现平滑滚动至指定锚点的功能。根据提供的锚点值(或从路由参数中获取),通过 getElementById
方法获取对应的 DOM 元素,并使用 scrollIntoView
方法进行平滑滚动。
我说如果,你想在页面上点击操作,修改了页面url而页面不更新的效果,可以采用下面的方法
// 只是一个展示其他设置
const searchParams = new URLSearchParams(window.location.search);
// 修改section参数的值
searchParams.set('section', 'xxx');
const newUrl = '#' + this.$route.path + '?' + searchParams.toString();
window.history.replaceState({}, '', newUrl);
在上面的一顿操作下来,url变了,但是没有更新,我们就可以顺利的在下面使用定义好的滑动操作
this.smoothScrollSection('Xxxx_x_x');
这个是真的在动,是根据,你上面修改Url的锚点值,传入即可。
效果
优化样式
需要安装
npm
cnpm install github-markdown-css
cnpm install highlight
导入
刚刚好,我把全部的markdown都经过index.vue文件,直接引入就行
import "github-markdown-css";
import "highlight.js/styles/github.css";
剩下的样式,可以根据喜好,自定义覆盖与修改。
<style lang="scss" scoped>
// 自定义一下
@import url(./markdownstyle.scss);
</style>
最终效果
转载自:https://juejin.cn/post/7231018455242457144