Tinymce富文本编辑器实战:初始化、语言包与工具栏定制全解
发布时间:2026/9/20 15:40:11 锦皓数字建站

简介围绕 Tinymce 富文本编辑器的使用与配置整理而成的示例资源面向需要在 Web 项目中集成所见即所得编辑能力的前端开发者重点解决 Tinymce 的安装引入、语言包国际化以及工具栏与工具栏组自定义等常见问题也适合刚接触编辑器二次开发的学习者参考。资源共 758 个文件大小约 2.9MB以 PNG 图片、JS 脚本、CSS 样式为主附带 Vue 组件、HTML 页面、JSON 配置与少量字体文件目录结构贴近真实项目便于按模块查找。已有 119 人浏览学习。从 tinymce-demo 项目骨架中可查看完整的编辑器初始化与销毁、皮肤样式定制、语言包资源配置、工具栏组按钮排序方式等关键实现同时还能了解转译、依赖管理、说明文档等配置文件在项目中的作用以及各类静态资源在编辑器皮肤和界面展示中的分工。整体适合直接复用或按需调整。 最近给公司的后台管理系统换编辑器在Tinymce上折腾了整整两天。从安装、初始化到语言包配置再到工具栏定制一路踩坑一路填坑。趁热把整个实践过程整理出来给正在用或者准备用Tinymce的同行做个参考。之所以选Tinymce主要是因为它在富文本编辑器里属于老牌选手功能扎实、插件生态完整表格、图片、链接、代码块这些高频功能开箱即用不需要像某些轻量编辑器那样到处拼轮子。而且它的定制能力很强从工具栏按钮到插件扩展都有完善的API。当然代价就是配置项多第一次接触容易懵尤其是语言包和工具栏这块官方文档写得比较散网上资料又新旧混杂照着抄经常踩坑。这篇文章我会从三个核心主题展开Tinymce的基本使用和初始化、语言包配置的原理与实操、工具栏与工具栏组的定制方法。每一部分都会给出完整的代码示例和我在实际项目中踩过的坑希望能帮你少走些弯路。1. Tinymce快速上手与初始化先说结论Tinymce的初始化和使用没有想象中复杂核心就是一个tinymce.init()方法关键是把selector、plugins、toolbar这几个配置项搞明白编辑器就能跑起来。但这里面有不少容易忽略的细节比如资源文件路径、主题皮肤加载、内容样式隔离等我会逐个说明。1.1 环境准备与安装方式Tinymce目前主流的接入方式有两种通过npm包管理依赖或者通过CDN直接引入。如果你用的是Vue、React这类工程化项目建议走npm方便版本管理和后续定制。以npm方式举例核心依赖就两个npm install tinymce如果是Vue3项目还需要安装官方的封装组件npm install tinymce/tinymce-vue这里有一个关键点安装完成后仅仅import tinymce是不够的。Tinymce的运行依赖它的静态资源文件themes、skins、plugins这些目录npm包默认不会自动暴露这些文件需要手动把它们拷贝到项目的静态资源目录。在Vue3 Vite项目里我一般这样做// vite.config.js 或者在 main.js 里设置 import { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ plugins: [vue()], assetsInclude: [**/*.js], })更通用的做法是在public目录下建一个tinymce文件夹然后把node_modules里的核心目录拷进去cp -r node_modules/tinymce/themes public/tinymce/ cp -r node_modules/tinymce/skins public/tinymce/ cp -r node_modules/tinymce/plugins public/tinymce/如果漏掉这步编辑器能初始化但界面会丢失样式甚至插件按钮全部失效。我在第一次接入时就漏了皮肤文件折腾了好久才发现是这个问题。1.2 初始化配置的核心参数Tinymce的所有配置都在tinymce.init()里完成下面是我在实际项目中总结的一套最小可用配置tinymce.init({ selector: #editor, height: 500, menubar: true, plugins: [ advlist, autolink, lists, link, image, charmap, preview, anchor, searchreplace, visualblocks, code, fullscreen, insertdatetime, media, table, help, wordcount ], toolbar: undo redo | blocks | bold italic forecolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | removeformat | help, content_style: body { font-family:Helvetica,Arial,sans-serif; font-size:16px } })几个关键配置项说明selectorCSS选择器指定哪个元素被替换为编辑器。一般是一个textarea的id。plugins插件数组决定编辑器支持哪些扩展能力。注意如果某个插件没在plugins里声明即便你在toolbar里写了对应按钮也不会生效。toolbar工具栏按钮配置用空格分隔按钮用竖线|做分组。content_style编辑区域的样式。这里有个小细节Tinymce编辑区域是iframe隔离的外部样式不会作用于编辑区域所以需要在这里定义内容的基础字体和行距。menubar是否显示顶部菜单栏。如果设为false工具栏就只剩一行按钮界面更简洁但会失去一些高级功能入口。这里特别提醒一下init方法执行后返回一个Promise如果配置出错Promise会reject但很多时候错误信息并不直观。如果编辑器一直不出现优先打开浏览器的Network面板看看有没有404请求这是最快定位问题的手段。1.3 组件的封装思路直接用原生tinymce.init()也没有问题但在现代前端框架里我更推荐封装成一个独立的Editor组件。这样做的好处是生命周期可以自动管理组件销毁时能调用tinymce.remove()清理实例避免内存泄漏多个页面复用编辑器时只需要通过props传递配置即可。以Vue3为例封装思路大致是template textarea refeditorRef/textarea /template script setup import { onMounted, onBeforeUnmount, ref, watch } from vue import tinymce from tinymce const props defineProps({ modelValue: String, apiKey: String, toolbar: String, plugins: Array }) const editorRef ref(null) let editor null onMounted(async () { editor await tinymce.init({ selector: #${editorRef.value.id}, value: props.modelValue, plugins: props.plugins, toolbar: props.toolbar, setup(ed) { ed.on(input change undo redo, () { const content ed.getContent() emit(update:modelValue, content) }) } }) }) onBeforeUnmount(() { tinymce.remove(editor) }) /script这种封装模式我已经用了很久稳定性很好。唯一的坑是tinymce.remove()如果传了实例对象部分老版本可能识别不了建议直接传selector字符串tinymce.remove(#editor)。2. 语言包配置详解从原理到实操语言包配置是很多人第一次用Tinymce时最容易卡住的地方。默认情况下Tinymce界面是英文的如果你直接设置language: zh_CN但没加载对应语言包编辑器会加载失败或报错这个坑我见过太多次了。2.1 语言包的加载机制首先理解Tinymce的语言包机制Tinymce本身不内置任何语言文件它会在初始化时根据language配置项去请求一个对应的语言代码.js文件。比如设置language: zh_CN它就会尝试加载zh_CN.js这个文件。这个文件默认从CDN加载官方CDN地址是https://cdn.tiny.cloud/1/{apiKey}/tinymce/langs/zh_CN.js注意这个CDN地址带了apiKey参数这是Tinymce 5以后的规定。如果你没有申请Tiny Cloud的API Key浏览器会返回403错误语言包自然加载失败编辑器初始化也会中断。解决思路有两个第一种是申请Tiny Cloud的免费API Key官方以key区分是否免费账户个人项目用免费额度足够了。但企业内部项目尤其是内网部署的场景CDN可能根本访问不到这就得用第二种方案。第二种是下载语言包并放到本地通过language_url配置项指定本地路径。这也是我推荐的做法。官方GitHub仓库tinymce-langs里有所有语言包的文件直接下载zh_CN.js放到项目的public/tinymce/langs/目录即可。2.2 中文语言包配置实操完整的本地化配置需要三个配置项配合tinymce.init({ selector: #editor, language: zh_CN, language_url: /tinymce/langs/zh_CN.js, // 其他配置 })language指定语言代码中文填zh_CN。language_url语言包文件的本地地址。这个配置项如果没有设置Tinymce才回去请求CDN设置了就用本地文件。如果你使用的是npm包方式还有一种更干净的做法直接通过import引入语言包文件。在main.js或组件入口处引入import tinymce/langs/zh_CN这样语言包会被打包进最终的构建产物不需要手动拷贝文件到静态目录也不会有路径问题。但要注意这种方式要求语言包文件就在tinymce/langs/下npm包默认是不带语言包的需要自己去官方仓库下载后放到该目录或者直接用CDN版本的语言包覆盖。实际测试下来官方CDN的语言包和本地文件在格式上没有差异都是向tinymce.addI18n()注册翻译字典。如果你有自己的翻译需求直接改这个js文件里的字典即可不需要额外写插件。2.3 语言包加载失败的定位与解决我总结了三种最常见的语言包加载失败场景现象原因解决方案控制台显示zh_CN.js 404本地目录没有该文件或路径写错检查public下文件路径确保language_url相对路径正确控制台显示403走了官方CDN但没有API Key设置language_url使用本地文件界面显示中文但部分按钮还是英文插件语言包未加载检查插件对应语言文件部分第三方插件需要单独引入语言文件其中第三种情况比较容易忽略。比如table插件的中文翻译在核心语言包里就有但如果你用了a11ychecker这种高级插件它自己还有一套语言文件。遇到这种情况直接在插件初始化时传入tinymce实例的language即可不用额外处理。另外一个细节如果页面里有多个编辑器实例每个实例都要单独配置language吗答案是可以在全局统一处理。在tinymce.init()之前调用tinymce.addI18n()手动注册语言包然后所有实例就都能共享了。3. 工具栏与工具栏组定制全解等你把编辑器和语言包都跑通了接下来做得最多的就是按照业务需求调整工具栏。这块初看简单无非就是增删按钮但深入进去会发现有很多组合技巧包括按钮分组、多行排列、下拉菜单嵌套、自定义按钮等。3.1 工具栏配置基础按钮、分组与排列工具栏配置使用toolbar字段它是一个字符串由按钮名称button name和分隔符组成空格表示按钮间隔竖线|表示分组。toolbar: undo redo | bold italic underline | bullist numlist这个配置会产生三个区域左侧撤销/重做中间文字加粗/斜体/下划线右侧有序/无序列表。组与组之间会出现一条竖直分隔线视觉上更清晰也方便用户按功能区域寻找按钮。如果你需要配置多行工具栏Tinymce 5以后支持在toolbar字符串里用||分隔不同行toolbar: undo redo | styles | bullist numlist | alignleft aligncenter alignright | link image | table | fullscreen || bold italic underline strikethrough | forecolor backcolor | removeformat | code两行工具栏会自动换行显示第一行放高频操作和需要视觉宽度的控件第二行放字符格式类操作。实际布局时要注意如果你的编辑器容器宽度有限多行工具栏会把编辑区域压缩得很矮此时需要适当增加编辑器的整体高度。工具栏是否换行还和toolbar_mode配置有关。默认是scrolling模式按钮超出宽度时会出现小箭头展开更多如果你希望按钮自动换行而不是收缩改成toolbar_mode: wrap这样工具栏按钮会像文本一样自动换行不会藏起来。3.2 工具栏组的自定义与嵌套除了基础的单按钮排列Tinymce还支持上拉菜单和嵌套分组。最常用的场景是把一类功能收进一个下拉按钮里比如“插入”功能下挂图片、媒体、表格、文件等。实现方式有两种。一种是直接使用Tinymce预置的按钮比如insert就是一个包含多项插入功能的按钮组toolbar: insert | undo redo | ...另一种是使用setup配置项通过editor.ui.registry.addGroupToolbar创建自定义分组。这个方法在Tinymce 5.2以后可用使用场景相对高级一般项目中用预置按钮组就够用了。如果是更复杂的嵌套下拉菜单可以使用addMenuItem配合addContextToolbar。例如给选中图片时单独弹出一条格式工具栏setup(editor) { editor.ui.registry.addContextToolbar(imageToolbar, { predicate: (node) node.nodeName IMG, items: alignleft aligncenter alignright | image tooltip }) }这个配置的效果是鼠标选中图片时编辑器会对当前选区上下文自动弹出工具栏方便快速调整图片位置和样式。这种体验很顺手很多成熟产品都在用。3.3 常用工具栏配置速查与排列建议下面是一套我整理的中文后台系统常用的配置模板覆盖了内容编辑、排版、插入对象、协作代码等场景toolbar: undo redo | blocks fontfamily fontsize | bold italic underline strikethrough | forecolor backcolor | alignleft aligncenter alignright alignjustify | lineheight | bullist numlist outdent indent | table link image media | removeformat fullscreen preview code对应的插件配置plugins: [ advlist, autolink, lists, link, image, charmap, preview, anchor, searchreplace, visualblocks, code, fullscreen, insertdatetime, media, table, wordcount ]这套配置有几点考虑blocks按钮用来切换段落、标题1到标题6是文章排版最常用的按钮。forecolor和backcolor分别控制文字颜色和背景高亮做文章标注很实用。lineheight是行高插件按钮需要配合插件才能出现如果你没有装对应插件写了也不会生效。fullscreen对后台内容管理特别重要编辑长文章时全屏模式能大幅提升体验。code按钮可以让用户直接查看和编辑HTML源码这是很多业务场景的硬需求。值得注意的是Tinymce不同版本对同一功能的按钮命名可能有差异。比如hr插入水平线按钮Tinymce 5里面要用hr插件Tinymce 6里可能需要额外注意命名。遇到按钮不显示时先确认插件是否加载再确认按钮名称是否准确不要盲目改配置。3.4 加载插件与自定义按钮的补充玩法如果你不想满足于系统预置的按钮Tinymce也支持自定义工具栏按钮。方法就是在setup里通过editor.ui.registry.addButton注册一个新按钮然后在toolbar字符串里直接使用按钮idsetup(editor) { editor.ui.registry.addButton(myExportWord, { text: 导出Word, onAction: () { // 在这里调用导出逻辑 console.log(导出Word) } }) }, toolbar: undo redo | myExportWord这里有一个小建议自定义按钮的功能逻辑不要直接写在组件里而是尽量抽成独立的工具函数或模块。因为编辑器可能会在多个页面复用写在组件里会导致功能重复且难以维护。关于热搜词里提到的tinymce export to word插件下载我说下我的理解。官方插件里并没有一个直接的Export to Word按钮但可以通过引入powerpaste插件配合服务端转换实现类似效果或者简单点直接获取编辑器内容后由后端生成Word。我自己的项目里是先获取HTML内容再通过后端服务转换前端只做一个按钮触发这样最省事。4. 常见问题与排查技巧实录最后这部分我整理一下实际使用中遇到的高频问题和排查思路有些是我自己踩过的坑有些是帮同事排查时积累的按照从简单到复杂的顺序排列。4.1 初始化失败与界面显示异常编辑器完全没渲染出来。优先看控制台有没有报错。如果报错信息是tinymce is not defined说明脚本没加载成功检查静态资源路径。如果控制台无声无息但页面空白多半是selector选错了元素常见于用className而不是id或者元素在编辑器初始化后才挂载到DOM上。编辑器渲染出来了但没有样式。这是皮肤文件没加载导致的。确认skins目录已经拷贝到静态目录并且在init里设置了skin: oxide。Tinymce 5/6默认主题皮肤是oxide暗色主题是oxide-dark如果你自定义了皮肤名一定要确保对应目录存在。编辑器高度异常或内容区空白。优先检查是不是初始化时容器还没渲染完毕。特别是在弹窗、抽屉这类动态渲染的组件里需要等DOM挂载完成后再调用init或者用setTimeout延后执行。4.2 内容提交与展示时的常见问题编辑器内容提交后前端展示出来样式不对。这个问题的根源在于Tinymce编辑区域是iframe隔离的内部有一套默认样式但提交到业务页面后这层隔离就没有了内容直接由业务页面的全局样式渲染。解决方案是在业务展示端引入一份和编辑区域一致的内容样式或者使用content_css配置项指定编辑区域的样式文件。我的做法是维护一个editor-content.css里面定义.ql-editor相关的样式然后同时用于编辑器的content_css和业务展示页的样式引入保证WYSIWYG。提交后HTML里有大量多余标签。Tinymce默认会保留很多语义化标签比如p、span等如果业务方不想要这些冗余代码可以在init里配置forced_root_block: p或者在提交时通过getContent({ format: text })只提取纯文本。多语言站点中语言包混乱。如果站点本身支持多语言切换需要在切换时动态重新初始化编辑器。简单做法是销毁现有实例再重新init复杂一点可以用tinymce.activeEditor.setLanguage()动态切换。4.3 性能与体验优化心得在低配置机器比如一些老的办公电脑上Tinymce初始化会有明显的卡顿感。我的经验是通过plugins精简插件列表把用不到的插件全部移除尤其是powerpaste、spellchecker这种重量级插件能明显缩短初始化时间。对于内容特别长的文章编辑和提交时都会比较吃力。可以考虑配置autosave插件自动保存草稿避免浏览器崩溃导致内容丢失。再把编辑器的branding: false关掉页面底部就不会显示官方Logo了界面清爽不少。最后分享一个小经验不管怎么定制工具栏都要保证核心编辑功能优先。富文本编辑器最重要的是“能写、能存、不丢内容”工具栏花里胡哨是锦上添花千万别本末倒置。我见过有同事写了满满四大行工具栏结果整个编辑区域只剩巴掌大这种配置再炫也没意义。Tinymce 6相关文档比5清楚不少但实际上手时依然会有很多细枝末节的问题。这篇整理出来的内容都是实打实处理过的坑照着配置能省下不少排查时间。如果你在接入过程中有别的问题也欢迎在评论区一起交流。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。