CKEditor富文本编辑器

前言

  • 后台管理系统是电商系统不可或缺的部分。而富文本编辑器在电商后台里又是非常常见的一个功能。
  • 自从前后端分离成为web项目主流,三大框架崛起,jquey成为过去式,jsp等项目逐渐被淘汰,一些优秀的“轮子”也停止了维护
  • 旧的“轮子”已经停止了维护,却没有新的合适的“轮子”换上

我研究过的富文本编辑器: UEditor,vue-quill-editor,CKEditor。区别:

  • UEditor是百度的产品,是我用过最功能最全,最好用的一款。缺点却很致命:1、最新版本停留在了2016-05-26,没有后续更新了。2、最新版本任然有不少bug需要优化。3、前后端没有分离,在现在这个后端高傲到不理你的时代下,除非后端主动使用,否则基本无法完成对接。
  • vue-quill-editor基于一款叫做quill的vue版本,比UEditor体积小了很多,同时功能也很大幅度的缩水,图片上传默认是转成base64插入到html代码中,严重影响上传的操作。
  • CKEditor如果用过前两个,再来看这个,会给你一种眼前一亮的感觉,这是一个高度组件化的插件,几乎所有功能都拆分成组件需要引入。官方还提供了多种风格的编辑器,并且有集成版本和源码版本(集成版开箱即用,源码版自定义功能)。各个框架也有单独的适配组件,配合源码组合食用。

文章虽是自己手写,但学习借鉴了大佬的写过的一篇教程:https://blog.csdn.net/asing1elife/article/details/103936626

官网

CKEditor 5 - 官网
CKEditor 5 - 文档
CKEditor 5 - Github

编辑器风格

  • 经典编辑器 Classic editor


    Classic editor
  • 内联编辑器 Inline editor


    Inline editor
  • 气球编辑器 Balloon editor


    Balloon editor
  • 气球块编辑器 Balloon block editor


    Balloon block editor
  • 文件编辑器 Document editor


    Document editor

安装

这里以Classic editor版本结合vue框架使用为例

安装Classic editor编译版本@ckeditor/ckeditor5-build-classic和用于对接vue框架的插件@ckeditor/ckeditor5-vue

npm install --save @ckeditor/ckeditor5-build-classic
npm install --save @ckeditor/ckeditor5-vue
// Or 同时安装
npm install --save @ckeditor/ckeditor5-build-classic @ckeditor/ckeditor5-vue

官方建议是全局调用ckeditor5-vue,我们当然是没必要的,封装在单独的组件里就行了

接下来简单调用一下ckeditor插件,看一下效果如何:

<template>
  <ckeditor :editor="editor" v-model="editorData" :config="editorConfig"></ckeditor>
</template>

<script>
  import ClassicEditor from '@ckeditor/ckeditor5-build-classic'
  import CKEditor from '@ckeditor/ckeditor5-vue'

  export default {
    name: 'CKEditor',
    components: {
      ckeditor: CKEditor.component
    }
    data () {
      return {
        // 编辑器组件传入编辑器实例
        editor: ClassicEditor,
        // 内容数据
        editorData: '',
        // 编辑器配置
        editorConfig: {
          // 内容为空时显示的提示文字
          placeholder: this.placeholder,
        }
      }
    }
  }
</script>

<style lang="scss" scoped></style>

源码版本自定义集成

通常情况下,官方集成的默认版本,并不能完全符合自己的业务需求,这个时候就需要自定义源码集成版本了

官方文档提供了源码版本的使用教程

首先需要安装依赖

// 编辑器vue组件
npm install --save @ckeditor/ckeditor5-vue
// 编辑器的webpack插件
npm install --save-dev @ckeditor/ckeditor5-dev-webpack-plugin
npm install --save-dev @ckeditor/ckeditor5-dev-utils
// webpack使用的loader插件
npm install --save-dev postcss-loader@3
npm install --save-dev raw-loader@0.5.1

