效果


需求

因为项目最后是对外开放的,所以在项目中有个文档中心,里面有一些用户手册、开发文档等展示需求。同时支持文章目录点击。

分析

第一时间想到的就是解析md文件了,语法简单,满足大部分写作要求,市面上也很有很多成熟的解析方案。

依赖项

这里是使用了react-markdowngithub-markdown-css这两个库,一个是解析md文件,一个是github的md文档样式。

具体

解析md文件(大概)
import React, { useState, useEffect } from 'react';
import ReactMarkdown from 'react-markdown/with-html';import 'github-markdown-css/github-markdown.css'const articles = {'1': '/developer_guide.md','2': '/user_manual.md'
}
const NormalTest: React.FC<any> = () => {const [currentArticle, setCurrentArticle] = useState<{ url: string, content: any }>({ url: '', content: '' });// 初始为开发文档useEffect(() => {changeCurrentArticle(articles['1'])}, [])// 更改当前文档const changeCurrentArticle = async (url: string) => {const res = await fetch(url);const content = await res.text();setCurrentArticle({ ...currentArticle, content, url })}return (<><ReactMarkdownclassName="markdown-body"source={currentArticle.content}escapeHtml={false}/></>)
};

如果页面正确显示,说明是解析成功

单独解析目录

这里要用到react-markdown组件的renderersAPI,给title加上锚点

HeadBlock.tsx

import React from 'react';const HeadBlock: React.FC<any> = (props) => {const { level, children } = props;const { nodeKey } = children[0].props;return (<>{React.createElement(`h${level}`, { className: 'article-nav', id: nodeKey, 'data-level': level }, children)}</>);
}export default HeadBlock;

index.tsx

import HeadBlock from './HeadBlock';<ReactMarkdownclassName="markdown-body"source={currentArticle.content}escapeHtml={false}renderers={{heading: HeadBlock}}
/>

完整版

import React, { useState, useEffect } from 'react';
import { Row, Col, Menu, Affix, Anchor } from 'antd';
import ReactMarkdown from 'react-markdown/with-html';
import { isEmpty } from "lodash";import HeadBlock from './HeadBlock';import 'github-markdown-css/github-markdown.css'
import './index.less';const { Link } = Anchor;const articles = {'1': '/developer_guide.md','2': '/user_manual.md'
}/*** * @param lists * 这里只做两级处理*/
export const navsToTree = (lists: any[]) => {if (isEmpty(lists)) return [];// 提取第一个level为最大level 后续比他大的一律为同级const maxLevel = lists[0].level;const newLists: any[] = [];lists.forEach((item: any) => {// 一级 同级if (item.level <= maxLevel) {newLists.push(item)} else {// 非同级if (newLists[newLists.length - 1].children) {newLists[newLists.length - 1].children.push(item)} else {newLists[newLists.length - 1].children = [item]}}})return newLists;
}const NormalTest: React.FC<any> = () => {const [currentArticle, setCurrentArticle] = useState<{ url: string, content: any }>({ url: '', content: '' });const [treeNavs, setTreeNavs] = useState<any[]>([])// 初始为开发文档useEffect(() => {// console.log(1);changeCurrentArticle(articles['1'])}, [])// 这里是根据文档修改进行获取目录useEffect(() => {/***  获取所有的文章标题*/// console.log(currentArticle);const markdownNavs = document.querySelectorAll('.article-nav')const navs: any[] = [];markdownNavs.forEach((item: any) => {const level = item.getAttribute('data-level');const value = item.textContent;const nodeKey = item.id;navs.push({ level, value, nodeKey })})transArticleNavs(navs)}, [currentArticle.content])// 更改当前文档const changeCurrentArticle = async (url: string) => {const res = await fetch(url);const content = await res.text();setCurrentArticle({ ...currentArticle, content, url })}// 书籍导航点击const menuOnClick = (e: any) => {const url = articles[e.key]changeCurrentArticle(url)}// 转换为文章右侧目录const transArticleNavs = (navs: any) => {// 转换为二级导航const treedevelopDocs = navsToTree(navs);setTreeNavs(treedevelopDocs)}return (<><Row className='articles'><Col flex='200px' className="articles-list"><Affix offsetTop={24}><Menu defaultSelectedKeys={['1']} onClick={menuOnClick} theme='light'><Menu.Item key="1">开发文档</Menu.Item><Menu.Item key="2">使用文档</Menu.Item></Menu></Affix></Col><Col flex='1' className='articles-content'><div className='articles-content_wrpper'><ReactMarkdownclassName="markdown-body"source={currentArticle.content}escapeHtml={false}renderers={{heading: HeadBlock}}/></div></Col><Col flex='200px' className="articles-menu"><Affix offsetTop={20} ><Anchor style={{ width: 160 }}>{treeNavs.map((item: any) => {if (item.children) {return (<Link href={`#${item.nodeKey}`} title={item.value} key={item.nodeKey}>{item.children.map((childItem: any) => (<Link href={`#${childItem.nodeKey}`} title={childItem.value} key={childItem.nodeKey} />))}</Link>)} else {return (<Link href={`#${item.nodeKey}`} title={item.value} key={item.nodeKey} />)}})}</Anchor></Affix></Col></Row></>);
};export default NormalTest;

