Vue使用TinyMCE为什么要复制node_modules中的文件到public?
前提概要
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>
但如果你找Vue引入TinyMCE的教程,会发现篇幅都相当的长。
排除掉插件和一些个性化配置,所有教程都会比官网教程多了这些步骤:
-
复制node_modules/tinymce/skins到public/tinymce/skins中
-
导入额外的依赖
import tinymce from 'tinymce/tinymce' import 'tinymce/icons/default/icons' import 'tinymce/models/dom' // 一定要引入 import 'tinymce/themes/silver' // 界面UI主题
-
在初始化对象中指定步骤1中的文件路径
const init = { skin_url: '/libs/tinymce/skins/ui/oxide', // skin路径,具体路径看自己的项目 content_css: 'libs/tinymce/skins/content/default/content.css' }
-
初始化编辑器
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才可以使用
同时控制台有如下输出:
"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可能连不上也比较慢,以及在内网使用,通常都会改为本地加载。
步骤解释
-
导入额外依赖
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就会从根目录中加载,所以需要一并导入。
-
复制node_modules/tinymce/skins,并通过init参数指定
skin_url
和content_css
-
skins
是个目录,所以只能通过URL的方式指定 -
content.css
即使通过import的方式导入,tinymce还是会从根目录加载
由于TinyMCE不能从node_modules加载这2个,所以就需要手动复制到public目录下并通过参数指定。当然也可以通过
copy-webpack-plugin
、rollup-plugin-copy
等插件在webpack或vite中配置,编译时自动复制过去。 -
-
初始化编辑器
查看代码可以发现,组件中中已经调用了
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'
转载自:https://juejin.cn/post/7345296161056931850