然后编辑webpack配置

官方以vue-cli 3.x为例,也就是单独编辑vue.config.js文件,如果使用的是vue-cli更低的版本,将无法使用vue.config.js文件配置,因此建议升级到vue-cli 3.x以上的版本。

vue-cli的升级,我没有去了解,但是笨办法还是可以有效解决的,新建一个vue项目,直接用vue-cli 3.x以上的版本新建,然后把src整体复制一遍,package.json复制一遍,有额外的webpack的配置,也复制到vue.config.js中,还有项目的版本库别忘了.git文件夹或.svn文件夹,最后重新npm install一遍

如果有特殊原因,不方便使用vue-cli 3.x以上的版本,可以直接在2.x版本中的webpack配置中做同样的配置,不过需要提前对webpack有所了解

下面是vue.config.js中的配置,能看懂wabpack的小伙伴可以仔细看看配置了什么东西,看不懂的直接复制也ok

const path = require('path');
const CKEditorWebpackPlugin = require('@ckeditor/ckeditor5-dev-webpack-plugin');
const {
  styles
} = require('@ckeditor/ckeditor5-dev-utils');

module.exports = {
  // The source of CKEditor is encapsulated in ES6 modules. By default, the code
  // from the node_modules directory is not transpiled, so you must explicitly tell
  // the CLI tools to transpile JavaScript files in all ckeditor5-* modules.
  transpileDependencies: [
    /ckeditor5-[^/\\]+[/\\]src[/\\].+\.js$/,
  ],

  configureWebpack: {
    plugins: [
      // CKEditor needs its own plugin to be built using webpack.
      new CKEditorWebpackPlugin({
        // See https://ckeditor.com/docs/ckeditor5/latest/features/ui-language.html
        language: 'zh-cn',
        addMainLanguageTranslationsToAllAssets: true
      })
    ]
  },

  // Vue CLI would normally use its own loader to load .svg and .css files, however:
  //    1. The icons used by CKEditor must be loaded using raw-loader,
  //    2. The CSS used by CKEditor must be transpiled using PostCSS to load properly.
  chainWebpack: config => {
    // (1.) To handle editor icons, get the default rule for *.svg files first:
    const svgRule = config.module.rule('svg');

    // Then you can either:
    //
    // * clear all loaders for existing 'svg' rule:
    //
    //      svgRule.uses.clear();
    //
    // * or exclude ckeditor directory from node_modules:
    svgRule.exclude.add(path.join(__dirname, 'node_modules', '@ckeditor'));

    // Add an entry for *.svg files belonging to CKEditor. You can either:
    //
    // * modify the existing 'svg' rule:
    //
    //      svgRule.use( 'raw-loader' ).loader( 'raw-loader' );
    //
    // * or add a new one:
    config.module
      .rule('cke-svg')
      .test(/ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/)
      .use('raw-loader')
      .loader('raw-loader');

    // (2.) Transpile the .css files imported by the editor using PostCSS.
    // Make sure only the CSS belonging to ckeditor5-* packages is processed this way.
    config.module
      .rule('cke-css')
      .test(/ckeditor5-[^/\\]+[/\\].+\.css$/)
      .use('postcss-loader')
      .loader('postcss-loader')
      .tap(() => {
        return styles.getPostCssConfig({
          themeImporter: {
            themePath: require.resolve('@ckeditor/ckeditor5-theme-lark'),
          },
          minify: true
        });
      });
  }
};

其他依赖安装

上面安装了项目的配置依赖,接下来安装源码依赖

// npm install --save @ckeditor/ckeditor5-vue
npm install --save @ckeditor/ckeditor5-editor-classic
npm install --save @ckeditor/ckeditor5-essentials
npm install --save @ckeditor/ckeditor5-basic-styles
npm install --save @ckeditor/ckeditor5-link
npm install --save @ckeditor/ckeditor5-paragraph
npm install --save @ckeditor/ckeditor5-theme-lark

