WangEditor 使用案例

5,690 阅读7分钟

注:本篇文章的代码地址如下

后端代码地址 URL:gitee.com/hotstrip/up…

前端代码地址 URL:gitee.com/hotstrip/Wa…

本篇文章的目录:

  • WangEditor 如何引入到项目中——前端
  • 上传图片实现原理
  • 图片上传接口实现——后端
  • 总结

WangEditor 如何引入到项目中——前端

对于一个不熟悉的组件,如何最快的上手?一般来说,官网和文档是不二选择。官方网站如下:WangEditor

在上面的官网文档上,一共给出了 3 种安装方式:

  • 下载源文件 js 引入
  • 使用包管理工具引入(诸如 npm 之类)
  • 使用在线的 js (CDN)

无论使用哪种方式,原理是一样的,都是加载 js 文件到项目中。本场 chat 使用 Vue.js 做前端开发,因此使用 npm 方式引入是最方便的。

引入方式很简单,安装 Node 环境之后在命令行工具里面输入下面的命令就行:

npm install -S wangeditor 
/** 
 * -S 参数表示安装到本地
 * 对于 Vue 项目会在 package.json 文件里自动更新依赖,
 * 之后在别的电脑上运行电脑就无需再次手动安装,
 * 执行 npm install 命令就可以跟随其他 package.json 中的依赖一起安装了
 * /

安装之后如何初始化呢?先不去考虑代码实现,从思维上来讲就 2 个步骤:

  • 对于 HTML 需要提供一个 DOM 节点
  • 调用 WangEditor 组件的方法,跟上面的 DOM 节点关联

然后创建富文本编辑器就交给 WangEditor 的组件去实现,怎么实现的不用在意,它就像是一个黑盒子,我们给它需要的东西,它就会给我们想要的东西。

理解了思路,那么剩下的就简单了,核心部分代码如下:

/**
 * 用了 Vue 的组件,下面需要关注的就是 <div ref="editorForm"/> 这一行
 * 提供了一个 HTML 的 DOM 节点,对应上面的第一步
 * /
<template>
  <div class="editor">
    <div ref="editorForm"/>
  </div>
</template>

对于第二步,让我们思考一个问题,编辑器很可能会有多个地方调用对吧,那么独立成组件会更加合适吧。对应的使用场景大概像这样——富文本编辑器在子组件里免渲染,其他页面按需引入该组件,同时页面能实时设置和获取到富文本编辑器里面的内容。那么独立组件会面临几个问题:

  1. 页面传递内容参数给富文本编辑器组件
  2. 编辑器内容更新之后,引入富文本编辑器组件的页面能实时获取数据

这两个问题,本质上就是父子组件的传递参数问题,父组件到子组件用 props 属性,子组件到父组件用 $emit 函数。核心代码如下:

props: {
    // 传递过来的编辑器内容参数,用于设置编辑器内容
    content: {
      type: String,
      default: ''
    }
},
data() {
    return {
      // 真正的编辑器里的内容
      editorContent: '',
      // 编辑器对象
      editor: null
    }
},
watch: {
    //  watch 表示监听  当父组件的内容变化时需要更新编辑器的内容
    content() {
      this.editor.txt.html(this.content)
    }
},
/**
 * 这里使用了 Vue 的 mounted 函数钩子,这属于 Vue 生命周期的一个阶段
 */
mounted() {
    // 初始化
    this.editor = new E(this.$refs.editorForm)
    // 当编辑器内容变化的时候通知父组件
    this.editor.customConfig.onchange = (html) => {
      this.editorContent = html
      // 通知父级控件方法,富组件可以通过 editorContent 事件去获取最新的编辑器内容
      this.$emit('editorContent', html)
    }
    this.editor.create()
    this.editor.txt.html(this.content)
}

以上就是子组件的核心代码部分,针对父组件部分就简单了,引入子组件,添加子组件定义的 editorContent 事件就行了。核心代码如下:

<template>
  <div id="app">
    <h1>WangEditor Demo</h1>
    // 子组件将会在这里渲染出来  指定初始化的数据,以及监听数据更新的事件
    <wang-editor :content="content" @editorContent="getEditorContent"/>
    <h2>效果区域展示:</h2>
    <div ref="preview" v-html="content"></div>
  </div>
</template>

<script>
// 引入子组件
import WangEditor from '@/components/wang-editor'
export default {
  name: 'app',
  components: {
    WangEditor
  },
  data() {
    return {
      // 编辑器内容
      content: ''
    }
  },
  methods: {
    // 获取编辑器内容
    getEditorContent(data) {
      this.content = data
    }
  }
}
</script>

完成了这一步,最基本的编辑器就已经好了,效果如下:

效果图

上传图片实现原理

