【系列】使用ThinkPHP6+React+Antd搭建博客系统(十七)
前言
前面我们已经将博客搭建完成,已经满足了平时对于文章创建和编辑工作。一开始时,我感觉富文本编辑器比较符合我们的使用习惯,但是今天,当我使用
Markdown
做笔记的时候,发现
Markdown
语法更简洁,书写更高效,真是后知后觉。
另外,对于页面中代码的支持,利用
Markdown
高亮插件,可以很方便的进行标记(真的很方便,只能说真香),更符合
沉浸式写作
。
关于富文本编辑器组件
braft-editor
使用,参考之前的文章:
富文本编辑器
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编辑页面
-
新建路由,路由文件位置
src/router/index.js
{
path: '/article/md-edit/:id?',
name: 'MarkDown编辑',
component: () => (<MdEdit />),
isFull: true,
hidden: true, // 隐藏
- 修改 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");
- 编写 MdEdit 页面
MdEdit
页面与
Edit
页面基本是一样的,只是改变一下编辑器插件。
关于
Edit
页面与代码参考,链接: 后端博客管理
页面示例,仅供参考
下面是部分代码
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">