【系列】使用ThinkPHP6+React+Antd搭建博客系统(十七)

2022-04-10
Cikayo

前言

前面我们已经将博客搭建完成,已经满足了平时对于文章创建和编辑工作。一开始时,我感觉富文本编辑器比较符合我们的使用习惯,但是今天,当我使用 Markdown 做笔记的时候,发现 Markdown 语法更简洁,书写更高效,真是后知后觉。

另外,对于页面中代码的支持,利用 Markdown 高亮插件,可以很方便的进行标记(真的很方便,只能说真香),更符合 沉浸式写作

关于富文本编辑器组件 braft-editor 使用,参考之前的文章:

【系列】使用ThinkPHP6+React+Antd搭建博客系统(十)-富文本编辑器使用

富文本编辑器 braft-editor 也是支持代码高亮的

参考链接: 使用代码高亮扩展

好了,本篇教程将为我们的博客支持 Markdown 编写文章。

第一步 选择React Markdown插件

如果你从Google上搜索 React Markdown ,排名靠前的是 react-markdown 这个插件。但是,这个插件只是对 Markdown语法的解析 ,并不是 Markdown的编辑器 ,而且我也没有找到 react-markdown Markdown 字符串转成 HTML 字符串的API。 而我们需要的是一个 Markdown 编辑器和 Markdown 语法解析器。

这里我们选择 for-editor 作为 Markdown 编辑器,使用 marked Markdown 转义为 HTML 。下面开始代码编写

npm install marked
npm install for-editor
npm install highlight.js // 代码高亮

这里说明一下, for-editor 编辑器中需要使用Markdown的语法,并不会像富文本编辑器一样 所见即所得 ,如果大家需要实时转义的编辑器可以自行寻找。
这里是关于Markdown语法说明: Markdown基本语法

第二步 后台Markdown编辑页面

  1. 新建路由,路由文件位置 src/router/index.js
{
  path: '/article/md-edit/:id?',
  name: 'MarkDown编辑',
  component: () => (<MdEdit />),
  isFull: true,
  hidden: true, // 隐藏
  1. 修改 ArticleList 页面
import { Link, useHistory } from "react-router-dom";
const history = useHistory();
// 这里我们通过type,区分是进入Markdown还是富文本编辑页面
function handleCreateClick(type) {
  switch(type) {
    case 'html':
      history.push("/article/edit");
      break
    default:
      history.push("/article/md-edit");
  1. 编写 MdEdit 页面

MdEdit 页面与 Edit 页面基本是一样的,只是改变一下编辑器插件。

关于 Edit 页面与代码参考,链接: 后端博客管理

页面示例,仅供参考

图示1

下面是部分代码

import { marked } from 'marked'
import Editor from 'for-editor'
import hljs from "highlight.js";
// 配置marked
let renderer = new marked.Renderer();
// 配置链接,点击链接时,打开新的浏览器窗口
renderer.link = function( href, title, text ) {
    return `<a target="_blank" href="${href}" title="${title}">${text}</a>`
marked.setOptions({
    renderer: renderer,
    highlight: function(code, lang) {
      const language = hljs.getLanguage(lang) ? lang : 'plaintext';
      return hljs.highlight(code, { language }).value;
    langPrefix: 'hljs language-', // highlight.js css expects a top-level 'hljs' class.
    pedantic: false,
    gfm: true,
    breaks: false,
    sanitize: false,
    smartLists: true,
    smartypants: false,
    xhtml: false

上面针对 link 标签进行了单独设置,点击链接时,打开新的浏览器窗口
参考配置页面: Github Issues

// 保存Markdown文本内容
const [mdContent, setMdContent] = useState('')
function handleEditorChange(value) {
  setMdContent(value)
<Editor value={mdContent} onChange={handleEditorChange} />
// 保存文章内容,这是使用Antd的Form组件,保存时进行表单验证,提交必要的文章标题、作者等信息
form.validateFields().then(values => {
  let url = articleAddUrl;
  if(id) {
    url = articleEditUrl;
  post(url, {
    status,
    is_markdown: 1,
    md_content: mdContent, // 原始Markdown内容
    content: marked.parse(mdContent), // 转义为HTML内容,方便前端渲染
    ...values

我们可以实现 Markdown 文章内容的编写了,可以增加一个预览效果的功能按钮,点击预览,弹层看到文章的展示,下面是部分代码

const [previewVisible, setPreviewVisible] = useState(false);
const [previewHTMLContent, setPreviewHTMLContent] = useState('')
// 预览方法
function handlePreviewClick() {
  let html = marked.parse(mdContent)
  setPreviewVisible(true)
  setPreviewHTMLContent(html)
// 取消预览
function handlePreviewHTMLCancel() {
  setPreviewVisible(false)
  setPreviewHTMLContent('')
// 预览按钮
<Button
  shape="round"
  size="large"
  onClick={handlePreviewClick}>预览</Button>
// 预览组件
<HTMLPreviewModal
  visible={previewVisible}
  content={previewHTMLContent}
  handleOk={handlePreviewHTMLCancel}
  handleCancel={handlePreviewHTMLCancel} />
// 预览组件
import { Modal } from 'antd'
import 'highlight.js/styles/base16/onedark.css'  // 引入渲染的主题样式
function HTMLPreviewModal({ visible, content, handleOk, handleCancel }) {
    return (
        <Modal
            title="Markdown内容预览"
            width="60%"
            visible={visible}
            onOk={handleOk}
            onCancel={handleCancel}>
            <div className="site-html-preview-content" dangerouslySetInnerHTML={{ __html:content }}></div>
        </Modal>
export default HTMLPreviewModal;

提示 :这里的主题样式文件,也需要加到前端的文章详情页CSS文件中,否则无法呈现显示效果

第三步 修改数据库表结构

之前设计的表结构,只有一个 content 内容字段。

从上面代码可以看出,我们还需要一个 md_content 字段,专门用来存放 Markdown 的内容(因为 content 存放的是纯HTML字符串,在编辑 Markdown 文章时,再将 content 转义回 Markdown 格式并不方便)。所以这里我增加了两个字段 is_markdown md_content is_markdown 标识是 Markdown 内容,也可以不加,只判断 md_content 字段是否为空即可)。

当我们后台文章编辑完成后,转义成 HTML 内容,保存到 content 字段中,发送给服务器。

第四步 修改前端页面展示

前端页面展示时,如果是 Markdown 内容,我们单独多添加一下 class ,这样方便CSS样式的修改

<!-- 这里的语法使用的ThinkPHP中的模版语法,请结合自己实际情况进行页面渲染 -->
<div class="article-detail__container {if $article.is_markdown == 1}site-markdown-container{/if}">
  <div class="article-detail__content">