likes
comments
collection
share

藜麦计划 · 纯前端的Markdown编辑器(一 · 需求及思路)

作者站长头像
站长
· 阅读数 5

前言

大家好啊,这里是懂一点项目管理和前端开发的狗子。

最近狗子的人生已经重启完毕,即将开始React开发的新篇章。但说实话,我React开发的技术确实还在基础水平,想检验一下自己的项目开发能力。

于是藜麦计划就诞生了,这是藜麦计划的第一个项目,纯练手项目,不喜勿喷。

计划说

因为是第一篇嘛,俺没计划写一篇文章作为大纲简介(后续可能写),所以暂时先写在这,所有文章以项目形式发布,所以大佬们看到项目的某一篇文章,就可以找到所有该项目的文章

Markdown编辑器

第一个项目我预计分为三篇文章讲述我如何开发一个纯前端的Markdown编辑器功能(模仿的是xx,你懂的),太过高级的功能后续持续随缘开发,主要是基础功能的讲解,欢迎大佬们吐槽

本文源码地址:antd-tools

本文体验地址:antd-tools

Markdown编辑器(一 · 需求及思路)

这一篇文章主要是讲需求,如果大致了解了Markdown需求的大佬们,可以直接移步下一篇文章

  • 我们要做什么?什么是Markdown?
  • 如何做?
  • 整体思路及流程
  • 总结

Markdown编辑器(二 · 核心代码开发)

这一篇主要讲述基础功能的开发部分逻辑的优化

  • 基础显示
  • 列表 - 有序,无序
  • 任务列表

....

  • 总结

Markdown编辑器(三 · 高级功能及优化)

这一篇主要讲述的是一些稍微复杂的功能实践,同时修改上一篇的逻辑问题

  • 代码块
  • 表格
  • 总结

正文

我们要做什么?什么是Markdown?

万物皆可项目,凡是项目先要明白项目需求是什么,有了需求之后才能知道开发的方向。

项目制定

目标:我们需要完成一个markdown的编辑器(纯前端)

基本语法Markdown 语法速查表

好处

  1. 了解了Markdown内部实现逻辑
  2. 加深了正则表达式的了解
  3. 熟悉了React项目的开发
  4. 学会了github.io的部署

如何做?

那如何做呢?

俗话说:要致富,先修路。

咱们先把核心的逻辑理清楚,其他的拼拼凑凑也能用。

藜麦计划 · 纯前端的Markdown编辑器(一 · 需求及思路)

敲黑板! 核心的来了哈!

基础方案:用正则表达式匹配Markdown语法的各种写法

藜麦计划 · 纯前端的Markdown编辑器(一 · 需求及思路)

正则表达式匹配

首先呢,打开你们的文心一言豆包Bing AIChatGPT等AI工具。

标题匹配

输入Prompt:写一个正则,用于解析markdown语法中的标题

ok,我们就能得到一个正则表达式:

const regex = /^\s*#{1,6}\s+/

大家获得到的正则可能不一致,可以先用起来,看能不能正常匹配到标题。

意思是:

  1. 匹配#字符,它可能会出现1-6个。
  2. #字符前会出现0-1个空白符,后面会出现1+个空白符。

这样,标题的核心就拿到了,接下来拿加粗的核心

文本加粗

按照标题的方案以此类推

输入Prompt:写一个正则,用于解析markdown语法中的加粗文字

const regex = /\*\*(.*?)\*\*|__(.*?)__/g

意思是:

  • ** 和 __ 分别匹配两个星号或两个下划线,他们都可以表示加粗。
  • (.*?) 是一个非贪婪匹配,用于捕获两个星号或下划线之间的任意字符(即要加粗的文字)。
  • | 是或运算符,表示匹配前面的模式或后面的模式。

根据我提供的标题文本加粗,大家举一反三,将其他的正则都可以获取到(有bug就解决bug),后续写其他格式的时候,我也会将正则写出来,用于给大家参考。

整体思路及流程

在核心逻辑解决的情况下,接下来可以梳理整体项目的设计了,于是我们可以开始概要设计了。

我们最终的效果大概是酱紫的:

藜麦计划 · 纯前端的Markdown编辑器(一 · 需求及思路)

模块划分及流程图

基础流程比较简单:输入 - 分析 - 渲染

藜麦计划 · 纯前端的Markdown编辑器(一 · 需求及思路)

根据AI提供的方向,我们一共需要做这几个模块:

藜麦计划 · 纯前端的Markdown编辑器(一 · 需求及思路)

