Vue使用TinyMCE为什么要复制node_modules中的文件到public?

前提概要

TinyMCE官方提供了@tinymce/tinymce-vue,先看下官方的示例:

vue 复制代码
<script setup>
import Editor from '@tinymce/tinymce-vue'
</script>

<template>
  <main id="sample">
    <Editor
      api-key="no-api-key"
      :init="{
        plugins: 'lists link image table code help wordcount'
      }"
    />
  </main>
</template>

www.tiny.cloud/docs/tinymc...

github.com/tinymce/tin...

但如果你找Vue引入TinyMCE的教程,会发现篇幅都相当的长。

排除掉插件和一些个性化配置,所有教程都会比官网教程多了这些步骤:

  1. 复制node_modules/tinymce/skins到public/tinymce/skins中

  2. 导入额外的依赖

    js 复制代码
    import tinymce from 'tinymce/tinymce'
    
    import 'tinymce/icons/default/icons'
    import 'tinymce/models/dom' // 一定要引入
    import 'tinymce/themes/silver' // 界面UI主题
  3. 在初始化对象中指定步骤1中的文件路径

    csharp 复制代码
    const init = {
    	skin_url: '/libs/tinymce/skins/ui/oxide', // skin路径,具体路径看自己的项目
    	content_css: 'libs/tinymce/skins/content/default/content.css'
    }
  4. 初始化编辑器

    js 复制代码
    onMounted(() => {
      tinymce.init({})
    })

大部分只说不这么做,会提示缺失文件导致TinyMCE加载错误,但为什么一个封装好的Vue组件还需要做这些额外步骤呢?

原因

我们不妨先看一下官网的配置,运行起来是什么样子

vue 复制代码
<script setup>
import Editor from '@tinymce/tinymce-vue'
</script>

<template>
  <div style="width: 800px; height: 500px">
    <Editor
      api-key="no-api-key"
      :init="{
        plugins: 'lists link image table code help wordcount'
      }"
    />
  </div>
</template>

运行后会发现要求填入api key才可以使用

同时控制台有如下输出:

vbnet 复制代码
"no-api-key" is not entitled to further Tiny Cloud editor loads.
All created TinyMCE editors are now configured to be read-only.

该api-key需要在官网注册申请。查询网络,可看到TinyMCE的脚本都是从Tiny的cdn下载的

所以教程中额外的步骤是为了让TinyMCE从本地加载。虽然也可以从官网注册一个账户获得APIKEY,但考虑到CDN可能连不上也比较慢,以及在内网使用,通常都会改为本地加载。

步骤解释

  1. 导入额外依赖

    js 复制代码
    import tinymce from 'tinymce/tinymce'
    
    import 'tinymce/icons/default/icons'
    import 'tinymce/models/dom' // 一定要引入
    import 'tinymce/themes/silver' // 界面UI主题

    这一步是为了从本地加载TinyMCE核心库,修改后会使用node_modules中的tinymce,而不再从CDN下载tinymce.min.js。其中后面3个如果不导入,TinyMCE就会从根目录中加载,所以需要一并导入。

  2. 复制node_modules/tinymce/skins,并通过init参数指定skin_urlcontent_css

    • skins是个目录,所以只能通过URL的方式指定

    • content.css即使通过import的方式导入,tinymce还是会从根目录加载

    由于TinyMCE不能从node_modules加载这2个,所以就需要手动复制到public目录下并通过参数指定。当然也可以通过copy-webpack-pluginrollup-plugin-copy等插件在webpack或vite中配置,编译时自动复制过去。

  3. 初始化编辑器

    查看代码可以发现,组件中中已经调用了tinymce.init()方法,那为什么教程都要再调用一次?其实这一步是不必要的。

    js 复制代码
    export const Editor = defineComponent({
      setup: (props: IPropTypes, ctx) => {
        let conf = props.init ? { ...props.init, ...defaultInitValues } : { ...defaultInitValues };
        const initWrapper = (): void => {
          const content = getContent(mounting);
          const finalInit = {
            ...conf,
           // 这里省略了一些默认配置
          };
    	
          getTinymce().init(finalInit);
        };

    如果去掉这个初始化的步骤,就会发现TinyMCE也可以正常使用。

    vue 复制代码
    <script setup>
    import Editor from '@tinymce/tinymce-vue'
    import tinymce from 'tinymce/tinymce'
    import 'tinymce/icons/default/icons'
    import 'tinymce/models/dom'
    import 'tinymce/themes/silver'
    const init = {
      skin_url: '/tinymce/skins/ui/oxide',
      content_css: '/tinymce/skins/content/default/content.css',
    }
    </script>
    
    <template>
      <div style="width: 800px; height: 500px">
        <Editor :init="init" />
      </div>
    </template>

    但有个例外情况,就是当使用ts时,同样的代码却会报错

    ini 复制代码
    tinymce_icons_defaul...279787&v=119ab009:2 Uncaught ReferenceError: tinymce is not defined
        at tinymce_icons_defaul...9787&v=119ab009:2:1

    查看tinymce/icons/default/icons.js中的代码可见,其会调用window上的tinymce对象。如果此时在控制台查询,会发现tinymce不在window中。

    js 复制代码
    // node_modules/tinymce/icons/default/icons.js
    tinymce.IconManager.add("default", {
        icons: {

    原因:

    • TinyMCE会在加载脚本时将自身实例挂到window上,而在组件中由于我们未使用该import的TinyMCE,被ts编译时去掉了,当然其他打包工具的配置也可能去掉未使用的import,导致没有执行。
    • icons由于是采用副作用导入(只是为了加载并执行脚本中的代码,而不导入模块中的任何内容),这种必然会执行。

    因此,只需把tinymce也改成副作用方式导入即可,或者在代码中至少调用一次。

    js 复制代码
    import 'tinymce/tinymce'
相关推荐
老马啸西风3 小时前
工作流引擎-18-开源审批流项目之 plumdo-work 工作流,表单,报表结合的多模块系统
vue.js·开源·activiti·workflow·flowable·oa·bpm
sunbyte4 小时前
50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | Form Wave(表单label波动效果)
前端·javascript·css·vue.js·tailwindcss
每天都想着怎么摸鱼的前端菜鸟6 小时前
uniapp开发app 实现简易的路由拦截器
vue.js·uni-app
琢磨先生TT6 小时前
我用 54000 分钟打造的后台系统模板,开源了!
前端·vue.js·前端框架
用户26124583401616 小时前
vue学习路线(8.事件处理)
前端·vue.js
我血条子呢6 小时前
[Vue2]判断引用组件是否注册了 $emit 对应的事件
前端·javascript·vue.js
sunbyte6 小时前
50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | Sound Board(音响控制面板)
前端·javascript·vue.js·ecmascript·tailwindcss
wxid:yiwoxuan6 小时前
房屋租赁系统 Java+Vue.js+SpringBoot,包括房屋信息、看房申请、租赁合同、房屋报修、收租信息、维修数据、租客管理、公告管理模块
java·vue.js·课程设计
乐予吕7 小时前
从 scoped 到 @scope:CSS 样式隔离的进化
前端·css·vue.js
難釋懷7 小时前
Vue初始化脚手架
前端·javascript·vue.js