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


    一、整体思路

    由于有很多很长的文档需要渲染,我觉得用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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96

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

    二、难点?或者说要点

    1.MarkDown自定义样式

    import 'github-markdown-css';
    
    • 1

    引入这个风格后确实文档的样式有所美观,
    但我还需要根据自己的要求修改几个风格,在引入的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; /*首行缩进*/
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2.导航目录生成

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

    setAt((
          <Anchor
            onClick={handleClickFun}
            getContainer={()=>ele}
          >
            {
              ttitles.map(t => (
                <Link href={'#h2_' + t.id} title={t.name}/>
              ))
            }
          </Anchor>
        ))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    原因就是我这里需要设置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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

  • 相关阅读:
    刨根问底 Redis, 面试过程真好使
    zookeeper+kafka消息队列群集部署
    Hexagon_V65_Programmers_Reference_Manual(26)
    Spring Data Web支持
    如何优雅部署OpenStack私有云I--Kolla
    透明度和透明贴图制作玻璃水杯
    OpenCV实现图像的礼帽和黑帽
    Go Web——template标准库
    ChatGPT研究论文提示词集合1-【主题选择与问题研究、文献综述】
    兄弟组件通信context
  • 原文地址:https://blog.csdn.net/u013113491/article/details/134053055