react项目中自定义一个markdown编辑器

news2024/11/24 1:57:20

Markdown 是一种轻量级标记语言。

Markdown是一种简单的格式化文本的方法,在任何设备上看起来都很棒。它不会做任何花哨的事情,比如改变字体大小、颜色或类型——只是基本的,使用你已经知道的键盘符号。

它还允许人们使用易读易写的纯文本格式编写文档,然后转换成有效的 XHTML(或者HTML)文档。

这种语言吸收了很多在电子邮件中已有的纯文本标记的特性。

由于 Markdown 的轻量化、易读易写特性,并且对于图片图表数学公式都有支持,许多网站都广泛使用 Markdown 来撰写帮助文档或是用于论坛上发表消息。

那么在react中,我们如何使用markdown编辑器来写开发文档?查阅资料及实践后,最终形成以下方案,记录下来以供参阅。

插件依赖及目录

依赖插件:

  • 编辑器使用 for-editor,
  • 内容预览及展示采用 react-markdown。
  • 数学公式支持及语法解析使用 remark-math、rehype-katex,数学公式的样式展示需要 katex.min.css 文件支持,见下文。
  • 引入 rehype-raw 解析HTML文本(因为可能仍需解析之前输入的富文本内容)。

依赖安装

yarn add for-editor react-markdown remark-math  rehype-katex rehype-raw 

package.json:组件涉及的依赖及版本

"dependencies": {
    "for-editor": "^0.3.5",  // Markdown编辑
    "react-markdown": "^8.0.6", // Markdown预览
    "rehype-katex": "^6.0.2", // 数学公式katex语法
    "rehype-raw": "^6.1.1", // 支持HTML语法解析
    "remark-math": "^5.1.1", // 支持数学公式
}

目录结构

├─components
    |── MarkdownEditor
        ├─ Index.js 
        └─ MarkdownPreview.js

相关代码及说明

Index.js:编辑器的主体代码

for-editor 是一个基于 react 的 markdown 语法编辑器

for-editor官网地址:https://www.npmjs.com/package/for-editor

Api

属性

nametypedefaultdescription
valueString-输入框内容
placeholderString开始编辑… 占位文本
lineNumBooleantrue是否显示行号
styleObject-编辑器样式
heightString600px编辑器高度
previewBooleanfalse预览模式
expandBooleanfalse全屏模式
subfieldBooleanfalse双栏模式(预览模式激活下有效)
languageStringzh-CN语言(支持 zh-CN:中文简体, en:英文)
toolbarObject如下自定义工具栏
*
  默认工具栏按钮全部开启, 传入自定义对象
  例如: {
    h1: true, // h1
    code: true, // 代码块
    preview: true, // 预览
  }
  此时, 仅仅显示此三个功能键
  注:传入空对象则不显示工具栏
 */
 
toolbar: {
  h1: true, // h1
  h2: true, // h2
  h3: true, // h3
  h4: true, // h4
  img: true, // 图片
  link: true, // 链接
  code: true, // 代码块
  preview: true, // 预览
  expand: true, // 全屏
  /* v0.0.9 */
  undo: true, // 撤销
  redo: true, // 重做
  save: true, // 保存
  /* v0.2.3 */
  subfield: true, // 单双栏模式
}

事件

nameparams 参数defaultdescription
onChangeString: value-内容改变时回调
onSaveString: value-保存时回调
addImgFile: file-添加图片时回调

图片上传

export default function MarkdownEditor({ value, onChangeContext, readOnly }) {  
  // 上传图片
  const addImg = (_file) => {
    console.log(_file)
    mdRef.current.$img2Url(_file.name, 'file_url');
  };

  return (
    <div style={{position: "relative"}}> 
        <ForEditor
        placeholder="请输入Markdown文本" 
        addImg={_file => addImg(_file)}
        /> 
    </div>
  );
}

Index.js完整代码如下:

import React, { Fragment, useRef, useState } from "react";
import { Button, Modal } from "antd";
import ForEditor from "for-editor";
import MdPreview from "../MarkdownEditor/MarkdownPreview";
 
