【前端】React使用react-markdown+antd实现引入渲染markdown文件

news2024/11/18 19:38:13

项目中遇见一个需求,要求直接在浏览器打开markdown文件进行预览,初次使用遇见一些坎坷,以下记录实现过程,将其封装成了一个组件。

1.下载依赖

yarn add react-markdown

//其余样式插件:
yarn add remark-gfm   
yarn add rehype-raw   
yarn add react-syntax-highlighter   
yarn add github-markdown-css

react-markdown是github上的一款开源的适用于markdown文件的组件。

2.引入使用

2.1 获取markdown文件内容

此处使用fetch来获取文件内容,我在项目中直接引入无法被识别,当然大家也可以试试直接引入的方式。

import React from 'react';
import ReactMarkdown from 'react-markdown';//引入
//import md from './README.md';

const App=({url})=>{

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

  useEffect(() => {
    //url是markdown文件的路径,我在项目中是放到了media文件夹下,示例:url为'/media/xx.md'
      fetch(url)
             .then(res => res.text())
             .then(text => setMdContent(text));
      }
    }, []);

    return(
        <ReactMarkdown
         className='markdown-body'
         children={mdContent}
         />
    )
}

export default App

实现效果(以我的vue学习笔记做个示例...)

3.样式美化

此时已经可以实现markdown文件的读取了,但是样式单一,表格、标题、代码块等各种样式都没有展示出来,并且也没有像Typora等编辑器那样的导航栏。导航栏部分放到文章最后进行讲述,接下来进行内容的美化。

仍然是通过插件来实现(使用的是标题1.下载依赖中的 “其余样式插件” )

import React from 'react';
import ReactMarkdown from 'react-markdown';//引入
//import md from './README.md';
import remarkGfm from 'remark-gfm';// 划线、表、任务列表和直接url等的语法扩展
import rehypeRaw from 'rehype-raw'// 解析标签,支持html语法
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' // 代码高亮
//高亮的主题,还有很多别的主题,可以自行选择
import { tomorrow } from 'react-syntax-highlighter/dist/esm/styles/prism'


const App=({url})=>{

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

  useEffect(() => {
    //url是markdown文件的路径,我在项目中是放到了media文件夹下,示例:url为'/media/xx.md'
      fetch(url)
             .then(res => res.text())
             .then(text => setMdContent(text));
      }
    }, []);

    return(
        <ReactMarkdown
         className='markdown-body'
         children={mdContent}
         remarkPlugins={[remarkGfm, { singleTilde: false }]}
         rehypePlugins={[rehypeRaw]}
         components={{
             code({ node, inline, className, children, ...props }) {
                 const match = /language-(\w+)/.exec(className || '')
                 return !inline && match ? (
                     <SyntaxHighlighter
                         children={String(children).replace(/\n$/, '')}
                         style={tomorrow}
                         language={match[1]}
                         PreTag="div"
                         {...props}
                     />
                 ) : (
                     <code className={className} {...props}>
                         {children}
                     </code>
                 )
             }
         }}
         />
    )
}

export default App

标签倒是被解析出来了。表格部分也整齐了许多,但是代码块的背景和表格线条等样式统统没有。

查阅之后发现是没有引入内容样式,github-markdown-css,这个可以直接使用yarn或npm进行安装,引入一下:

import 'github-markdown-css';

 现在就可以得到一份完美的markdown格式内容了。

4.导航栏目录实现

关于目录的实现,也有对应的插件可以直接进行生成,markdown-navbar,很多文章都说非常好用,懒人插件,但是这个插件对markdown文件编写格式有要求,格式不规范的话识别会出错,导致将文章内容或者备注也识别为标题。这里也简单介绍一下使用。

首先安装

yarn add markdown-navbar

使用。目录想要显示在导航栏左侧或固定在某处等等,都可以自行调整样式。

import MarkNav from 'markdown-navbar';

const App=({url})=>{

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

  useEffect(() => {
    //url是markdown文件的路径,我在项目中是放到了media文件夹下,示例:url为'/media/xx.md'
      fetch(url)
             .then(res => res.text())
             .then(text => setMdContent(text));
      }
    }, []);

    return(
        <div className='nav-menu'>
            <MarkNav
                className="article-menu"
                source={mdContent}
                headingTopOffset={80}
                ordered={false}   //是否显示标题题号1,2等
                /> 
        </div>
        <div className='content-box'>
          //markdown内容
        </div>
    )
}

export default App

 接下来是手动实现markdown目录导航栏的方式。

本文是使用antd的组件Anchor进行了目录的实现。

首先遍历了markdown文件中所有的内容,取出所有h1-h6标签以及标签中的文本进行目录的展示,在每个标题标签中加入一个a标签作为锚点,方便点击标题跳转时进行定位。方法如下。

 const [titles, setTitles] = useState([])
  
