在antd里面渲染MarkDown并且自定义一个锚点目录TOC(重点解决导航目录不跟随文档滚动的问题)

news2024/10/5 17:23:01

一、整体思路

由于有很多很长的文档需要渲染,我觉得用MarkDown的方式会比较适合管理,所以这两天测试了一下在antd里面集成MarkDown的渲染模块。
总体思路参考:
https://blog.csdn.net/Sakuraaaa_/article/details/128400497
感恩大佬的倾情付出。

    1. 使用react-markdown解析MarkDown
    1. 使用antd的Anchor锚点组件做一个类TOC的跳转目录

二、不说废话

1.完整代码

import React, {useState, useEffect, useRef} from 'react';
import Markdown from 'react-markdown';
import {getMarkDown} from "@/services/ant-design-pro/api";
import 'github-markdown-css';
import {Collapse, Anchor, BackTop, Col, Row} from "antd";
const { Link } = Anchor;
const { Panel } = Collapse;
import styles from './style.less';

const App = ({markdownFileName}) => {

  const [mdContent, setMdContent] = useState('')
  const [at, setAt] = useState('')

  useEffect(() => {
    let ret = getMarkDown(markdownFileName)
    ret.then((res)=>{
      setMdContent(res)
      addAnchor()
    })
  }, []);


  const addAnchor = () => {
    const ele = document.getElementsByClassName('markdown-body')[0];
    let eid = 0;
    let ttitles = [];
    for (const e of ele.childNodes) {
      // if (e.nodeName === 'H1' || e.nodeName === 'H2' || e.nodeName === 'H3' || e.nodeName === 'H4' || e.nodeName === 'H5' || e.nodeName === 'H6') {
      if (e.nodeName === 'H2') {
        let a = document.createElement('a');
        a.setAttribute('id',  'h2_' + eid);
        a.setAttribute('class', 'anchor-title');
        a.setAttribute('href', '#' + eid);
        a.innerText = ' '
        let title = {
          type: e.nodeName,
          id: eid,
          name: e.innerText
        };
        ttitles.push(title);
        e.appendChild(a);
        eid++;
      }
    }
    setAt((
      <Anchor
        onClick={handleClickFun}
        getContainer={()=>ele}
      >
        {
          ttitles.map(t => (
            <Link href={'#h2_' + t.id} title={t.name}/>
          ))
        }
      </Anchor>
    ))

  }


  const handleClickFun = (e, link) => {
    console.log('click')
    e.preventDefault();
    if (link.href) {
      // 找到锚点对应得的节点
      let element = document.getElementById(link.href);
      // 如果对应id的锚点存在,就跳滚动到锚点顶部
      element && element.scrollIntoView({ block: 'start', behavior: 'auto' });
    }
  }


  return (
      <div className="scrollable-container">
        <div
            className={styles.markdownnav}
        >
          <Collapse defaultActiveKey={['1']} ghost>
            <Panel header="章节目录" key="1">
            {at}
          </Panel>
        </Collapse>
        </div>

          <Markdown
            className='markdown-body'
          >{mdContent}
          </Markdown>
        <BackTop/>
      </div>
  )
}

export default App

效果,由于手头上没有长的md,简单演示下。
在这里插入图片描述
代码很简单,主要和大家说说我遇到的几个问题。

二、难点?或者说要点

1.MarkDown自定义样式

import 'github-markdown-css';

引入这个风格后确实文档的样式有所美观,
但我还需要根据自己的要求修改几个风格,在引入的less中用global参数覆盖上面的样式就可以。原理可以看看我自定义dot那篇。

:global {
  .markdown-body h1 {
    //margin: .67em 0;
    font-weight: var(--base-text-weight-semibold, 600);
    padding-bottom: .3em;
    font-size: 2em;
    border-bottom: 1px solid var(--color-border-muted);
    margin: 0 auto;
    text-align: center;
  }

  .markdown-body p{
    text-indent: 2em; /*首行缩进*/
  }
}

