这篇文章上次修改于 903 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

1. 选择 Vditor 的原因

一款好的浏览器端编辑器至少应支持如下功能:

  • 渲染 Markdown
  • 支持图片粘贴上传

Vditor 便很好地满足了上述需求,此外,它还有如下优点:

  • 支持所见即所得模式,比较方便。
  • 样式美观。
  • 有友好的文档
  • 社区上提问问题后,会被解答。

2. Vue 中使用 Vditor

2.1. Vditor 初始化

     const options = {
        cache: {
          enable: false,
        },
        mode: 'wysiwyg',
        height: this.height,  // 编辑器总高度
        typewriterMode: true,
        toolbarConfig: {
          hide: true,  // 隐藏工具栏
        },
        theme: 'dark',  // 编辑器主题
        preview: {
          delay: 100,
          theme: {
            current: 'dark'  // 内容主题
          },
          hljs: {
            enable: true,  // 代码高亮
            style: 'vim',  
            lineNumber: true
          },
        },
        outline: {  // 显示大纲
          enable: true
        },
        value: this.content == null? "": this.content,  // 显示内容
        after: () => {
        },
      }
      this.vditor = new Vditor('vditor', options)

2.2. Vditor 默认为预览模式

img

预览模式的视图是给用户看的,不允许编辑。在上述 after 中模拟【预览】按钮的点击事件即可。

after: () => {
    var evt = document.createEvent('Event');
    evt.initEvent('click', true, true);
    this.vditor.vditor.toolbar.elements.preview.firstElementChild.dispatchEvent(evt);
},

2.3. Vditor 粘贴上传图片

参考 options-upload,创建 vditor 时,加上 upload 函数,向后端发送图片成功后,在正文中插入后端返回的图片 URL。注意,如果考虑安全的话,发送请求时还应该加上 token。

methods: {
    initVditor() {
      const that = this
      const options = {
        ...
        after: () => {
        },
        upload: {
          url: process.env.WEB_API + '/file/upload',
          max: 5 * 1024 * 1024,
          linkToImgUrl: process.env.WEB_API + '/file/upload',
          handler(files) {
            let formData = new FormData()
            for (let i = 0; i < files.length; i++) {
              formData.append('file', files[i])
            }
            let request = new XMLHttpRequest()
            // 图片上传路径
            request.open('POST', process.env.WEB_API + '/file/upload')
            request.onload = that.onloadCallback 
            request.send(formData)
          }
        }
      }
      this.vditor = new Vditor('vditor', options)
      return this.vditor
    },

    onloadCallback(oEvent) {
      const currentTarget = oEvent.currentTarget
      console.log("返回的结果", currentTarget)
      if (currentTarget.status !== 200) {
        return this.$message({
          type: 'error',
          message: currentTarget.status + ' ' + currentTarget.statusText
        })
      }

      let resp = JSON.parse(currentTarget.response)
      let imgMdStr = ''
  
      if (resp.Code === Code.SUCCESS) {
        imgMdStr = `![${'img'}](${resp.Data.ImgUrl})`
      } else {
        return this.$message({
          type: 'error',
          message: resp.Msg
        })
      }
      this.vditor.insertValue(imgMdStr)
    },

后端收到前端传来的图片后,可以保存在本地,或者上传到云存储上,然后返回图片的 URL。本文为简单起见,上传到了腾讯云 COS 上,该服务有比较友好的 SDK 文档。保存在 COS 中图片的 URL 为 ${cos 域名}/图片名。