:初始化、語言包與工具欄定制全解)
簡介圍繞 Tinymce 富文本編輯器的使用與配置整理而成的示例資源面向需要在 Web 項目中集成所見即所得編輯能力的前端開發(fā)者重點解決 Tinymce 的安裝引入、語言包國際化以及工具欄與工具欄組自定義等常見問題也適合剛接觸編輯器二次開發(fā)的學(xué)習(xí)者參考。資源共 758 個文件大小約 2.9MB以 PNG 圖片、JS 腳本、CSS 樣式為主附帶 Vue 組件、HTML 頁面、JSON 配置與少量字體文件目錄結(jié)構(gòu)貼近真實項目便于按模塊查找。已有 119 人瀏覽學(xué)習(xí)。從 tinymce-demo 項目骨架中可查看完整的編輯器初始化與銷毀、皮膚樣式定制、語言包資源配置、工具欄組按鈕排序方式等關(guān)鍵實現(xiàn)同時還能了解轉(zhuǎn)譯、依賴管理、說明文檔等配置文件在項目中的作用以及各類靜態(tài)資源在編輯器皮膚和界面展示中的分工。整體適合直接復(fù)用或按需調(diào)整。 最近給公司的后臺管理系統(tǒng)換編輯器在Tinymce上折騰了整整兩天。從安裝、初始化到語言包配置再到工具欄定制一路踩坑一路填坑。趁熱把整個實踐過程整理出來給正在用或者準(zhǔn)備用Tinymce的同行做個參考。之所以選Tinymce主要是因為它在富文本編輯器里屬于老牌選手功能扎實、插件生態(tài)完整表格、圖片、鏈接、代碼塊這些高頻功能開箱即用不需要像某些輕量編輯器那樣到處拼輪子。而且它的定制能力很強(qiáng)從工具欄按鈕到插件擴(kuò)展都有完善的API。當(dāng)然代價就是配置項多第一次接觸容易懵尤其是語言包和工具欄這塊官方文檔寫得比較散網(wǎng)上資料又新舊混雜照著抄經(jīng)常踩坑。這篇文章我會從三個核心主題展開Tinymce的基本使用和初始化、語言包配置的原理與實操、工具欄與工具欄組的定制方法。每一部分都會給出完整的代碼示例和我在實際項目中踩過的坑希望能幫你少走些彎路。1. Tinymce快速上手與初始化先說結(jié)論Tinymce的初始化和使用沒有想象中復(fù)雜核心就是一個tinymce.init()方法關(guān)鍵是把selector、plugins、toolbar這幾個配置項搞明白編輯器就能跑起來。但這里面有不少容易忽略的細(xì)節(jié)比如資源文件路徑、主題皮膚加載、內(nèi)容樣式隔離等我會逐個說明。1.1 環(huán)境準(zhǔn)備與安裝方式Tinymce目前主流的接入方式有兩種通過npm包管理依賴或者通過CDN直接引入。如果你用的是Vue、React這類工程化項目建議走npm方便版本管理和后續(xù)定制。以npm方式舉例核心依賴就兩個npm install tinymce如果是Vue3項目還需要安裝官方的封裝組件npm install tinymce/tinymce-vue這里有一個關(guān)鍵點安裝完成后僅僅import tinymce是不夠的。Tinymce的運行依賴它的靜態(tài)資源文件themes、skins、plugins這些目錄npm包默認(rèn)不會自動暴露這些文件需要手動把它們拷貝到項目的靜態(tài)資源目錄。在Vue3 Vite項目里我一般這樣做// vite.config.js 或者在 main.js 里設(shè)置 import { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ plugins: [vue()], assetsInclude: [**/*.js], })更通用的做法是在public目錄下建一個tinymce文件夾然后把node_modules里的核心目錄拷進(jìn)去cp -r node_modules/tinymce/themes public/tinymce/ cp -r node_modules/tinymce/skins public/tinymce/ cp -r node_modules/tinymce/plugins public/tinymce/如果漏掉這步編輯器能初始化但界面會丟失樣式甚至插件按鈕全部失效。我在第一次接入時就漏了皮膚文件折騰了好久才發(fā)現(xiàn)是這個問題。1.2 初始化配置的核心參數(shù)Tinymce的所有配置都在tinymce.init()里完成下面是我在實際項目中總結(jié)的一套最小可用配置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 } })幾個關(guān)鍵配置項說明selectorCSS選擇器指定哪個元素被替換為編輯器。一般是一個textarea的id。plugins插件數(shù)組決定編輯器支持哪些擴(kuò)展能力。注意如果某個插件沒在plugins里聲明即便你在toolbar里寫了對應(yīng)按鈕也不會生效。toolbar工具欄按鈕配置用空格分隔按鈕用豎線|做分組。content_style編輯區(qū)域的樣式。這里有個小細(xì)節(jié)Tinymce編輯區(qū)域是iframe隔離的外部樣式不會作用于編輯區(qū)域所以需要在這里定義內(nèi)容的基礎(chǔ)字體和行距。menubar是否顯示頂部菜單欄。如果設(shè)為false工具欄就只剩一行按鈕界面更簡潔但會失去一些高級功能入口。這里特別提醒一下init方法執(zhí)行后返回一個Promise如果配置出錯Promise會reject但很多時候錯誤信息并不直觀。如果編輯器一直不出現(xiàn)優(yōu)先打開瀏覽器的Network面板看看有沒有404請求這是最快定位問題的手段。1.3 組件的封裝思路直接用原生tinymce.init()也沒有問題但在現(xiàn)代前端框架里我更推薦封裝成一個獨立的Editor組件。這樣做的好處是生命周期可以自動管理組件銷毀時能調(diào)用tinymce.remove()清理實例避免內(nèi)存泄漏多個頁面復(fù)用編輯器時只需要通過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這種封裝模式我已經(jīng)用了很久穩(wěn)定性很好。唯一的坑是tinymce.remove()如果傳了實例對象部分老版本可能識別不了建議直接傳selector字符串tinymce.remove(#editor)。2. 語言包配置詳解從原理到實操語言包配置是很多人第一次用Tinymce時最容易卡住的地方。默認(rèn)情況下Tinymce界面是英文的如果你直接設(shè)置language: zh_CN但沒加載對應(yīng)語言包編輯器會加載失敗或報錯這個坑我見過太多次了。2.1 語言包的加載機(jī)制首先理解Tinymce的語言包機(jī)制Tinymce本身不內(nèi)置任何語言文件它會在初始化時根據(jù)language配置項去請求一個對應(yīng)的語言代碼.js文件。比如設(shè)置language: zh_CN它就會嘗試加載zh_CN.js這個文件。這個文件默認(rèn)從CDN加載官方CDN地址是https://cdn.tiny.cloud/1/{apiKey}/tinymce/langs/zh_CN.js注意這個CDN地址帶了apiKey參數(shù)這是Tinymce 5以后的規(guī)定。如果你沒有申請Tiny Cloud的API Key瀏覽器會返回403錯誤語言包自然加載失敗編輯器初始化也會中斷。解決思路有兩個第一種是申請Tiny Cloud的免費API Key官方以key區(qū)分是否免費賬戶個人項目用免費額度足夠了。但企業(yè)內(nèi)部項目尤其是內(nèi)網(wǎng)部署的場景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語言包文件的本地地址。這個配置項如果沒有設(shè)置Tinymce才回去請求CDN設(shè)置了就用本地文件。如果你使用的是npm包方式還有一種更干凈的做法直接通過import引入語言包文件。在main.js或組件入口處引入import tinymce/langs/zh_CN這樣語言包會被打包進(jìn)最終的構(gòu)建產(chǎn)物不需要手動拷貝文件到靜態(tài)目錄也不會有路徑問題。但要注意這種方式要求語言包文件就在tinymce/langs/下npm包默認(rèn)是不帶語言包的需要自己去官方倉庫下載后放到該目錄或者直接用CDN版本的語言包覆蓋。實際測試下來官方CDN的語言包和本地文件在格式上沒有差異都是向tinymce.addI18n()注冊翻譯字典。如果你有自己的翻譯需求直接改這個js文件里的字典即可不需要額外寫插件。2.3 語言包加載失敗的定位與解決我總結(jié)了三種最常見的語言包加載失敗場景現(xiàn)象原因解決方案控制臺顯示zh_CN.js 404本地目錄沒有該文件或路徑寫錯檢查public下文件路徑確保language_url相對路徑正確控制臺顯示403走了官方CDN但沒有API Key設(shè)置language_url使用本地文件界面顯示中文但部分按鈕還是英文插件語言包未加載檢查插件對應(yīng)語言文件部分第三方插件需要單獨引入語言文件其中第三種情況比較容易忽略。比如table插件的中文翻譯在核心語言包里就有但如果你用了a11ychecker這種高級插件它自己還有一套語言文件。遇到這種情況直接在插件初始化時傳入tinymce實例的language即可不用額外處理。另外一個細(xì)節(jié)如果頁面里有多個編輯器實例每個實例都要單獨配置language嗎答案是可以在全局統(tǒng)一處理。在tinymce.init()之前調(diào)用tinymce.addI18n()手動注冊語言包然后所有實例就都能共享了。3. 工具欄與工具欄組定制全解等你把編輯器和語言包都跑通了接下來做得最多的就是按照業(yè)務(wù)需求調(diào)整工具欄。這塊初看簡單無非就是增刪按鈕但深入進(jìn)去會發(fā)現(xiàn)有很多組合技巧包括按鈕分組、多行排列、下拉菜單嵌套、自定義按鈕等。3.1 工具欄配置基礎(chǔ)按鈕、分組與排列工具欄配置使用toolbar字段它是一個字符串由按鈕名稱button name和分隔符組成空格表示按鈕間隔豎線|表示分組。toolbar: undo redo | bold italic underline | bullist numlist這個配置會產(chǎn)生三個區(qū)域左側(cè)撤銷/重做中間文字加粗/斜體/下劃線右側(cè)有序/無序列表。組與組之間會出現(xiàn)一條豎直分隔線視覺上更清晰也方便用戶按功能區(qū)域?qū)ふ野粹o。如果你需要配置多行工具欄Tinymce 5以后支持在toolbar字符串里用||分隔不同行toolbar: undo redo | styles | bullist numlist | alignleft aligncenter alignright | link image | table | fullscreen || bold italic underline strikethrough | forecolor backcolor | removeformat | code兩行工具欄會自動換行顯示第一行放高頻操作和需要視覺寬度的控件第二行放字符格式類操作。實際布局時要注意如果你的編輯器容器寬度有限多行工具欄會把編輯區(qū)域壓縮得很矮此時需要適當(dāng)增加編輯器的整體高度。工具欄是否換行還和toolbar_mode配置有關(guān)。默認(rèn)是scrolling模式按鈕超出寬度時會出現(xiàn)小箭頭展開更多如果你希望按鈕自動換行而不是收縮改成toolbar_mode: wrap這樣工具欄按鈕會像文本一樣自動換行不會藏起來。3.2 工具欄組的自定義與嵌套除了基礎(chǔ)的單按鈕排列Tinymce還支持上拉菜單和嵌套分組。最常用的場景是把一類功能收進(jìn)一個下拉按鈕里比如“插入”功能下掛圖片、媒體、表格、文件等。實現(xiàn)方式有兩種。一種是直接使用Tinymce預(yù)置的按鈕比如insert就是一個包含多項插入功能的按鈕組toolbar: insert | undo redo | ...另一種是使用setup配置項通過editor.ui.registry.addGroupToolbar創(chuàng)建自定義分組。這個方法在Tinymce 5.2以后可用使用場景相對高級一般項目中用預(yù)置按鈕組就夠用了。如果是更復(fù)雜的嵌套下拉菜單可以使用addMenuItem配合addContextToolbar。例如給選中圖片時單獨彈出一條格式工具欄setup(editor) { editor.ui.registry.addContextToolbar(imageToolbar, { predicate: (node) node.nodeName IMG, items: alignleft aligncenter alignright | image tooltip }) }這個配置的效果是鼠標(biāo)選中圖片時編輯器會對當(dāng)前選區(qū)上下文自動彈出工具欄方便快速調(diào)整圖片位置和樣式。這種體驗很順手很多成熟產(chǎn)品都在用。3.3 常用工具欄配置速查與排列建議下面是一套我整理的中文后臺系統(tǒng)常用的配置模板覆蓋了內(nèi)容編輯、排版、插入對象、協(xié)作代碼等場景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對應(yīng)的插件配置plugins: [ advlist, autolink, lists, link, image, charmap, preview, anchor, searchreplace, visualblocks, code, fullscreen, insertdatetime, media, table, wordcount ]這套配置有幾點考慮blocks按鈕用來切換段落、標(biāo)題1到標(biāo)題6是文章排版最常用的按鈕。forecolor和backcolor分別控制文字顏色和背景高亮做文章標(biāo)注很實用。lineheight是行高插件按鈕需要配合插件才能出現(xiàn)如果你沒有裝對應(yīng)插件寫了也不會生效。fullscreen對后臺內(nèi)容管理特別重要編輯長文章時全屏模式能大幅提升體驗。code按鈕可以讓用戶直接查看和編輯HTML源碼這是很多業(yè)務(wù)場景的硬需求。值得注意的是Tinymce不同版本對同一功能的按鈕命名可能有差異。比如hr插入水平線按鈕Tinymce 5里面要用hr插件Tinymce 6里可能需要額外注意命名。遇到按鈕不顯示時先確認(rèn)插件是否加載再確認(rèn)按鈕名稱是否準(zhǔn)確不要盲目改配置。3.4 加載插件與自定義按鈕的補充玩法如果你不想滿足于系統(tǒng)預(yù)置的按鈕Tinymce也支持自定義工具欄按鈕。方法就是在setup里通過editor.ui.registry.addButton注冊一個新按鈕然后在toolbar字符串里直接使用按鈕idsetup(editor) { editor.ui.registry.addButton(myExportWord, { text: 導(dǎo)出Word, onAction: () { // 在這里調(diào)用導(dǎo)出邏輯 console.log(導(dǎo)出Word) } }) }, toolbar: undo redo | myExportWord這里有一個小建議自定義按鈕的功能邏輯不要直接寫在組件里而是盡量抽成獨立的工具函數(shù)或模塊。因為編輯器可能會在多個頁面復(fù)用寫在組件里會導(dǎo)致功能重復(fù)且難以維護(hù)。關(guān)于熱搜詞里提到的tinymce export to word插件下載我說下我的理解。官方插件里并沒有一個直接的Export to Word按鈕但可以通過引入powerpaste插件配合服務(wù)端轉(zhuǎn)換實現(xiàn)類似效果或者簡單點直接獲取編輯器內(nèi)容后由后端生成Word。我自己的項目里是先獲取HTML內(nèi)容再通過后端服務(wù)轉(zhuǎn)換前端只做一個按鈕觸發(fā)這樣最省事。4. 常見問題與排查技巧實錄最后這部分我整理一下實際使用中遇到的高頻問題和排查思路有些是我自己踩過的坑有些是幫同事排查時積累的按照從簡單到復(fù)雜的順序排列。4.1 初始化失敗與界面顯示異常編輯器完全沒渲染出來。優(yōu)先看控制臺有沒有報錯。如果報錯信息是tinymce is not defined說明腳本沒加載成功檢查靜態(tài)資源路徑。如果控制臺無聲無息但頁面空白多半是selector選錯了元素常見于用className而不是id或者元素在編輯器初始化后才掛載到DOM上。編輯器渲染出來了但沒有樣式。這是皮膚文件沒加載導(dǎo)致的。確認(rèn)skins目錄已經(jīng)拷貝到靜態(tài)目錄并且在init里設(shè)置了skin: oxide。Tinymce 5/6默認(rèn)主題皮膚是oxide暗色主題是oxide-dark如果你自定義了皮膚名一定要確保對應(yīng)目錄存在。編輯器高度異?;騼?nèi)容區(qū)空白。優(yōu)先檢查是不是初始化時容器還沒渲染完畢。特別是在彈窗、抽屜這類動態(tài)渲染的組件里需要等DOM掛載完成后再調(diào)用init或者用setTimeout延后執(zhí)行。4.2 內(nèi)容提交與展示時的常見問題編輯器內(nèi)容提交后前端展示出來樣式不對。這個問題的根源在于Tinymce編輯區(qū)域是iframe隔離的內(nèi)部有一套默認(rèn)樣式但提交到業(yè)務(wù)頁面后這層隔離就沒有了內(nèi)容直接由業(yè)務(wù)頁面的全局樣式渲染。解決方案是在業(yè)務(wù)展示端引入一份和編輯區(qū)域一致的內(nèi)容樣式或者使用content_css配置項指定編輯區(qū)域的樣式文件。我的做法是維護(hù)一個editor-content.css里面定義.ql-editor相關(guān)的樣式然后同時用于編輯器的content_css和業(yè)務(wù)展示頁的樣式引入保證WYSIWYG。提交后HTML里有大量多余標(biāo)簽。Tinymce默認(rèn)會保留很多語義化標(biāo)簽比如p、span等如果業(yè)務(wù)方不想要這些冗余代碼可以在init里配置forced_root_block: p或者在提交時通過getContent({ format: text })只提取純文本。多語言站點中語言包混亂。如果站點本身支持多語言切換需要在切換時動態(tài)重新初始化編輯器。簡單做法是銷毀現(xiàn)有實例再重新init復(fù)雜一點可以用tinymce.activeEditor.setLanguage()動態(tài)切換。4.3 性能與體驗優(yōu)化心得在低配置機(jī)器比如一些老的辦公電腦上Tinymce初始化會有明顯的卡頓感。我的經(jīng)驗是通過plugins精簡插件列表把用不到的插件全部移除尤其是powerpaste、spellchecker這種重量級插件能明顯縮短初始化時間。對于內(nèi)容特別長的文章編輯和提交時都會比較吃力??梢钥紤]配置autosave插件自動保存草稿避免瀏覽器崩潰導(dǎo)致內(nèi)容丟失。再把編輯器的branding: false關(guān)掉頁面底部就不會顯示官方Logo了界面清爽不少。最后分享一個小經(jīng)驗不管怎么定制工具欄都要保證核心編輯功能優(yōu)先。富文本編輯器最重要的是“能寫、能存、不丟內(nèi)容”工具欄花里胡哨是錦上添花千萬別本末倒置。我見過有同事寫了滿滿四大行工具欄結(jié)果整個編輯區(qū)域只剩巴掌大這種配置再炫也沒意義。Tinymce 6相關(guān)文檔比5清楚不少但實際上手時依然會有很多細(xì)枝末節(jié)的問題。這篇整理出來的內(nèi)容都是實打?qū)嵦幚磉^的坑照著配置能省下不少排查時間。如果你在接入過程中有別的問題也歡迎在評論區(qū)一起交流。本文還有配套的精品資源點擊獲取