const addAnchor = () => {
        const ele = document.getElementsByClassName('markdown-body')[0];
        let eid = 0;
        let titles = [];
        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') {
                let a = document.createElement('a');
                a.setAttribute('id', '#' + eid);
                a.setAttribute('class', 'anchor-title');
                a.setAttribute('href', '#' + eid);
                a.innerText = ' '
                let title = {
                    type: e.nodeName,
                    id: eid,
                    name: e.innerText
                };
                titles.push(title);
                e.appendChild(a);
                eid++;
            }
        }
        return titles;
    }

 使用antd的Anchor组件,Link的href与上述加入的a标签中的href相同,title就是标题标签中取出的文本内容,为每个Link增加一个类名,如此处h1标签类名为title-H1,h2标签类名为title-H2...方便区分修改样式。

 <Anchor
     className='markdown-nav'
     affix={false}
     onClick={handleClickFun}
     getContainer={() => document.getElementsByClassName('content-box')}
 >
     {
         titles.map(t => (
             <Link href={'#' + t.id} title={t.name} className={'title-' + t.type} key={t.id} />
         ))
     }
 </Anchor>

为Anchor绑定点击事件,点击标题的时候,让内容也对应定位到标题所在的地方。

测试发现,antd的Anchor组件在点击锚点后会修改URL,将当前点击的herf拼接到路由上,而单页应用中如果使用哈希模式的路由,当URL被修改后,刷新页面会导致当前路由没有定义而出现404的情况。

查阅antd官方文档。 发现该组件点击事件的回调函数第一个参数就是事件,那么就通过e.preventDefault()阻止掉默认事件,然后再通过第二个参数拿到点击的href,通过document.getElementById获取到这个元素,然后使用scrollIntoView添加页面滚动效果。

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

以下是完整实现效果,标题导航栏的缩进自行通过样式调整即可,本文是让二级标题缩进1em,三级标题缩进2em,依次类推...

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

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

相关文章

vue+springboot使用文件流实现文件下载

前言 本次将使用vue和springboot通过文件流的方式教大家怎么去实现文件的下载&#xff0c;那么话不多说&#xff0c;我们直接开始 相关vue导出文件 以下链接为我其他vue进行下载导出文件文章 vue实现把字符串导出为txt 步骤 文件路径 要进行下载的话&#xff0c;我们肯定…

WEB核心【案例:文件下载,案例:点击切换验证码,几种获取properties资源方式】第十二章

目录 1.文件下载 1.1超链接下载&#xff1a; 1.2自定义servlet下载 1.3小结 2.点击切换验证码 2.1前置只是-验证码生成 2.2分析及代码实现 2.3需求2&#xff1a;点击切换验证码-绕过缓存 3.几种获取preperties资源方式 &#x1f49f;作者简介&#xff1a;大家好呀&…

CTF中Web题目的各种基础的思路-----入门篇十分的详细

我期间也考察很多人的&#xff0c;但搞这个的确实有点少&#xff0c;希望这篇可以大家一点帮助&#xff0c;这篇文章也借鉴一些人的文章&#xff0c;还有很多东西&#xff0c;我没搞&#xff0c;确实有点麻烦&#xff0c;但以后还会不断更新的&#xff0c;希望大家在web这里少走…

Web系统常见安全漏洞介绍及解决方案-CSRF攻击

&#x1f433;博客主页&#xff1a;拒绝冗余 – 生命不息&#xff0c;折腾不止 &#x1f310;订阅专栏&#xff1a;『Web安全』 &#x1f4f0;如觉得博主文章写的不错或对你有所帮助的话&#xff0c;还望大家多多支持呀&#xff01; &#x1f449;关注✨、点赞&#x1f44d;、收…

CSS基本布局——grid布局

grid布局简介&#xff1a; Grid布局是将容器划分成“行”和“列”&#xff0c;产生单元格&#xff0c;然后指定“项目所在”的单元格&#xff0c;可以看作是二维布局。 基本概念&#xff1a; 容器&#xff08;container&#xff09;——有容器属性项目&#xff08;items&…

vite项目优化

首先在讲述vite优化之前&#xff0c;我们先来分析一下和传统的项目管理构建工具的区别&#xff0c;以webpack为例&#xff0c;它是利用plugin插件和loader加载器对项目的所有模块和依赖统一通过入口文件进行编译&#xff0c;从而变成我们html所需要的js格式渲染我们的页面。 随…

Vue项目目录结构介绍(三)

前言 本章我们会对一个 Vue 项目的目录结构进行讲解&#xff0c;解释各子目录以及文件的作用&#xff0c;前端的模块化&#xff0c;Vue 单文件组件规范等。 1、基础目录和文件介绍 在上一章&#xff0c;我们通过 vue-cli 创建了一个新的项目&#xff0c;生成的项目目录里已经包…

tauri+vite+vue3开发环境下创建、启动运行和打包发布

目录 1.创建项目 2.安装依赖 3.启动项目 4.打包生成windows安装包 5.安装打包生成的安装包 1.创建项目 运行下面命令创建一个tauri项目 pnpm create tauri-app 我创建该项目时的node版本为16.15.0 兼容性注意 Vite 需要 Node.js 版本 14.18&#xff0c;16。然而&#x…

