• React + Node.js『markdown 笔记本 』项目


    一、项目简介

    该项目的主要功能是增删改查笔记,笔记可以使用 markdown 语法编写,笔记内容展示 markdown 语法编译后内容。

    二、项目技术栈

    前端:

    • React 作为前端框架
    • UmiJS 作为前端应用框架,并用 Umi 管理路由
    • Ant Design 作为前端UI框架
    • Axios + ahooks 的 useRequest 发送网络请求
    • TypeScript 进行类型约束
    • Less 编写样式

    后台:

    • 采用 Node.js + Express 作为后台框架
    • 使用 mongodb 数据库

    三、最终效果

    • 首页

    在这里插入图片描述

    • 搜索结果
      在这里插入图片描述

    • 新增笔记
      在这里插入图片描述

    • 笔记详情
      在这里插入图片描述

    • 编辑页面
      在这里插入图片描述

    四、文件目录结构说明

    在这里插入图片描述在这里插入图片描述
    前端:

    • assets:存放共用的图标、图片
    • common:存放共用的资源,如常量、全局 CSS、公共的 js 工具函数、context 文件
    • components:存放多个页面共享的组件
    • pages:划分各个页面
    • services:发送前端网络请求
    • .umirc.ts:配置文件,也可以配置前端路由

    后台:

    • app.js:进行网络请求配置、挂载路由
    • router.js:后台路由
    • modules:存放数据库的 Scheme

    五、核心技术

    1. 引入 normalize.css 对样式进行初始化

    主要解决各大浏览器默认样式不同的问题。同时相比 reset.css ,normalize.css 可以模块化引入、修复了浏览器的一些 bug、还没有复杂的继承链。

    2. 引入 classnames 库,对 className 进行管理

    可以 通过状态 state 实现对类名的控制

    import cs from 'classnames'
    
    const [state, setState] = useState()
    <Layout className={cs(
          globalStyles.layout,
          styles.layout, {
          'layout': state,
    })} >
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3. 使用 useContext 进行兄弟组件间的数据传输

    还挺好用的,但是如果是深层传递数据,可能不能一下知道哪里提供数据、都提供了什么数据,会导致代码复杂一点。

    // createContext
    const TopicListContext = React.createContext({} as IContextProps)
    
    // 提供数据
    <TopicListContext.Provider value={{ searchResult, setSearchResult }}>
       ...
    </TopicListContext.Provider >
    
    // 使用数据
    const { setSearchResult } = useContext(TopicListContext)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    4. 定义公共组件 Header ,实现组件的复用

    通过 props 的 isSearch 参数来判断是否显示搜索框。
    通过获取 url 里的 id 来判断是否显示二级目录。
    在这里插入图片描述
    在这里插入图片描述

    5. 使用 umi 进行路由管理

    umi 具有约定式路由,它不需要手写配置,文件系统即路由,通过目录和文件及其命名分析出路由配置。
    我用的是配置式路由,主要是自己实习外没使用过 umi 的路由,所以想试一试。

    6. 引入 marked 库,对 markdown 语法进行解析

    解析后的内容可以通过 dangerouslySetInnerHTML 对 div 进行 InnerHTML 的替换。

    import { marked } from 'marked'
    
    marked.setOptions({
      renderer: new marked.Renderer(),
      highlight: function (code) {
        return hljs.highlightAuto(code).value;
      },
      gfm: true, // 允许 Git Hub标准的markdown.
      pedantic: false, // 不纠正原始模型任何的不良行为和错误(默认为false)
      sanitize: false, // 对输出进行过滤(清理),将忽略任何已经输入的html代码(标签)
      // tables: true, // 允许支持表格语法(该选项要求 gfm 为true)
      breaks: true, // 允许回车换行(该选项要求 gfm 为true)
      smartLists: true, // 使用比原生markdown更时髦的列表
      smartypants: false, // 使用更为时髦的标点
    })
    
    <div dangerouslySetInnerHTML={{ __html: marked.parse(res.topic.article)}}>
    </div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    7. 引入 moment 库,对时间进行格式化

    import moment from 'moment'
    import 'moment/locale/zh-cn'
    // 使用中文时间
    moment.locale('zh-cn')
    
    {moment(topic?.created_time).format('LL')}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    8. 后台部分

    与博客系统的后台大致相同,使用技术可见该 博客

    六、遇到的问题

    • 报错:参数 “props” 隐式具有 “any” 类型

    对 props 参数进行类型限制。

    type myProps = {
      isSearch: boolean;
    }
    
    export default function Header(props: myProps) {}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 报错:umi.js:373181 Uncaught Error: dangerouslySetInnerHTML does not make sense on < textarea>

    设置 innerHTML 对 textarea 没有意义,因为 textarea 的内容都保存在它的 value 属性中,并且
    textarea 不能呈现 HTML。 如果想在一个容器里放 HTML,可以使用 div、p 等。

    • antd form initialvalues 属性失效

    initalvallues 只在第一次拿到初始值有效,不能初值是空之后再赋值,此时不会生效。不会根据其内容发生变化而进行对应更新。
    此时我们可以通过 setFieldsValue 进行设置,setFieldsValue 可以动态的设置初始值。

    const [form] = Form.useForm()
    form.setFieldsValue({
        title: res.topic.title,
        model: res.topic.model
    })
    
    <Form form={form}>
    	...
    </Form>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • form.setFieldsValue() 不能对 textarea 设置

    组件被div或者其他容器包裹,可以通过 getFiledDecorator 解决。
    参考博客

    <Form.Item  name='article' >
        <div className={styles.markdownDiv}>
              <Input.TextArea />
        </div>
    </Form.Item>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    七、项目链接

    markdown-notebook 项目

  • 相关阅读:
    【Mysql】 linux | mysql | 配置日志路径 | 关闭binlog
    Synchronized 与 Lock 生产者和消费者问题
    KT148A语音芯片组合播放之间有间隔不连贯的处理方法V1
    状态机+策略在工单流转里的使用
    基于偏好的球形修剪多目标差分进化算法(Matlab代码实现)
    移动硬盘无法识别怎么办?有哪些免费的读写硬盘工具
    【机器学习】PyTorch中 tensor.detach() 和 tensor.data 的区别
    原型模式-C++实现
    【Linux】基础IO —— 缓冲区深度剖析
    力扣28. 找出字符串中第一个匹配项的下标
  • 原文地址:https://blog.csdn.net/ladream/article/details/126328941