react解析md文件相关推荐

  1. md 文件使用html阅读,使用markdow-it渲染md文件为html页面

    前言:最近在写一些新闻资讯详情的页面,header组件.footer组件.目录组件都是固定的,只有新闻的内容是变化的.为了不去写重复的代码(有句话怎么说来着:战略上偷懒,战术上勤奋,就是这个意思),准 ...

  2. 解析Markdown文件生成React组件文档

    前言 最近做的项目使用了微前端框架single-spa. 对于这类微前端框架而言,通常有个utility应用,也就是公共应用,里面是各个子应用之间可以共用的一些公共组件或者方法. 对于一个团队而言,项 ...

  3. 【vite+vue3.0】基于vite写一个将md文件渲染为js文件的插件

    基于vite写一个将md文件渲染为js文件的插件 前言 尤大是这么描述 Vite 的: 「一个基于浏览器原生 ES imports 的开发服务器. 利用浏览器去解析 imports,在服务器端按需编译 ...

  4. 微信小程序PHP文件建在哪里,微信小程序解析H5文件方法

    经常有网友问怎么让微信小程序解析H5文件或者类似封装H5网页到APP里面?我一开始觉得这是不可能的,因为官方的解答是这样的: 每一个小程序页面是由同路径下同名的四个不同后缀文件的组成,如:index. ...

  5. nextjs+MDX渲染md文件并生成目录

    nextjs+MDX渲染md文件并生成目录 一.效果展示 线上网站:点击体验 md文件 网页 二.需要使用的模块包 包名 作用 @mdx-js/react 用来渲染react组件 @next-mdx- ...

  6. 【树形结构】巴基斯坦城市列表 (城市原始数据来自md文件)

    github md文件地址 : cnic/cnic_codes_list.md at main · graysuit/cnic · GitHub import lombok.Builder; impo ...

  7. Markdown *.MD 文件 技术文档 在SDL Trados Studio中翻译

    Markdown *.MD 文件 技术文档 在SDL Trados Studio中翻译 Markdown 是一种最新主流的技术文档写作格式,广泛用于API编写,在技术领域十分流行,本篇文档也是在CSD ...

  8. 提取坚果云思维导图的文字到md文件

    分析 素材文件在文末 观察nbmx文件,猜测是压缩文档,尝试解压,得到content.json文件.观察content.json,部分内容如下: {"data": {"e ...

  9. linux 预览md文件_利用Tornado搭建文档预览系统

    在平时的工作或学习中,我们经常会接触不同格式的文档类型,比如txt,log,Offices文档,编程代码脚本,图片,视频等.本文将会介绍笔者的一个朴素想法,即把不同格式的文档都放在同一个平台中进行预览 ...

最新文章

  1. 首次揭秘!大麦如何应对超大规模高性能选座抢票?
  2. python编程基础是什么-编程学习第一步,让你20天搞定Python编程
  3. WinCE/Mobile上下滑动浏览DataGrid数据 【转】
  4. MySQL INSERT:插入数据(添加数据)
  5. java单词测试,java单词 - 在线打字测试(dazi.kukuw.com)
  6. Color Cube – 国产的优秀配色取色工具
  7. 湖南科技大学计算机男女比例,湖师大文学院新生男女比例1:9 成了“女儿国”...
  8. 从零开始学前端:CSS字体属性和文本属性 --- 今天你学习了吗?(CSS:Day08)
  9. 计算机维护宝典,超齐全的维修宝典之电脑维修实例大全
  10. ipv6地址_「案例」路由器怎么自动获取IPv6地址?
  11. Codeforces 948D Perfect Security
  12. 代码整洁之道-编写 Pythonic 代码
  13. HID、SCSI、CCID设备的通信
  14. 众包中真值推断的随机猜测和任务难度建模
  15. 学习shell小窍门
  16. BootCDN和npm
  17. Android5 supersu,最新的安卓5.1.1 ROOT教程(不需要刷第三方内核)
  18. Linux虚拟机在线添加GPT格式硬盘
  19. aspose 插入图片的方法
  20. python 流水作业调度,流水作业调度完整代码

热门文章

  1. 大数据在线实习项目|学生消费行为分析在线实习项目-项目介绍
  2. transaction 2 failed while formatting outputs from RPC
  3. 相机标定——相机成像问题
  4. 开源的Remoting框架:Cinnamon
  5. 3Sum 3Sum Closest 4Sum
  6. EasyTouch_摇杆/按钮/触摸板
  7. 演绎真实世界?看人性在游戏中的养成之路
  8. javascript 阻止事件冒泡 cancelBubble
  9. 关于在电脑写的项目在手机上真机测试的注意事项
  10. 软件测试同学碰到不靠谱的开发怎么办?