PS:具体什么是AST我就不多说了,基础文章太多。

PSS:只是我们的AST是自定义的,并非已有的AST语法树。

为什么?

很明显,如果不是很明白思路的大佬们会问:为什么要词法分析和语法分析?

我是写了2天才慢慢明白为什么的

词法分析主要是分析每一行或者是每一个模块里面有什么,然后统一放到一起,相当于创建了一个一维数组,每一行就是一个数据。它没办法去匹配文字中所有相同的模块,因为会导致乱序(当然,你有更好的办法就说)。

藜麦计划 · 纯前端的Markdown编辑器(一 · 需求及思路)

语法分析主要是组合在词法分析中有关联的模块(语法),并且生成一个AST,AST中能够更加精确地描述出当前语句和上下文有什么关系。

以上就是我的理解

大概想法已经确定,接下来开始编码。

基础项目搭建(可跳过)

我的项目是基于 Antd 的React + TS项目,所以搭建比较简单,这里不做要求,大家各自搭建自己的项目即可。

PS:这里吐槽一句,确实TS在写这种Demo的时候很耗时间,当然了,也很好查问题。

在 Vite 中使用Antd

然后在src中创建一个markdown文件夹

藜麦计划 · 纯前端的Markdown编辑器(一 · 需求及思路)

然后再加入当前非常好用的Tailwind CSS!省掉我们写大量css代码的解决方案。

Install Tailwind CSS with Vite

基础文件创建(扫一眼就行)

index

创建index.tsx用于渲染我们的markdown页面,整体的结构也非常简单(样例看上面的图):

左←输入框,右→显示框

输入框我们直接使用Antd的TextArea,右侧排版使用的是Typography排版组件。

再加上一些边边角角的样式,于是主函数就生成了:

PS:我最上层div定义了一个固定的宽高,所以class可以用h-full

export default function MarkdownToHtml() {
    const [markdownText, setMarkdownText] = useState(textDemo);
    // TODO 分词,解析为tokens

    // TODO 根据上一步所得到的tokens,形成AST
    const ast = {};

    return (
        <>
            <div className="h-full w-full flex backdrop-opacity-10 backdrop-invert rounded">
                <div style={{ flex: `0 0 40rem` }}>
                    <MarkdownInput defaultValue={markdownText} onChange={setMarkdownText} />
                </div>
                <Typography className="w-full p-2 text-left border
                    border-stone-300 border-solid rounded">
                    {/* <GenerateHtml node={ast} /> */}
                </Typography>
            </div>
        </>
    );
}

输入框暂时没有特别复杂,一起写到index文件中,后期如果写复杂了,就抽离出来。

function MarkdownInput({ onChange, defaultValue }: {
    onChange: (value: string) => void;
    defaultValue?: string
}) {
    return (
        <>
            <div className="h-full">
                <TextArea
                    defaultValue={defaultValue}
                    placeholder="Write markdown"
                    style={{ height: '100%', resize: 'none', backgroundColor: 'rgba(255, 255, 255, 0.8)' }}
                    onChange={e => {
                        onChange(e.target.value);
                    }} />
            </div>
        </>
    )
}

最终结果渲染

大家在上面看到了我有一个GenerateHtml组件注释了,也没有写到index里面,主要还是觉得这一块会很大,里面的if逻辑会挺多,所以单独拿出来了。

创建一个generate-html文件

不过这块是下一篇文章的内容,暂时就写一个空的标签

export default function GenerateHtml({ node }: Props) {
    return <></>;
}

GenerateHtml组件主要是根据AST结构渲染成不同的节点,例如对于文字的一些效果,我们就可以在Antd文档里面找到:

Typography 排版

藜麦计划 · 纯前端的Markdown编辑器(一 · 需求及思路)

常量和类型

按照规范,我们所有常量文件全部放入到 const.ts 中,例如上面已经获取到的正则表达式。

藜麦计划 · 纯前端的Markdown编辑器(一 · 需求及思路)

类型文件也丢到type.d.ts

词法分析和语法分析

这一块也是第二章主要讲解的内容,暂时先创建两个文件:tokenize.tsparse.ts

tokenize用于解析输入原文,转换为token。

parse用于整合token,转为AST。

总结

以上就是项目初期做的准备,良好的项目需要一个简单的开始。

这一篇文章我们了解到了几个问题:

为什么要做

该怎么做

前期如何准备

下一章的链接已经在上面给大家了,有想法的大佬们请继续品尝。