关于上传图片一般用 Base64 或者 form 表单上传,前者是把一张图片转换成 Base64 编码,然后用 img 标签的 src 属性中加上 data:image/jpeg;base64, 前缀交给浏览器渲染出来;后者是用 HTTP 方式上传到服务器上,然后通过 URI 访问图片资源。

使用 Base64 方式,图片不需要上传到服务器,当作字符串一样保存就行,但是字符串长度会很长很长;后者会占用服务器资源作存储,但返回给前端就是一串 URI 地址。

再回到 form 表单上传图片,既然是通过 HTTP 方式,那么肯定会涉及到后端。但不管怎么样都是从 HTTP 的请求体里面拿到图片,然后读取文件流,存储到服务器上,最后返回该图片可访问的 URI 资源。

对于 WangEditor 编辑器的上传图片,同样提供了 Base64 上传和接口上传图片两种方式,下面就是后端上传图片接口的实现。

图片上传接口实现——后端

上面已经提到过,后台提供上传图片接口,这个接口的主要作用就是从 HTTP 请求中获取文件流信息,然后保存到服务器上,同时返回文件的访问地址 URI。重点就是要处理好图片的存储和返回图片存储地址,废话不多说,直接上代码:

/**
 * Created by Administrator on 2019/9/6.
 * 上传图片 Controller 类
 */
@RestController
public class UploadImageController {
    private static Logger logger = LoggerFactory.getLogger(UploadImageController.class);

    /**
     * 上传图片接口地址
     * @param multipartHttpServletRequest
     * @return
     */
    @PostMapping(value = "/uploadImage")
    public Object uploadImage(MultipartHttpServletRequest multipartHttpServletRequest){
        // 图片存储路径
        String path = "src/main/resources/static";
        // 返回值
        HashMap map = new HashMap();
        List<String> data = new ArrayList<>();
        // 取得request中的所有文件名
        Iterator<String> iterator = multipartHttpServletRequest.getFileNames();
        // 遍历
        while (iterator.hasNext()) {
            // 取得上传文件
            MultipartFile multipartFile = multipartHttpServletRequest.getFile(iterator.next());
            if (multipartFile != null) {
                // 文件名
                String fileName = multipartFile.getOriginalFilename();
                // 获取文件拓展名
                String extName = FileUtil.getExtName(fileName);
                if (StringUtils.isEmpty(extName)) {
                    logger.error("文件后缀名称为空,文件可能有问题...");
                    map.put("errno", 1);
                    map.put("data", data);
                    return map;
                }
                // 保证 文件夹存
                File fileDir = new File(path);
                if (!fileDir.exists()){
                    fileDir.mkdirs();
                }
                File file = new File(fileDir, fileName);
                // 拷贝文件流  到上面的文件
                FileUtil.copyInputStreamToFile(multipartFile, file);
                // 构建图片的可访问地址
                String webUrl = multipartHttpServletRequest.getScheme()
                        + "://" + multipartHttpServletRequest.getServerName()
                        + ":" + multipartHttpServletRequest.getServerPort()
                        + multipartHttpServletRequest.getContextPath();
                String imageUrl = file.getPath().substring(path.length()).replaceAll("\\\\", "/");
                logger.info("文件路径: {}", webUrl + imageUrl);
                // 添加到数组中
                data.add(webUrl + imageUrl);
            }
        }
        // 返回前端需要的格式
        map.put("errno", 0);
        map.put("data", data);
        return map;
    }
}

上面的代码就提供了一个上传图片的接口地址,图片会存储在项目的 classpath 下面的 static 目录中,返回值的格式需要和 WangEditor 要求的格式保持一致。

与此同时,前端的富文本编辑器组件中也需要配置上传图片接口地址,如下:

上传图片接口配置
红框中的地址就是后台项目的服务上传图片接口访问地址,前半部分可以在 classpath 中的 application.yml 文件修改,指定端口、项目访问地址,以及静态资源存储地址:

后端程序配置信息

总结

到了这里呢,本场 chat 就算是已经结束了,最后再梳理下整体的逻辑。

一开始遇到新的需求,需要用到不熟悉的组件,最好的开始就是官方文档,从官方文档其实可以看到很多东西。比如如何开始的步骤、提供的功能、设计思路等,然后需要结合自己的需求去做取舍和调整,对于一些苛刻的要求能否用提供的功能去间接实现等。

然后提供了基于 Vue 框架的 WangEditor 富文本编辑器前端部分的设计思路和代码实现,之后简单说明了两种上传图片的方式的优劣。

最后使用 Java 和 Spring Boot 完成了后端部分的图片上传接口,之后修改前端的配置就搞定了一个简单的案例。

注:代码地址如下

后端代码地址 URL:gitee.com/hotstrip/up…

前端代码地址 URL:gitee.com/hotstrip/Wa…