最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 如何用Vue实现简易的富文本编辑器,并支持Markdown语法

    正文概述 掘金(Timfan)   2021-02-07   898

    前端开发经常会用到富文本编辑器,比如CKEditor,动不动一个库几十M的代码量,其中涉及许多你可能用不到的功能特性和相关设置,CKEditor最新版本的代码仓库就有接近2000个JS文件,300,000行代码。

    可是如果你只需要一个简易版的编辑器,真的值得引入这么一个庞大的库吗?

    今天我们从实现一个简易版的编辑器带大家了解一下其背后涉及到的原理。

    开始

    这个编辑器将要使用到markdown:一个简洁语法并且自带样式的语言,而且远比纯HTML的输入输出要安全得多。

    首先,我们需要一些依赖包。 @ts-stack/markdown 和 turndown,@ts-stack/markdown是用来将markdown语法转化为HTML代码显示用的,而turndown是将HTML代码转化为markdown语言。

    接下来,创建一个基础的Vue组件,命名为WysiwygEditor.vue,在组件中添加一个div元素,并且将它的contenteditable 属性设置为true,然后添加一些Tailwind样式去美化一下。

    <!-- WysiwygEditor.vue -->
    <template>
      <div>
        <div
          @input="onInput"
          v-html="innerValue"
          contenteditable="true"
          class="wysiwyg-output outline-none border-2 p-4 rounded-lg border-gray-300 focus:border-green-300"
        />
      </div>
    </template>
    
    <script>
    export default {
      name: 'WysiwygEditor',
    
      props: ['value'],
    
      data() {
        return {
          innerValue: this.value
        }
      },
    
      methods: {
        onInput(event) {
          this.$emit('input', event.target.innerHTML)
        }
      }
    }
    </script>
    

    然后使用该组件:

    <!-- Some other component -->
    <template>
      <!-- ... -->
      <wysiwyg-editor v-model="someText" />
      <!-- ... -->
    </template>
    <!-- ... -->
    

    看起来像这样 如何用Vue实现简易的富文本编辑器,并支持Markdown语法

    现在这个div元素的样式看起来像textarea 标签的效果了。

    让文本变为富文本

    在编辑器的上面会有一些带有bold,italic,underlined,headings,lists等文本的编辑按钮。并且上面会有对应功能的图标。可以通过安装fontawesome icon来实现。然后对按钮进行一些样式设置。

    .button {
      @apply border-2;
      @apply border-gray-300;
      @apply rounded-lg;
      @apply px-3 py-1;
      @apply mb-3 mr-3;
    }
    .button:hover {
      @apply border-green-300;
    }
    

    先将这些按钮添加鼠标点击后的监听方法,后面我们会去实现每一个方法里的具体执行。

    <!-- WysiwygEditor.vue -->
    <template>
      <!-- ... -->
        <div class="flex flex-wrap">
          <button @click="applyBold" class="button">
            <font-awesome-icon :icon="['fas', 'bold']" />
          </button>
          <button @click="applyItalic" class="button">
            <font-awesome-icon :icon="['fas', 'italic']" />
          </button>
          <button @click="applyHeading" class="button">
            <font-awesome-icon :icon="['fas', 'heading']" />
          </button>
          <button @click="applyUl" class="button">
            <font-awesome-icon :icon="['fas', 'list-ul']" />
          </button>
          <button @click="applyOl" class="button">
            <font-awesome-icon :icon="['fas', 'list-ol']" />
          </button>
          <button @click="undo" class="button">
            <font-awesome-icon :icon="['fas', 'undo']" />
          </button>
          <button @click="redo" class="button">
            <font-awesome-icon :icon="['fas', 'redo']" />
          </button>
        </div>
      <!-- ... -->
    </template>
    <!-- ... -->
    

    编辑器现在看起来是这样了 如何用Vue实现简易的富文本编辑器,并支持Markdown语法

    现在看起来是不是越来越接近了。还缺少按钮动作的执行方法。这里要用到document.execCommand,虽然MDN已经宣称将废弃该特性,但是大部分浏览器仍然支持。我们暂且还是使用它。

    让我们通过它来实现applyBold方法

    methods: {
      // ...
    
      applyBold() {
        document.execCommand('bold')
      },
    
      // ...
    }
    

    非常简洁明了,同样,我们来实现其它方法

    // ...
    
      applyItalic() {
        document.execCommand('italic')
      },
      applyHeading() {
        document.execCommand('formatBlock', false, '<h1>')
      },
      applyUl() {
        document.execCommand('insertUnorderedList')
      },
      applyOl() {
        document.execCommand('insertOrderedList')
      },
      undo() {
        document.execCommand('undo')
      },
      redo() {
        document.execCommand('redo')
      }
    
      // ...
    

    这里唯一需要说明的是applyHeading,因为我明确需要在此处指定所需的元素。使用这些命令后,可以预先对输出的元素标签进行一些样式设置

    .wysiwyg-output h1 {
      @apply text-2xl;
      @apply font-bold;
      @apply pb-4;
    }
    .wysiwyg-output p {
      @apply pb-4;
    }
    .wysiwyg-output p {
      @apply pb-4;
    }
    .wysiwyg-output ul {
      @apply ml-6;
      @apply list-disc;
    }
    .wysiwyg-output ol {
      @apply ml-6;
      @apply list-decimal;
    }
    

    有了一定样式后,在输入框中输入一些内容 如何用Vue实现简易的富文本编辑器,并支持Markdown语法

    为了使得更美观一点,把空行用空的段落标签代替,以回车结束的内容归为一个段落

     // ...
      data() {
        return {
          innerValue: this.value || '<p><br></p>'
        }
      },
    
      mounted() {
        document.execCommand('defaultParagraphSeparator', false, 'p')
      },
      // ...
    

    添加markdown支持

    如果我想直接在编辑器里写markdown语法,暂时还不支持

    # Hello, world!
    
    **Lorem ipsum dolor** _sit amet_
    
    * Some
    * Unordered
    * List
    
    
    1. Some
    1. Ordered
    1. List
    

    结果看起来是这样 如何用Vue实现简易的富文本编辑器,并支持Markdown语法

    完全没有任何样式。别忘了,前面我们安装了@ts-stack/markdown库,现在可以使用了

    import { Marked } from '@ts-stack/markdown'
    
    export default {
      name: 'WysiwygEditor',
    
      props: ['value'],
    
      data() {
        return {
          innerValue: Marked.parse(this.value) || '<p><br></p>'
        }
      },
    
    // ...
    

    我们把输入的内容markdown语法转化为HTML代码之后,看起就正常了 如何用Vue实现简易的富文本编辑器,并支持Markdown语法

    同时还需要在组件传出本文编辑器数据的时候,进行转化,这里要用到前面安装的turndown

    import TurndownService from 'turndown'
    
    export default {
    
    // ...
    
      methods: {
        onInput(event) {
          const turndown = new TurndownService({
            emDelimiter: '_',
            linkStyle: 'inlined',
            headingStyle: 'atx'
          })
    
          this.$emit('input', turndown.turndown(event.target.innerHTML))
        },
    // ...
    

    让我们把编辑器中输入的markdown语法文本在页面中通过模板输出后的效果

    <!-- Some other component -->
    <template>
      <!-- ... -->
      <wysiwyg-editor v-model="someText" />
    
      <pre class="p-4 bg-gray-300 mt-12">{{ someText }}</pre>
      <!-- ... -->
    </template>
    

    同步输入输出,内容是一致的,没有任何问题 如何用Vue实现简易的富文本编辑器,并支持Markdown语法 如何用Vue实现简易的富文本编辑器,并支持Markdown语法

    看起来一切正常,达到了我们想要的效果。下面是全部的代码

    <template>
      <div>
        <div class="flex flex-wrap">
          <button @click="applyBold" class="button">
            <font-awesome-icon :icon="['fas', 'bold']" />
          </button>
          <button @click="applyItalic" class="button">
            <font-awesome-icon :icon="['fas', 'italic']" />
          </button>
          <button @click="applyHeading" class="button">
            <font-awesome-icon :icon="['fas', 'heading']" />
          </button>
          <button @click="applyUl" class="button">
            <font-awesome-icon :icon="['fas', 'list-ul']" />
          </button>
          <button @click="applyOl" class="button">
            <font-awesome-icon :icon="['fas', 'list-ol']" />
          </button>
          <button @click="undo" class="button">
            <font-awesome-icon :icon="['fas', 'undo']" />
          </button>
          <button @click="redo" class="button">
            <font-awesome-icon :icon="['fas', 'redo']" />
          </button>
        </div>
    
        <div
          @input="onInput"
          v-html="innerValue"
          contenteditable="true"
          class="wysiwyg-output outline-none border-2 p-4 rounded-lg border-gray-300 focus:border-green-300"
        />
      </div>
    </template>
    
    <script>
    import { Marked } from '@ts-stack/markdown'
    import TurndownService from 'turndown'
    
    export default {
      name: 'WysiwygEditor',
    
      props: ['value'],
    
      data() {
        return {
          innerValue: Marked.parse(this.value) || '<p><br></p>'
        }
      },
    
      mounted() {
        document.execCommand('defaultParagraphSeparator', false, 'p')
      },
    
      methods: {
        onInput(event) {
          const turndown = new TurndownService({
            emDelimiter: '_',
            linkStyle: 'inlined',
            headingStyle: 'atx'
          })
          this.$emit('input', turndown.turndown(event.target.innerHTML))
        },
        applyBold() {
          document.execCommand('bold')
        },
        applyItalic() {
          document.execCommand('italic')
        },
        applyHeading() {
          document.execCommand('formatBlock', false, '<h1>')
        },
        applyUl() {
          document.execCommand('insertUnorderedList')
        },
        applyOl() {
          document.execCommand('insertOrderedList')
        },
        undo() {
          document.execCommand('undo')
        },
        redo() {
          document.execCommand('redo')
        }
      }
    }
    </script>
    

    结论

    只需要87行代码便实现了一个简易的富文本编辑器。虽然功能还是太简单,但是最起码我们知道了实现一个富文本编辑器后面的原理。后面需要增加功能就不是什么难事了。

    分享硬核的编程知识,关注“太空编程”公众号


    起源地下载网 » 如何用Vue实现简易的富文本编辑器,并支持Markdown语法

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元