2.导航目录生成

虽然我接触react时间也不短了,但是确实也没有精细的研究过react的渲染过程中几个回调的顺序问题。尤其是函数组件的资料更难找。
你看我这里的jsx里面,并没有直接把Anchor组件写在这里,而是写在了MarkDown渲染完成后的回调中。

setAt((
      <Anchor
        onClick={handleClickFun}
        getContainer={()=>ele}
      >
        {
          ttitles.map(t => (
            <Link href={'#h2_' + t.id} title={t.name}/>
          ))
        }
      </Anchor>
    ))

原因就是我这里需要设置getContainer的值,默认是获取window,那window铁定是不滚动的,还我们需要把MarkDown的elment给他挂上去。如果直接写需要用ref的方式获取,但是MarkDown组件没办法获取他的ref,我不知道怎么解决这个问题,所以讨巧的再useEffect里面来做目录组件的插入,因为这里Dom的渲染已经完成,我们可以通过document.getElementsByClassName来获取MarkDown的element。

3.导航目录随文档滚动问题

根据上面的S大佬的代码写,无论我怎么调整getContainer的容器都无法跟随文档滚动。
所实话这个也不是我第一次吐槽antd了,这个文档真的是简陋,不够这次我痛定思痛。
根据https://juejin.cn/post/7158430758070140942这位大佬的思路,测地落实了一下用webstrom的断点来调试源码的方法。终于搞清楚为啥没办法跟随文档滚动。

我们需要先明白一个点:Anchor的代码里面是怎么实现滚动监听的呢?
实现还挺粗暴的,对你传入的getContainer的容器进行滚动监听,当出现滚动事件的时候,检查容器中的所有跳转链接和顶部的距离,来设置激活的目录。

问题出在S大佬的代码中插入a链接的id上。
在这里插入图片描述
可以看到S大佬这里给id的前面加了个#,这个#在点击跳转的时候不会有问题,但是在滚动的时候就会出现问题。原因是这个地方进行了一个正则的过滤
在这里插入图片描述
这个正则主要的工作是过滤掉前面的#号
在这里插入图片描述
然而根据S大佬的id配置的是#开头的。所以就导致这里的id没办法找到对应的a标签,所以就没办法监听到他的滚动位置。
解决方案也很简单。

  • 1.在设置跳转标签,也就是插入a标签的地方。a标签的id不能带前面的#
  • 2.在Anchor的Link中需要带#

这样设置完就可以看到目录跟随文章滚动的效果了。

上面我说了我要吐槽antd的文档,因为这么重要的设置他甚至都没有给一个示例代码,一切都需要靠自己分析自己猜,很好奇这个东西他们自己到底用不用?还是和语雀一样外部一套内部一套,外部这个就做着玩的吧。

PS

1.webstrom 用Chrome 调试时遇到链接不上chrome的问题

报错信息:

Please ensure that the browser was started successfully with remote debugging port opened.

原因是:由于偷懒我还在使用2021.3版本的webstrom,所以无法链接上新版本的chrome。更新webstorm之后解决。

具体配置方法可以查看:
https://juejin.cn/post/7126550003585122334

2.如何在antd里面优雅的获取element

// 取值
const child = useRef()
<div className="scrollable-container" ref={child}>
</div>

// 调用
child.current

然而,对于react-markdown的MarkDown并不行,会报错:
在这里插入图片描述
不过我没什么代码美学,我就直接用document.getElementsByClassName了,希望有研究的大佬可以教我一下。

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

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

相关文章

技术分享 | 某下一代防火墙远程命令执行漏洞分析及防护绕过

0x01 概述 最近&#xff0c;某下一代防火墙曝光了远程代码执行漏洞。此漏洞通过绕过身份认证和注入 cookie 的方式来执行系统命令&#xff0c;公开的利用方式受到诸多限制且命令执行无回显&#xff0c;并且当目标机器不出网时&#xff0c;该漏洞利用方式便无法发挥作用&#x…