【玩转CSS】这些高级技巧,你都会吗

&#x1f525;一个人走得远了&#xff0c;就会忘记自己为了什么而出发&#xff0c;希望你可以不忘初心&#xff0c;不要随波逐流&#xff0c;一直走下去&#x1f3b6; &#x1f98b; 欢迎关注&#x1f5b1;点赞&#x1f44d;收藏&#x1f31f;留言&#x1f43e; &#x1f984; …

js去掉两个数组相同的元素、js删除数组中某一个对象、js快速查找数组中重复项下标

一、js去掉两个数组相同的元素 注意&#xff1a;这里并非是数组去重&#xff0c;数组去重是去掉一个数组中相同的元素&#xff0c;这里是比较两个数组&#xff0c;过滤掉二者相同的&#xff0c;留下不同的。 通过 some() 在对方数组里面查找相同元素&#xff0c;再利用filter…

老老实实的程序员该如何描述自己的缺点

答辩的时候&#xff0c;晋升的时候&#xff0c;面试的时候&#xff0c;你有没有经常遇到一个问题&#xff0c;那就是你觉得自己有什么缺点吗&#xff1f; 目录 1. 每个人都有缺点 2. 这道题在考什么&#xff1f; 3. 我之前是怎么回答的 4. 你可以这样回答试一试 5. 总结 …

浅析<router-view> v-slot事例

官方关于<router-link> 的 v-slot的相关介绍: https://router.vuejs.org/zh/api/#router-view-%E7%9A%84-v-slot 并给出了一个例子&#xff1a; <router-view v-slot"{ Component, route }"><transition :name"route.meta.transition || fade&q…

selenium驱动Firefox安装和环境配置

目录 一、前言 二、版本 三、配置环境 四、在pycharm中添加selenium 五、测试代码&#xff0c;成功打开百度&#xff0c;则配置成功 一、前言 根据多篇文章总结了一下自己操作过程&#xff0c;主要是想记录一下。 二、版本 1.查看自己的Firefox的版本&#xff0c;在浏览器…

Web视频video自动播放(移动端及PC端)

做了个关于视频播放的活动&#xff0c;被各种问题折腾得精疲力竭。为了日后能够轻松点&#xff0c;特记录下出现的各种问题及解决方法。 活动要适配移动端&#xff08;IPhone、Android&#xff09;和PC端&#xff08;Chrome&#xff09; 需要解决的问题&#xff1a;移动端禁止全…

vue echarts饼图环形 (随着legend动态显示数据总数)

目录 1.安装echarts 2.引入echarts 3.创建要放入echarts实例的一个盒子 4.创建echarts实例 5.随着legend动态显示数据总数 效果视频 1.安装echarts npm install echarts --save 2.引入echarts 在 当前vue文件中引入 echarts 如下图所示&#xff1a; 3.创建要放入echa…

Vue项目实战——【基于 Vue3.x + Vant UI】实现一个多功能记账本(搭建开发环境)

基于 Vue3.x Vant UI 的多功能记账本&#xff08;二&#xff09; 文章目录基于 Vue3.x Vant UI 的多功能记账本&#xff08;二&#xff09;搭建开发环境项目演示1、创建项目2、配置路由3、添加 Vant UI 组件库4、移动端 rem 配置5、添加 iconfont 文字图标库6、二次封装 Axio…

Vue实现鼠标悬浮隐藏与显示图片效果 @mouseenter 和 @mouseleave事件

前言 前端vue 有个功能是鼠标移动到指定item上显示出来一个编辑和删除的图标 鼠标悬停在列表那么需要有悬浮显示的列表编辑和删除icon 文字不好描述&#xff0c;因为是web端录屏也比较麻烦 这里用截图说明 图片说明 功能实现 之前没做过这种效果&#xff0c;问了一下我的组长…

echarts 地图和柱状图结合(在地图上显示柱状图)

如图&#xff0c;需求要做一个在地图上显示柱状图的echarts图&#xff0c;但是百度了半天&#xff0c;发现很少有人发这种例子。这个代码也是借鉴的别人的文章&#xff0c;但需求肯定不完全一致&#xff0c;那我会根据我的需求把代码和注意事项发出来并解释。&#xff08;如果有…

ant-design-vue:基础使用

一、环境介绍 vue3tsant-design-vue 二、安装使用 2.1、安装脚手架工具 $ npm install -g vue/cli # OR $ yarn global add vue/cli 2.2、创建一个demo项目 2.2.1、创建项目 $ vue create antd-demo 2.2.2、 安装成功&#xff1a; 2.2.3、 项目目录及启动项目&#xff1a…

前端常见的几种布局方式,2分钟即可看完,全是干货。

前端常见的几种布局方式 提示&#xff1a;本篇文章不包含具体知识点&#xff0c;但是可以帮助小白了解到什么是布局 文章目录前端常见的几种布局方式前言参考文档一、前端常见的几种布局方式是什么&#xff1f;二、几大布局介绍1.浮动布局2.定位布局3.弹性布局4.栅格布局5.响应…