/** https://github.com/kkfor/for-editor
 * @param {string} value Markdown文本内容
 * @param {() => void} onChange 更改内容方法
 * @param {boolean} readOnly 只读状态
 */

export default function MarkdownEditor({ value, onChangeContext, readOnly }) {
  const [visible, setVisible] = useState(false); // 预览弹框状态
  const mdRef = useRef(null); // 编辑器ref

  // 工具栏菜单
  const toolbar = {
    h1: true, // h1
    h2: true, // h2
    h3: true, // h3
    h4: true, // h4
    img: true, // 图片
    link: true, // 链接
    code: true, // 代码块
    // preview: true, // 预览
    expand: true, // 全屏
    /* v0.0.9 */
    undo: true, // 撤销
    redo: true, // 重做
    save: true, // 保存 
    // subfield: true, // 单双栏模式
  };

  // 上传图片
  const addImg = (_file) => {
    console.log(_file)
    mdRef.current.$img2Url(_file.name, 'file_url');
  };

  const handleChange = (value)=> { 
    onChangeContext(value)  
  }

  return (
    <div style={{position: "relative"}}>
      {readOnly ? (
        <MdPreview content={value} />
      ) : (
        <Fragment>
          <Button style={{position: "absolute",right: "44px", top: "11px"}}
            size="small" 
            onClick={() => setVisible(true)}
          >
            预览
          </Button>
          <Modal
            title="Markdown内容预览"
            width="60%"
            okText="关闭"
            open={visible}
            onOk={() => setVisible(false)}
            onCancel={() => setVisible(false)}
            cancelButtonProps={{ style: { display: "none" } }}
          >
            <MdPreview content={value} />
          </Modal>

          <ForEditor
            placeholder="请输入Markdown文本"
            height={360}
            ref={mdRef}
            lineNum={true}
            toolbar={toolbar}
            value={value}
            onChange={handleChange}
            addImg={_file => addImg(_file)}
          />
        </Fragment>
      )}
    </div>
  );
}

组件最终样式

MarkdownPreview.js:预览的主体代码

github地址:https://github.com/remarkjs/react-markdown

import React from "react";
import ReactMarkdown from "react-markdown";
import remarkMath from "remark-math";
import rehypeKatex from "rehype-katex";
import rehypeRaw from "rehype-raw";
 
/**
 * @param {string} content Markdown文本内容
 * @param {Boolean}  escapeHtml 是否转义html语法,参数为true后转义显示html源码
 */

export default function MarkdownPreview({ content }) {
  return (
    <ReactMarkdown   
      remarkPlugins={[remarkMath]}
      escapeHtml={true} // escapeHtml 是否转义html语法,参数为true后转义显示html源码
      rehypePlugins={[rehypeKatex, rehypeRaw]} 
    >
    {content}
    </ReactMarkdown>
  );
}

使用组件

import React, {forwardRef, useEffect, useState } from "react";
import { Button, Select, Space, Form, Input } from "antd";

// 引入自定义的MarkdownEditor组件
import MarkdownEditor from "./MarkdownEditor/Index";

const NewsAudit = forwardRef(({},ref)=> {  

  const [context, setcontext] = useState("") 
  const onChangeContext = (value) => {
    console.log(`onChangeContext`);
    console.log(value);
    setcontext(value)
  };

  return ( 
    <div>
        // 使用自定义的MarkdownEditor组件
        <MarkdownEditor onChangeContext = {onChangeContext} readOnly = {false} value= {context}></MarkdownEditor> 
    </div>
  );
})

export default NewsAudit;

参考文档

  • https://www.jianshu.com/p/a4746fce6ab3

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

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

相关文章

为什么众多大型国企都在升级企业数智化底座?

在数字经济大潮中&#xff0c;数字化转型已不是企业的“选修课”&#xff0c;而是关乎企业生存和长远发展的“必修课”。在企业数字化转型中&#xff0c;国有企业特别是中央企业普遍将数字化转型战略作为“十四五”时期业务规划的重要内容之一&#xff0c;数字化能力也成为衡量…

