Blog
首页
文档
收藏
关于
  • 在线转换时间戳 (opens new window)
  • 在线压缩图片 (opens new window)
  • Float-Double转二进制 (opens new window)
  • 文件转Hex字符串 (opens new window)

HiuZing

🍑
首页
文档
收藏
关于
  • 在线转换时间戳 (opens new window)
  • 在线压缩图片 (opens new window)
  • Float-Double转二进制 (opens new window)
  • 文件转Hex字符串 (opens new window)
  • 前端面试题

  • JavaScript

  • Vue2

  • port

  • CSS

  • Node.js

  • JavaScript优化

  • uniapp

  • Mini Program

  • TypeScript

  • 面向对象编程

  • UI组件

  • Plugin

  • Vue3

    • 教程

    • Vue Router

    • API

    • Vuex

    • 实例处理方案

      • 图标处理方案
      • 本地缓存处理方案
      • 响应拦截器处理方案
      • 登录鉴权处理方案
      • 退出登录处理方案
      • 国际化处理方案
      • 动态换肤处理方案
      • Screenfull全屏处理方案
      • HeaderSearch 处理方案
      • TagsView处理方案
      • Guide 处理方案
      • Excel 导入处理方案
      • 打印详情处理方案
      • 权限受控处理方案
      • 动态表格处理方案
      • 富文本和markdown处理方案
        • 辅助业务:创建文章基本结构实现
        • 编辑库选择标准
        • 新建文章:markdown 实现
        • 新建文章:markdown 文章提交
        • 新建文章:markdown 文章编辑
        • 新建文章:富文本 实现
        • 总结
      • 项目部署处理方案
      • 可视化处理方案
    • 文档

    • 用法

  • 性能优化

  • Axios

  • 状态管理

  • React

  • Mock

  • Icon

  • Template

  • 构建工具

  • 项目规范配置

  • Taro

  • SVG

  • React Native

  • 前端
  • Vue3
  • 实例处理方案
HiuZing
2023-03-22
目录

富文本和markdown处理方案

核心业务:编辑文章。

编辑文章 提供了两种编辑方式:

  1. 富文本
  2. markdown

主要就是分成三个部分:

  1. 辅助业务:创建文章、编辑文章
  2. 富文本库:介绍 、使用
  3. markdown:介绍、使用

# 辅助业务:创建文章基本结构实现

创建文章 的基本结构主要分成三部分:

  1. article-create 页面:基本结构

  2. Editor 组件:富文本编辑器

  3. Markdown 组件:markdown 编辑器

  4. 创建 views/article-create/components/Editor

  5. 创建 views/article-create/components/Markdown

  6. 在 views/article-create 完成基本结构

    <template>
      <div class="article-create">
        <el-card>
          <el-input
            class="title-input"
            :placeholder="$t('msg.article.titlePlaceholder')"
            v-model="title"
            maxlength="20"
            clearable
          >
          </el-input>
          <el-tabs v-model="activeName">
            <el-tab-pane :label="$t('msg.article.markdown')" name="markdown">
              <markdown></markdown>
            </el-tab-pane>
            <el-tab-pane :label="$t('msg.article.richText')" name="editor">
              <editor></editor>
            </el-tab-pane>
          </el-tabs>
        </el-card>
      </div>
    </template>
    
    <script setup>
    import Editor from './components/Editor.vue'
    import Markdown from './components/Markdown.vue'
    import { ref } from 'vue'
    
    const activeName = ref('markdown')
    const title = ref('')
    </script>
    
    <style lang="scss" scoped>
    .title-input {
      margin-bottom: 20px;
    }
    </style>
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38

# 编辑库选择标准

对于现在的前端编辑库(markdown 与 富文本)而言,如果仅从功能上来去看的话,那么其实都是相差无几的。

随便从 github 中挑选编辑库,只要 star 在 10K(保守些) 以上的,编辑器之上的常用功能一应俱全。

如果你现在想要去选择一个编辑库,那么可以从以下几点中进行选择:

  1. 开源协议 (opens new window):其中尽量选择 MIT 或者 BSD 协议的开源项目 image-20211006194630048
  2. 功能:功能需要满足基本需求
  3. issue:通过 issue 查看作者对该库的维护程度 image-20211006194612197
  4. 文档:文档越详尽越好,最好提供了中文文档(英文好的可以忽略)
  5. 国产的:或许你 朋友的朋友的朋友 就是这个库的作者

选择以下编辑器库:

  1. markdown 编辑器:tui.editor (opens new window)
  2. 富文本编辑器:wangEditor (opens new window)