创建编辑器组件

自定义内容比较多,所以需要构建一个编辑器组件来封装功能

1、 先把上面安装步骤中的基础用法封装一下,封装成一个数据双向绑定的独立组件

<template>
  <ckeditor :editor="editor" v-model="editorData" :config="editorConfig"></ckeditor>
</template>

<script>
import ClassicEditor from '@ckeditor/ckeditor5-build-classic'
import CKEditor from '@ckeditor/ckeditor5-vue'

export default {
  name: "Editor",
  components: {
    ckeditor: CKEditor.component
  },
  props: {
    value: {
      type: String,
      default: ""
    }
  },
  data() {
    return {
      editor: ClassicEditor,
      editorData: "",
      editorConfig: {
        // 内容为空时显示的提示文字
        placeholder: this.placeholder,
      }
    };
  },
  watch: {
    value(n) {
      if (!this.editor) {
        return;
      }
      // 外部内容发生变化时,将新值赋予编辑器
      if (n && n !== this.editorData) {
        this.editorData = this.value;
      }
    },
    editorData(n) {
      if (n && n !== this.value) {
        // 编辑器内容发生变化时,告知外部,实现 v-model 双向监听效果
        this.$emit("input", n);
      }
    }
  },
  created() {
    this.editorData = this.value;
  }
};
</script>

<style lang="scss" scoped></style>

2、 自定义集成,无非就是自己配置一个ClassicEditor实例去替代官方突出的默认的ClassicEditor@ckeditor/ckeditor5-build-classic,这里稍微设计一些项目结构:因为是全局复用型的组件,我把它放在src/components目录下,新建一个文件夹命名为Editor也就是富文本组件。Editor文件夹中新建index.vue编辑器的主要文件,再新建一个core文件夹存放ClassicEditor实例

目录

index.vue文件暂且先把上面封装好的编辑器组件复制过来

然后我们开始设计自定义的编辑器实例

3、 在core文件夹中新建ckeditor.js文件,引入基础包

import ClassicEditorBase from "@ckeditor/ckeditor5-editor-classic/src/classiceditor";

4、 根据实际需要引入功能插件

/* 逻辑功能 */
// 核心功能
import Essentials from "@ckeditor/ckeditor5-essentials/src/essentials";
// 格式相关
import Autoformat from "@ckeditor/ckeditor5-autoformat/src/autoformat";
// 段落
import Paragraph from "@ckeditor/ckeditor5-paragraph/src/paragraph";
// 粘贴功能
import PasteFromOffice from "@ckeditor/ckeditor5-paste-from-office/src/pastefromoffice";
// 文本转换
import TextTransformation from "@ckeditor/ckeditor5-typing/src/texttransformation";
// 上传文件相关
import UploadAdapter from "@ckeditor/ckeditor5-adapter-ckfinder/src/uploadadapter";
import CKFinder from "@ckeditor/ckeditor5-ckfinder/src/ckfinder";
// 图片相关
import Image from "@ckeditor/ckeditor5-image/src/image";
import ImageCaption from "@ckeditor/ckeditor5-image/src/imagecaption";
import ImageStyle from "@ckeditor/ckeditor5-image/src/imagestyle";
import ImageToolbar from "@ckeditor/ckeditor5-image/src/imagetoolbar";
import ImageUpload from "@ckeditor/ckeditor5-image/src/imageupload";

