likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 7

前提概要

TinyMCE官方提供了@tinymce/tinymce-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. 导入额外的依赖

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

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

    onMounted(() => {
      tinymce.init({})
    })
    

大部分只说不这么做,会提示缺失文件导致TinyMCE加载错误,但为什么一个封装好的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才可以使用

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

同时控制台有如下输出:

"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下载的

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

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

步骤解释

  1. 导入额外依赖

    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就会从根目录中加载,所以需要一并导入。

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

  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()方法,那为什么教程都要再调用一次?其实这一步是不必要的。

    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也可以正常使用。

    <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时,同样的代码却会报错

    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中。

    // node_modules/tinymce/icons/default/icons.js
    tinymce.IconManager.add("default", {
        icons: {
    

    原因:

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

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

    import 'tinymce/tinymce'