zl程序教程

您现在的位置是:首页 >  其他

当前栏目

React 实现一个markdown[1]

2023-02-25 18:16:36 时间

「这是我参与2022首次更文挑战的第19天,活动详情查看:2022首次更文挑战

我想实现类似我们掘金的一个文章发布的markdown,这是我们最后实现的结果

使用组件

React的markdown组件有很多我们这里用到的是react-markdown-editor-lite。他是对 MarkdownIt的再次封装。我们其他的Markdown右半部分的效果展示,是需要我们自己去实现的。这个插件会自动许渲染出右边的效果。

npm地址https://www.npmjs.com/package/react-markdown-editor-lite

演示地址https://harrychen0506.github.io/react-markdown-editor-lite/

标题

实现一下我们的标题输入部分,左部分是一个输入框Input,右边是一个div里面里面有一个提交按钮。

 return (
    <div className={style.markdown}>
      <div className={style.header}>
        {/* onChange(e)   e.target.value */}
        <Input
          className={style.input}
          size="middle"
          onChange={e => setSubmitParams({ ...submitParams, title: e.target.value })}
        />
        <div className={style.btn_con}>
          <Button type="primary" onClick={showDrawer}>
            发布
          </Button>
        </div>
      </div>
    </div>  
      )

对应的样式

.header{
    display:flex;
}
.input{
    width:70%;
    border:0px;
    font-size:24px;
    font-weight: bold;
}
.btn_con{
    width:30%;
    padding:20px;
    text-align: right;
    background-color:#FFFFFF;
}

添加markedown

需要导入一下内容

import MarkdownIt from 'markdown-it';
import 'react-markdown-editor-lite/lib/index.css';
import MdEditor from 'react-markdown-editor-lite';
return (
    <div className={style.markdown}>
      <div className={style.header}>
        {/* onChange(e)   e.target.value */}
        <Input
          className={style.input}
          size="middle"
          // 改变提交参数的title
          onChange={e => setSubmitParams({ ...submitParams, title: e.target.value })}
        />
        <div className={style.btn_con}>
        // 展示抽屉
          <Button type="primary" onClick={showDrawer}>
            发布
          </Button>
        </div>
      </div>
      <MdEditor
        value={text}
        // 给markdown一个高
        style={{ height: '500px' }}
        // 这个必传 用于右边展示效果的渲染
        renderHTML={text => mdParser.render(text)}
        // 改变markdown内容触发 函数 handleEditorChange 
        onChange={handleEditorChange}
        // 配置html md是否渲染
        config={{
          view: {
            menu: true,
            md: true,
            html: true,
          },
        }}
      />
    </div>  
      )

全屏问题

点击这个全屏图标进入全屏后,发现我们刚才的标题部分不见了

审查元素发现是这个Markdown给我们的title内容遮盖住了。这个Markdown的class名是full

因此给full添加一个样式,让他距离顶部一个title内容的高度

:global{
    .full {
        top:70px !important;
    }
}

完善组件功能

上面的代码只是在样式上实现了,我们来继续完善功能。

用到的变量

提交参数

这是我们整个页面提交到后端的参数。对应我们的Markdown的有markedown和html

  // 提交参数
  const [submitParams, setSubmitParams] = useState<SubmitParams>({
    html: '',
    markedown: '',
    user_id: '2',
    desc: '',
    title: '',
    user: 'ss',
    date: new Date(),
    type: '',
    column: '',
    cover: '',
    publish: false,
  });

Markdown用到的参数

  // Markdown文本
  const [text, setText] = useState();
  // MarkDown HTML
  const [html, setHtml] = useState();

方法

我们看一下上面的<MdEditor>组件。看他用到的方法

 <MdEditor
        value={text}
        style={{ height: '500px' }}
        // 用于右边展示效果的渲染
        renderHTML={text => mdParser.render(text)}
        onChange={handleEditorChange}
        config={{
          view: {
            menu: true,
            md: true,
            html: true,
          },
      
        }}
      />

mdParser

这个方法是对Markdown的内容处理之后再渲染。这里最后对Markdown中的code内容进行了高亮处理。

// 声明Markdown组件
const mdParser = new MarkdownIt({
  html: true,
  linkify: true,
  typographer: true, // 设置代码高亮的配置
  highlight(code, language) {
    if (language &amp;&amp; hljs.getLanguage(language)) {
      try {
        return `<pre><code class="hljs language-${language}">${hljs.highlight(code, { language }).value}</code></pre>`;
      } catch (__) {}
    }

    return `<pre class="hljs"><code>${mdParser.utils.escapeHtml(code)}</code></pre>`;
  },
});

这里需要引入一些样式样式

 npm install github-markdown-css
// react-markdown-editor-lite中自带了,如果没有需要下载
import hljs from 'highlight.js'; // 引入highlight.js库  代码高亮
import 'highlight.js/styles/github.css'; // 引入github风格的代码高亮样式
// 这个dark风格跟我的有些样式冲突 所以没使用
// import 'highlight.js/styles/dark.css'

实现效果如下

handleEditorChange

这是Markdown内容改变触发的函数,html和text分别是Markdown对应的标签和md格式。

  // 文本编辑器内容变化
  const handleEditorChange = ({ html, text }) => {
    //给变量赋值
    setText(text);
    setHtml(html);
    // 这里我们提取标签中的文字然后作为文字简介
    const reg = /<[^<>]+>/g; // 1、全局匹配g肯定忘记写  2、<>标签中不能包含标签实现过滤HTML标签
    const text2 = html.replace(reg, '').replace(/[\r\n]/g,"");
    // 这里将html和desc(文章简介)赋值给提交参数
    // desc进行了截取处理
    setSubmitParams({ ...submitParams, html, desc: text2.slice(0, 100) });
  };

如图我们写一个表格,其对应的html 和 text如下

完善上传图片

到这里,我们基本实现了一个Markdown,但是对于Markdown的图片,其实还是需要上传到后端,后端再返回给前端一个地址的

  • onImageUpload={handleImageUpload} 上传图片是触发函数handleImageUpload
  • imageUrl: 'https://octodex.github.com/images/minion.png' 如果上传图片失败返回一个默认图片地址。
  <MdEditor
        value={text}
        style={{ height: '500px' }}
        // 用于右边展示效果的渲染
        renderHTML={text => mdParser.render(text)}
        onChange={handleEditorChange}
        onImageUpload={handleImageUpload}
        config={{
          view: {
            menu: true,
            md: true,
            html: true,
          },
          imageUrl: 'https://octodex.github.com/images/minion.png',
        }}
      />
 

handleImageUpload

async function handleImageUpload(file, callback) {
  const formData = new FormData();
  formData.append('file', file);
  //走接口
  await UploadImage(formData).then(res => {
    callback(res.url);
  });
  const reader = new FileReader();
  // reader.onload = () => {

  // };
  reader.readAsDataURL(file);
}