/* 在编辑器顶部有按钮的功能 */
// 自定义服务器上传
import SimpleUploadAdapter from '@ckeditor/ckeditor5-upload/src/adapters/simpleuploadadapter';
// 缩进
import Indent from "@ckeditor/ckeditor5-indent/src/indent";
// 多媒体文件
import MediaEmbed from "@ckeditor/ckeditor5-media-embed/src/mediaembed";
// 标题
import Heading from "@ckeditor/ckeditor5-heading/src/heading";
// 字体颜色
import FontColor from "@ckeditor/ckeditor5-font/src/fontcolor";
// 字体背景颜色
import FontBackgroundColor from "@ckeditor/ckeditor5-font/src/fontbackgroundcolor";
// 字体大小
import FontSize from "@ckeditor/ckeditor5-font/src/fontsize";
// 字体种类
import FontFamily from "@ckeditor/ckeditor5-font/src/fontfamily";
// 粗体
import Bold from "@ckeditor/ckeditor5-basic-styles/src/bold";
// 斜体
import Italic from "@ckeditor/ckeditor5-basic-styles/src/italic";
// 下划线
import Underline from "@ckeditor/ckeditor5-basic-styles/src/underline";
// 中划线
import Strikethrough from "@ckeditor/ckeditor5-basic-styles/src/strikethrough";
// 上角标
import Subscript from "@ckeditor/ckeditor5-basic-styles/src/subscript";
// 下角标
import Superscript from "@ckeditor/ckeditor5-basic-styles/src/superscript";
// 超文本
import Link from "@ckeditor/ckeditor5-link/src/link";
// 列表
import List from "@ckeditor/ckeditor5-list/src/list";
// 引用文章
import BlockQuote from "@ckeditor/ckeditor5-block-quote/src/blockquote";

5、 创建ClassicEditor类,继承ClassicEditor官方的基础类也就是ClassicEditorBase,并export出去

export default class ClassicEditor extends ClassicEditorBase {}

6、 配置ClassicEditor需要构建的插件

ClassicEditor.builtinPlugins = [
  // 如果没有别的特殊设计,这里就是把上面引入的功能插件全部传进去
  Essentials,
  Autoformat,
  UploadAdapter,
  ...
  List,
  BlockQuote
];

7、 配置初始参数

ClassicEditor.defaultConfig = {
  // 编辑器菜单栏中的按钮项
  toolbar: {
    items: [
      "heading",
      "fontColor",
      "fontBackgroundColor",
      "fontsize",
      "fontfamily",
      "|",
      "bold",
      "italic",
      "underline",
      "strikethrough",
      "subscript",
      "superscript",
      "|",
      "link",
      "blockQuote",
      "|",
      "bulletedList",
      "numberedList",
      // "|",
      // "indent",
      // "outdent",
      "|",
      "imageUpload",
      "mediaEmbed",
      "|",
      "undo",
      "redo"
    ]
  },
  // 图片的配置
  image: {
    toolbar: [
      "imageStyle:full",
      "imageStyle:side",
      "|",
      "imageTextAlternative"
    ]
  },
  // 这个language需要与webpack中配置的language保持一致
  language: "zh-cn"
};

附加:CKEditor官方插件地址

问题:使用源码版本,在npm run dev运行项目的时候会出现一个报错

[CKEditorWebpackPlugin] Error: No JS asset has been found during the compilation. You should add translation assets directly to the application from the `translations` directory. If that was intentional use the `buildAllTranslationsToSeparateFiles` option to get rif of the error.
[CKEditorWebpackPlugin] Error: No translation has been found for the zh-cn language.

研究了很久之后,在官方的github上看见了作者的答复,大致是说这是一直都存在的错误,历史版本里面把这个错误隐藏了,CKEditor5重新放出了这个错误,代码逻辑是正常的,报错不会有影响,以后会想办法解决这个错误

8、 完成组件封装

到这里,自定义的ClassicEditor实列创建完成了,再回到刚才的src/component/Editor/index.vue文件,把引入ClassicEditor的那句改成引入自己的ClassicEditor实例

import ClassicEditor from '@ckeditor/ckeditor5-build-classic'
// 改成
import ClassicEditor from "./core/ckeditor";

配置图片上传

未完待续。。。

?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,029评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,238评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,576评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,214评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,324评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,392评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,416评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,196评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,631评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,919评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,090评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,767评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,410评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,090评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,328评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,952评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,979评论 2 351

推荐阅读更多精彩内容