图文详解CAN Log文件 - ASC文件格式

目录 1 CAN Log文件 -- ASC文件格式 1.1 Header 1.2 版本编号 1.3 经典CAN网络中的描述 1.3.1 经典CAN Standard标准帧的描述 1.3.2 经典CAN Extended扩展帧的描述 1.3.3 CAN Remote远程帧的描述 1.3.4 CAN Error错误帧的描述 1.4 CANFD网络中的描述 1.4.1 经典CAN S…

图解并用 C 语言实现非比较排序(计数排序、桶排序和基数排序)

目录 一、计数排序 二、桶排序 三、基数排序 一、计数排序 算法步骤&#xff1a; 找出待排序数组 arr 中的最小值和最大值&#xff08;分别用 min 和 max 表示&#xff09;。 创建一个长度为 max - min 1、元素初始值全为 0 的计数器数组 count。 扫描一遍原始数组&…

Nacos客户端实例注册源码分析-篇一

Nacos客户端实例注册源码分析-篇一 版本 nacos 服务器端 nacos 2.0.3 实例客户端注册入口 注册案例 回到之前搭建的服务提供者项目 9002 &#xff0c;在真实的生产环境下&#xff0c;如果需要让某一个服务注册到 Nacos 的服务当中&#xff0c;我们引入对应的 nacos 发现依赖&…

4月Google Play政策更新,游戏上架需要注意这几点

3月21日&#xff0c;据路透社报道&#xff0c;由于发现国内某知名电商应用存在恶意软件问题&#xff0c;谷歌已暂时将该APP从商店下架&#xff0c;并表示&#xff1a;将该APP下架是一种安全预防措施&#xff0c;已经下载的用户也会收到警告&#xff0c;提示他们进行卸载。 4月…

基于深度学习的动物识别系统(YOLOv5清新界面版,Python代码)

摘要&#xff1a;动物识别系统用于识别和统计常见动物数量&#xff0c;通过深度学习技术检测日常几种动物图像识别&#xff0c;支持图片、视频和摄像头画面等形式。在介绍算法原理的同时&#xff0c;给出Python的实现代码、训练数据集以及PyQt的UI界面。动物识别系统主要用于常…

c/c++:算术运算符,赋值运算,逻辑运算,比较运算,三目运算,逗号运算,数据类型转换

c/c&#xff1a;算术运算符&#xff0c;赋值运算&#xff0c;逻辑运算&#xff0c;比较运算&#xff0c;三目运算&#xff0c;逗号运算&#xff0c;数据类型转换 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;此时学会c的…

【自定义表格穿梭框】自定义封装jqgrid表格穿梭框,支持分页复选全选(附完整源码及效果图)

【写在前面】其实之前业务中也有这个方面的需求&#xff0c;但是总觉得自己写的有点乱&#xff0c;此时也就借这个机会重新封装一个公共的函数去实现这个穿梭框的效果&#xff0c;支持分页勾选&#xff0c;页面展示已选中和未选择的数据&#xff0c;使得系统操作更友好。 涉及知…

数学建模(三):模拟退火算法(SA)

文章目录模拟退火算法&#xff08;SA&#xff09;一、 概述1、 算法简介2、 核心思想3、 数学原理4、 模拟退火的流程二、 实例分析1、 初始化参数2、 Metrospolis 准则3、 生成新的值4、 获取最优值5、 主程序6、 总代码模拟退火算法&#xff08;SA&#xff09; 一、 概述 1…

折叠屏市场起风,华为、OPPO“你追我赶”

配图来自Canva可画 现如今&#xff0c;智能手机已经成为了人们生活中不可或缺的重要工具&#xff0c;无论是出行&#xff0c;还是社交&#xff0c;亦或是支付&#xff0c;只需要一部智能手机就可以通通搞定。因此&#xff0c;在消费者多样化需求的助推下&#xff0c;智能手机行…