推荐编辑器库:

  1. markdown 编辑器:
    1. tui.editor (opens new window):Markdown 所见即所得编辑器-高效且可扩展,使用MIT开源协议。
    2. editor (opens new window):纯文本 markdown 编辑器
    3. editor.md (opens new window):开源可嵌入的在线markdown编辑器(组件),基于 CodeMirror & jQuery & Marked。国产
    4. markdown-here (opens new window):谷歌开源,但是已经 多年不更新 了
    5. stackedit (opens new window):基于PageDown,Stack Overflow和其他Stack Exchange站点使用的Markdown库的功能齐全的开源Markdown编辑器。两年未更新了
    6. markdown-it (opens new window):可配置语法,可添加、替换规则。挺长时间未更新了
  2. 富文本编辑器:
    1. wangEditor (opens new window):国产、文档详尽、更新快速
    2. tinymce (opens new window):对 IE6+ 和 Firefox1.5+ 都有着非常良好的支持
    3. quill (opens new window):代码高亮功能、视频加载功能、公式处理比较强。
    4. ckeditor5 (opens new window):编辑能力强
    5. wysiwyg-editor (opens new window):收费的 , 就是牛

# 新建文章:markdown 实现

  1. 下载 tui.editor (opens new window)

    npm i @toast-ui/[email protected]
    
    1
  2. 渲染基本结构

    <template>
      <div class="markdown-container">
        <!-- 渲染区 -->
        <div id="markdown-box"></div>
        <div class="bottom">
          <el-button type="primary" @click="onSubmitClick">{{
            $t('msg.article.commit')
          }}</el-button>
        </div>
      </div>
    </template>
    
    <script setup>
    import {} from 'vue'
    </script>
    
    <style lang="scss" scoped>
    .markdown-container {
      .bottom {
        margin-top: 20px;
        text-align: right;
      }
    }
    </style>
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
  3. 初始化 editor ,处理国际化内容

    <script setup>
    import MkEditor from '@toast-ui/editor'
    import '@toast-ui/editor/dist/toastui-editor.css'
    import '@toast-ui/editor/dist/i18n/zh-cn'
    import { onMounted } from 'vue'
    import { useStore } from 'vuex'
    
    // Editor实例
    let mkEditor
    // 处理离开页面切换语言导致 dom 无法被获取
    let el
    onMounted(() => {
      el = document.querySelector('#markdown-box')
      initEditor()
    })
    
    const store = useStore()
    const initEditor = () => {
      mkEditor = new MkEditor({
        el,
        height: '500px',
        // 样式 竖向
        previewStyle: 'vertical',
        language: store.getters.language === 'zh' ? 'zh-CN' : 'en'
      })
    
      mkEditor.getMarkdown()
    }
    </script>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
  4. 在editor在语言改变时,重置 editor

    import { watchSwitchLang } from '@/utils/i18n'
    
    watchSwitchLang(() => {
      if (!el) return
      // 拿到用户输入的内容
      const htmlStr = mkEditor.getHTML()
      // 先处理掉
      mkEditor.destroy()
      // 重新初始化
      initEditor()
      // 重新赋值
      mkEditor.setHTML(htmlStr)
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

# 新建文章:markdown 文章提交

  1. 在 api/article 中,定义创建文章接口

    /**
     * 创建文章
     */
    export const createArticle = (data) => {
      return request({
        url: '/article/create',
        method: 'POST',
        data
      })
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  2. 因为 markdown 或者是 富文本 最终都会处理提交事件,所以我们可以把这两件事情合并到一个模块中实现:

  3. 创建 article-create/components/commit.js

    import { createArticle } from '@/api/article'
    import { ElMessage } from 'element-plus'
    import i18n from '@/i18n'
    const t = i18n.global.t
    
    export const commitArticle = async (data) => {
      const res = await createArticle(data)
      ElMessage.success(t('msg.article.createSuccess'))
      return res
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  4. 在 markdown.vue 中导入该方法

    import { commitArticle } from './commit'
    
    1
  5. 触发按钮提交事件

    const props = defineProps({
      title: {
        required: true,
        type: String
      }
    })
    
    const emits = defineEmits(['onSuccess'])
    ...
    // 处理提交
    const onSubmitClick = async () => {
      // 创建文章
      await commitArticle({
        title: props.title,
        content: mkEditor.getHTML()
      })
      // 提交后重置一下
      mkEditor.reset()
      emits('onSuccess')
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
  6. 在 article-create 中传递 title,处理 onSuccess 事件

    // 创建成功
    const onSuccess = () => {
      title.value = ''
    }
    
    1
    2
    3
    4

# 新建文章:markdown 文章编辑

  1. 在 article-detail 中点击编辑按钮,进入创建文章页面

    // 编辑
    const router = useRouter()
    const onEditClick = () => {
      router.push(`/article/editor/${articleId}`)
    }
    
    1
    2
    3
    4
    5
  2. 在 article-craete 中,处理 编辑 相关操作

  3. 获取当前文章数据

    // 处理编辑相关
    const route = useRoute()
    const articleId = route.params.id
    const detail = ref({})
    const getArticleDetail = async () => {
      detail.value = await articleDetail(articleId)
      // 标题赋值
      title.value = detail.value.title
    }
    // articleId存在即编辑
    if (articleId) {
      getArticleDetail()
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  4. 把获取到的数据传递给 markdown 组件

    <markdown
        :title="title"
        :detail="detail"
        @onSuccess="onSuccess"
    ></markdown>
    
    1
    2
    3
    4
    5
  5. 在 markdown 中接收该数据

    const props = defineProps({
     ...
      detail: {
        type: Object
      }
    })
    
    1
    2
    3
    4
    5
    6
  6. 检测数据变化,存在 detail 时,把 detail 赋值给 mkEditor

    // 编辑相关
    watch(
      () => props.detail,
      (val) => {
        if (val && val.content) {
          mkEditor.setHTML(val.content)
        }
      },
      {
        immediate: true
      }
    )
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  7. 创建 编辑文章 接口

    /**
     * 编辑文章详情
     */
    export const articleEdit = (data) => {
      return request({
        url: '/article/edit',
        method: 'POST',
        data
      })
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  8. 在 commit.js 中生成 编辑文章 方法

    export const editArticle = async data => {
      const res = await articleEdit(data)
      ElMessage.success(t('msg.article.editorSuccess'))
      return res
    }
    
    1
    2
    3
    4
    5
  9. 在 markdown 中处理提交按钮事件

    // 处理提交
    const onSubmitClick = async () => {
      if (props.detail && props.detail._id) {
        // 编辑文章
        await editArticle({
          id: props.detail._id,
          title: props.title,
          content: mkEditor.getHTML()
        })
      } else {
        // 创建文章
        await commitArticle({
          title: props.title,
          content: mkEditor.getHTML()
        })
      }
    
      mkEditor.reset()
      emits('onSuccess')
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

# 新建文章:富文本 实现

富文本我们使用 wangEditor (opens new window),所以我们得先去下载 wangEditor (opens new window)

npm i [email protected]
1
  1. 创建基本组件结构

    <template>
      <div class="editor-container">
        <div id="editor-box"></div>
        <div class="bottom">
          <el-button type="primary" @click="onSubmitClick">{{
            $t('msg.article.commit')
          }}</el-button>
        </div>
      </div>
    </template>
    
    <script setup>
    import {} from 'vue'
    </script>
    
    <style lang="scss" scoped>
    .editor-container {
      .bottom {
        margin-top: 20px;
        text-align: right;
      }
    }
    </style>
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
  2. 初始化 wangEditor

    <script setup>
    import E from 'wangeditor'
    import { onMounted } from 'vue'
    
    // Editor实例
    let editor
    // 处理离开页面切换语言导致 dom 无法被获取
    let el
    onMounted(() => {
      el = document.querySelector('#editor-box')
      initEditor()
    })
    
    const initEditor = () => {
      editor = new E(el)
      editor.config.zIndex = 1
      // 菜单栏提示
      editor.config.showMenuTooltips = true
      editor.config.menuTooltipPosition = 'down'
      editor.create()
    }
    </script>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
  3. wangEditor 的 国际化处理 (opens new window),官网支持 i18next (opens new window)

    npm i --save i18next@20.4.0
    
    1
  4. 对 wangEditor 进行国际化处理

    import i18next from 'i18next'
    import { useStore } from 'vuex'
    
    const store = useStore()
    ...
    const initEditor = () => {
      ...
    
      // 国际化相关处理
      editor.config.lang = store.getters.language === 'zh' ? 'zh-CN' : 'en'
      editor.i18next = i18next
    
      editor.create()
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  5. 处理提交事件

    import { onMounted, defineProps, defineEmits } from 'vue'
    import { commitArticle } from './commit'
    
    const props = defineProps({
      title: {
        required: true,
        type: String
      }
    })
    
    const emits = defineEmits(['onSuccess'])
    ...
    const onSubmitClick = async () => {
      // 创建文章
      await commitArticle({
        title: props.title,
        content: editor.txt.html()
      })
    
      editor.txt.html('')
      emits('onSuccess')
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
  6. 不要忘记在 article-create 中处理对应事件

    <editor
        :title="title"
        :detail="detail"
        @onSuccess="onSuccess"
    ></editor>
    
    1
    2
    3
    4
    5
  7. 最后处理编辑

    const props = defineProps({
      ...
      detail: {
        type: Object
      }
    })
    
    // 编辑相关
    // props.detail检测有无数据
    watch(
      () => props.detail,
      (val) => {
        if (val && val.content) {
          editor.txt.html(val.content)
        }
      },
      {
        immediate: true
      }
    )
    
    const onSubmitClick = async () => {
      if (props.detail && props.detail._id) {
        // 编辑文章
        await editArticle({
          id: props.detail._id,
          title: props.title,
          content: editor.txt.html()
        })
      } else {
        // 创建文章
        await commitArticle({
          title: props.title,
          content: editor.txt.html()
        })
      }
    
      editor.txt.html('')
      emits('onSuccess')
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40

# 总结

核心重点就是 编辑库 的选择

常用的编辑库其实主要就分成了这么两种:

  1. markdown
  2. 富文本

因为对于编辑器库而言,它的使用方式都是大同小异的,大家只需要根据我们 《编辑器库选择标准》 来选择使用自己当前情况的编辑器库即可

上次更新: 2025/06/23, 07:26:12
动态表格处理方案
项目部署处理方案

← 动态表格处理方案 项目部署处理方案→

最近更新
01
CodePush
06-22
02
打包发布
03-09
03
常用命令
03-09
更多文章>
Theme by Vdoing | Copyright © 2021-2025 WeiXiaojing | 友情链接
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式