C语言——有 15 个数按由大到小顺序存放在一个数组中,输入一个数,要求用折半查找法找出该数是数组中第几个元素的值。如果该数不在数组中,则输出“无此数”

完整代码&#xff1a; /* 有 15 个数按由大到小顺序存放在一个数组中&#xff0c;输入一个数&#xff0c;要求用折半查找法找出 该数是数组中第几个元素的值。如果该数不在数组中&#xff0c;则输出“无此数”。*/ #include<stdio.h>//折半查找法&#xff0c;n是查找的那…

超宽带技术在汽车领域的应用

随着科技的不断发展&#xff0c;超宽带&#xff08;Ultra-Wideband, UWB&#xff09;技术在各个领域展现出了强大的潜力&#xff0c;其中汽车领域更是受益匪浅。UWB技术以其高精度的定位能力、高速的数据传输和低功耗的特点&#xff0c;为汽车行业带来了许多创新。本文将探讨UW…

在Python的虚拟环境中卸载eric6的方法

问题描述 之前在电脑的Python虚拟环境中安装了PyQt5及相应的界面设计器eric6。当时安装eric6后&#xff0c;没成功运行&#xff0c;提示少一个什么系统文件。我已在旁边的台式机上安装了较新版的PyQt6&#xff0c;决定不再用老版本的eric6&#xff0c;于是我需在笔记本电脑上卸…

C#WPF嵌入字体实例

本文介绍C#WPF嵌入字体实例。 首先创建项目 添加Resources文件夹,添加字体文件,字体文件属性:生成操作为Resources,复制到输出目录:不复制 字体的使用可以采用以下两种方法: 方式一 直接引用 FontFamily="./Resources/#幼圆" 方式二 定义资源 <Applica…

微信小程序:js处理一段文字,根据句号或者分号进行换行