【Spring】—Spring中Bean的配置、作用域

一、Bean的配置 Spring用于生产和管理Spring容器中的Bean&#xff0c;需要开发者对Spring的配置文件进行配置。在实际开发中&#xff0c;最常采用XML格式的配置方式&#xff0c;即通过XML文件来注册并管理Bean之间的依赖关系。 在Spring中&#xff0c;XML配置文件的根元素是…

易基因:全基因组CpG密度和DNA甲基化分析方法比较(MeDIP、RRBS和WGBS)| 研究综述

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 CpG密度&#xff08;CpG density&#xff09;与各种组织中的DNA甲基化相关。基因组按CpG密度分为&#xff1a;CpG岛&#xff08;CpG island&#xff0c;CGI&#xff09;、CpG岛上下游2kb…

FFMPEG VCL Pack Crack显示位置支持或光标

FFMPEG VCL Pack Crack显示位置支持或光标 FFMPEG VCL Pack是一个组合解决方案和平台&#xff0c;用于在Delphi中录制、转换和传播音频和视频&#xff0c;其中包括音频/视频库中的前一个libavcodec。 FFMPEG VCL Pack功能和选项&#xff1a; 新的Live555公司基于Rtsp Media Ser…

基于深度学习的安全帽检测系统(YOLOv5清新界面版,Python代码)

摘要&#xff1a;安全帽检测系统用于自动化监测安全帽佩戴情况&#xff0c;在需要佩戴安全帽的场合自动安全提醒&#xff0c;实现图片、视频和摄像头等多种形式监测。在介绍算法原理的同时&#xff0c;给出Python的实现代码、训练数据集&#xff0c;以及PyQt的UI界面。安全帽检…

设计模式之迭代器模式(C++)

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 一、迭代器模式是什么&#xff1f; 迭代器模式是一种行为型的软件设计模式&#xff0c;提供一种方法能顺序访问聚合对象中的各个元…

如何做好缓存设计?

大家好&#xff0c;我是易安&#xff01;今天我们来谈一谈缓存应该如何设计。 什么是缓存 缓存是一种临时储存数据的方式。当用户查询数据时&#xff0c;系统会首先在缓存中查找&#xff0c;如果数据已经存在于缓存中&#xff0c;则直接使用&#xff0c;否则系统会到数据的原始…

研报精选230410

目录 【行业230410西南证券】医药行业2023年4月投资月报&#xff1a;看好创新药和中药行情【行业230410国信证券】汽车行业4月投资策略&#xff1a;3月新能源乘用车批发销量预计同比增长32%&#xff0c;持续关注板块年报季报行情【行业230410西南证券】医药行业周报&#xff1a…

【集成架构】探索3种顶级「集成框架」Apache、Spring和Mule

正确的集成框架是绑定应用程序架构构建块的粘合剂。应用程序组件必须不断交换关键数据&#xff0c;以方便用户操作、服务扩展、威胁监视、后端操作、事件触发等。如果没有可靠的集成过程&#xff0c;应用程序和服务故障将淹没软件环境。正确的集成框架是绑定应用程序架构构建块…

【JAVA】#详细介绍!!! synchronized 加锁 详解(1)!

本文分以下几点来介绍synchronized&#xff08;根据JDK1.8&#xff09; 1. 介绍synchronized 2. synchronized 为什么能保证线程安全 3. synchronized 的 用法 4. synchronized 的锁特性 目录 1. 介绍synchronized 2. synchronized的用法 2.1 synchronized修饰指定代码块 2…

如何定位Spark数据倾斜问题,解决方案

文章目录前言一、数据倾斜和数据过量二、 数据倾斜的表现三、定位数据倾斜问题定位思路&#xff1a;查看任务-》查看Stage-》查看代码四、7种典型的数据倾斜场景解决方案一&#xff1a;聚合元数据解决方案二&#xff1a;过滤导致倾斜的key解决方案三&#xff1a;提高shuffle操作…