一、根据句号和分号进行换行 效果 代码 wxml <view><text>{{remark}}</text> </view> js 核心代码 var repalce_remark remark.replace(/[&#xff1b;。]/g, "$&\n"); // 使用正则表达式进行替换 remark 是待替换的字符串。/[&am…

APP移动出海必备神器,MobPush提供海外消息智能推送一站式解决方案

随着国内移动应用市场的趋于饱和&#xff0c;海外新兴市场成为越来越多移动应用开发者的进一步提升APP市场占有率的不二之选。据统计&#xff0c;中国应用开发者中有79.1%计划出海。但如何利用消息推送实现与用户的深度绑定仍然存在较为一定问题。在国外&#xff0c;应用开发者…

深度学习:激活函数曲线总结

深度学习&#xff1a;激活函数曲线总结 在深度学习中有很多时候需要利用激活函数进行非线性处理&#xff0c;在搭建网路的时候也是非常重要的&#xff0c;为了更好的理解不同的激活函数的区别和差异&#xff0c;在这里做一个简单的总结&#xff0c;在pytorch中常用的激活函数的…

如何使用爬虫做一个网站

​ 大家如果有兴趣做网站&#xff0c;在买了VPS&#xff0c;部署了wordpress&#xff0c;配置LNMP环境&#xff0c;折腾一番却发现内容提供是一个大问题&#xff0c;往往会在建站的大&#xff08;da&#xff09;道&#xff08;keng&#xff09;上泄气 &#xff0c;别怕&#xf…

找免费商用字体,就上这5个网站,再也不怕侵权了。

很多新手设计师没有版权意识&#xff0c;网上找了字体直接使用结果被发律师函&#xff0c;造成巨大损失&#xff0c;建议大家使用前先去查询一下这款字体是否能商用&#xff0c;另外就是去找一些可以免费商用的字体使用。如何才能找到免费商用字体呢&#xff1f; 下面我就把我…

迅镭激光万瓦切割设备中标全球轨交装备龙头中国中车

轨道交通装备被称之为国之重器&#xff0c;历经60多年的发展&#xff0c;我国轨交装备制造业已成为自主创新程度最高、国际创新竞争力最强、产业带动效应最明显的行业之一。 而承担我国现代化轨道交通装备研发制造的中国中车股份有限公司(以下称“中国中车”)&#xff0c;也在用…

微前端qiankun接入Vue和React项目

主应用&#xff1a;Vue3Webpack 1、创建主应用&#xff1a; npx vue create main-vue3-app 2、安装qiankun npx yarn add qiankun 3、项目中使用的vue、vue-router、qiankun依赖如下&#xff0c;webpack版本为5.x 4、在根目录下创建vue.config.js const { defineConfig }…

一张逻辑图讲清楚OS在做什么:浅谈OS

本文旨在通过思维导图的方式、对os主要的知识点简短介绍&#xff0c;让读者能短时间快速把os是什么、干什么给串起来。当别人问起来时&#xff0c;可以用3-5分钟讲清楚。如果读者对os有更加深入的兴趣&#xff0c;可点对点针对某一内容再做进一步研究。通常对于互联网从业者来说…

TSINGSEE青犀老旧小区升级改造AI+视频监控方案

一、背景与需求 近年来&#xff0c;政府高度重视城镇老旧小区改造工作&#xff0c;强调要加快老旧小区改造&#xff0c;不断完善城市管理和服务&#xff0c;彻底改变粗放型管理方式&#xff0c;让人民群众在城市生活得更方便、更舒心、更美好。老旧小区升级改造面临以下问题&a…

绿米Aqara S1【妙控开关 S1E】的破解方法

概述 为了更好地向友商学习,我们采购了绿米的一块妙控开关 S1E的屏幕,用来研究。 首先上图看一下他的设计及推广的说明: 产品详情,参见Aqara的官方网站: 具体链接:妙控开关 S1E|Aqara 全屋智能 硬件概要 这里我就直接上硬件的说明了。 官方的设备参数如下: 我来写…

SRS srs-bench

1 srs-bench 音视频压测工具&#xff0c;包括RTMP/FLV/WebRTC/GB28181等&#xff0c;未来还会完善。 SB(SRS Bench) is a set of benchmark and regression test tools, for SRS and other media servers, supports HTTP-FLV, RTMP, HLS, WebRTC and GB28181. For RTMP/HLS/F…

7.MySQL复合查询

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 目录 复合查询 基本查询回顾 多表查询 自连接 子查询 单行子查询 多行子查询 多列子查询 在from子句中使用子查询 合并查询 union union all 实战OJ 复合查询 前面我们讲解的mysql表的查询都是对一张表进行查询…

图像恢复介绍(持续更新)

前言 噪声的产生是信号在采集、传输以及记录过程中&#xff0c;受到成像设备自身因素和外界环境的影响而产生的。现实中的噪声是随机分布的&#xff0c;事实上&#xff0c;噪声无法完全去除&#xff0c;只能使得重现信号尽可能的接近原始信号&#xff0c;因此&#xff0c;去噪严…

JavaSpringbootmysql农产品销售管理系统47627-计算机毕业设计项目选题推荐(附源码)

摘 要 随着互联网趋势的到来&#xff0c;各行各业都在考虑利用互联网将自己推广出去&#xff0c;最好方式就是建立自己的互联网系统&#xff0c;并对其进行维护和管理。在现实运用中&#xff0c;应用软件的工作规则和开发步骤&#xff0c;采用Java技术建设农产品销售管理系统。…

Matplotlib | 高阶绘图案例【4】- 2023年编程语言榜单Python稳坐第一

文章目录 &#x1f3f3;️‍&#x1f308; 1. 导入模块&#x1f3f3;️‍&#x1f308; 2. 数据处理2.1 高效数据2.2 保留需要的列 &#x1f3f3;️‍&#x1f308; 3. 绘图3.1 绘制图布&#xff0c;添加3个子图3.2 绘制子图1条形图3.3 子图1条形图添加数据标签3.4 绘制子图2条…