当前位置: 首页 > news >正文

Vue3+TinyMCE6实战:手把手教你开发带目录导航的富文本编辑器(附完整代码)

Vue3+TinyMCE6实战:构建智能目录导航的富文本编辑器

在当今内容驱动的应用开发中,富文本编辑器已成为不可或缺的核心组件。而TinyMCE作为业界领先的WYSIWYG编辑器,其6.x版本带来了更现代化的架构和更强大的扩展能力。本文将带您从零开始,在Vue3环境中构建一个具备智能目录导航功能的企业级编辑器解决方案。

1. 环境搭建与基础配置

首先确保您的开发环境已准备好以下基础条件:

  • Node.js 16+ 和 npm/yarn
  • Vue3项目(可通过vue create或Vite快速创建)
  • TinyMCE 6.x官方包(社区版或商业版)

安装核心依赖:

npm install tinymce @tinymce/tinymce-vue

public目录下创建tinymce文件夹,放置以下资源:

public/ tinymce/ skins/ # 皮肤文件 icons/ # 图标集 plugins/ # 自定义插件目录 langs/ # 语言包(如需中文)

基础配置示例(src/utils/tinymce-config.js):

export const baseConfig = { language: 'zh_CN', skin: 'oxide', content_css: 'default', menubar: 'file edit view insert format tools table help', toolbar: 'undo redo | styleselect | bold italic | alignleft aligncenter alignright | bullist numlist outdent indent | toc', plugins: 'lists link image table code help wordcount', height: 800, branding: false }

提示:TinyMCE6对插件系统进行了重构,部分5.x插件需要适配新架构。遇到兼容性问题时可检查控制台错误并参考官方迁移指南。

2. 目录导航插件开发

2.1 插件架构设计

目录导航插件的核心功能应包括:

  • 实时解析文档中的标题结构(h1-h6)
  • 生成带锚点的可交互目录树
  • 支持平滑滚动定位
  • 提供目录更新机制

创建插件文件public/tinymce/plugins/toc/plugin.js

(function() { tinymce.PluginManager.add('toc', function(editor) { // 生成唯一ID的工具函数 const createIdGenerator = (prefix) => { let counter = 0 return () => { const guid = Date.now().toString(32) return `${prefix}${guid}${(counter++).toString(32)}` } } // 核心功能实现... }) })()

2.2 标题解析与目录生成

实现标题解析的核心逻辑:

const parseHeaders = () => { const tocClass = 'mce-toc' const headers = Array.from( editor.getBody().querySelectorAll('h1, h2, h3, h4, h5, h6') ).filter(el => !el.classList.contains(tocClass)) return headers.map(header => { if (!header.id) { header.id = createIdGenerator('hdr_')() } return { id: header.id, level: parseInt(header.tagName.substring(1)), text: header.innerText, element: header } }) }

目录生成算法采用多级嵌套列表结构:

const generateTocHTML = (headers) => { if (!headers.length) return '' let html = '<div class="mce-toc" contenteditable="false">' html += '<h2 contenteditable="true">目录</h2><ul>' let prevLevel = headers[0].level headers.forEach((h, i) => { const nextLevel = headers[i+1]?.level || null if (h.level > prevLevel) { html += '<ul>' } else if (h.level < prevLevel) { html += '</li></ul>'.repeat(prevLevel - h.level) } else if (i > 0) { html += '</li>' } html += `<li><a href="#${h.id}">${h.text}</a>` prevLevel = h.level }) return html + '</ul></div>' }

2.3 交互功能实现

添加平滑滚动和点击事件处理:

const setupScrollBehavior = () => { editor.on('click', e => { const toc = editor.getBody().querySelector('.mce-toc') if (!toc || !toc.contains(e.target)) return if (e.target.tagName === 'A') { e.preventDefault() const id = e.target.getAttribute('href').substring(1) const target = editor.getBody().querySelector(`#${id}`) target?.scrollIntoView({ behavior: 'smooth' }) } }) }

3. Vue3组件集成

3.1 封装TinyMCE组件

创建可复用的编辑器组件src/components/RichEditor.vue

<template> <div class="editor-container"> <Editor v-model="content" :init="editorConfig" :key="editorKey" /> </div> </template> <script setup> import { ref, computed } from 'vue' import Editor from '@tinymce/tinymce-vue' const props = defineProps({ modelValue: String, config: Object }) const emit = defineEmits(['update:modelValue']) const content = computed({ get: () => props.modelValue, set: (val) => emit('update:modelValue', val) }) const editorKey = ref(0) const editorConfig = ref({ ...props.config, setup: (editor) => { editor.on('init', () => { console.log('Editor initialized') }) } }) </script>

3.2 实现双向数据绑定

在父组件中使用:

<template> <div class="app"> <RichEditor v-model="content" :config="editorConfig" /> </div> </template> <script setup> import { ref } from 'vue' import RichEditor from './components/RichEditor.vue' import { baseConfig } from './utils/tinymce-config' const content = ref('<h1>文档标题</h1><p>这里是正文内容...</p>') const editorConfig = ref({ ...baseConfig, plugins: [...baseConfig.plugins, 'toc'], toolbar: `${baseConfig.toolbar} | tocupdate` }) </script>

4. 高级功能扩展

4.1 实时目录更新

通过MutationObserver实现内容变化时的自动更新:

const setupAutoUpdate = () => { const observer = new MutationObserver(mutations => { if (mutations.some(m => Array.from(m.addedNodes).some(n => n.nodeType === 1 && /^H[1-6]$/i.test(n.tagName) ) )) { editor.execCommand('mceUpdateToc') } }) editor.on('init', () => { observer.observe(editor.getBody(), { childList: true, subtree: true }) }) editor.on('remove', () => observer.disconnect()) }

4.2 样式定制与主题适配

添加CSS样式确保目录与编辑器风格统一:

.mce-toc { border-left: 3px solid #eee; padding: 0.5rem 1rem; margin: 1rem 0; background: #f9f9f9; } .mce-toc h2 { font-size: 1.2em; margin-top: 0; } .mce-toc ul { list-style: none; padding-left: 1em; } .mce-toc li { margin: 0.3em 0; } .mce-toc a { color: #06c; text-decoration: none; } .mce-toc a:hover { text-decoration: underline; }

4.3 性能优化策略

对于大型文档的优化处理:

const throttledUpdate = _.throttle(() => { editor.execCommand('mceUpdateToc') }, 500, { leading: false }) editor.on('keyup', throttledUpdate) editor.on('change', throttledUpdate)

5. 企业级解决方案实践

在实际项目中,我们还需要考虑以下增强功能:

  • 多实例管理:当页面中存在多个编辑器实例时,确保插件状态隔离
  • 撤销/重做支持:将目录操作纳入编辑器的撤销栈
  • 本地化扩展:支持多语言目录标题
  • 可访问性:为目录添加ARIA属性

完整插件实现可封装为独立npm包,便于团队共享:

{ "name": "tinymce-toc-plugin", "version": "1.0.0", "main": "dist/plugin.js", "files": ["dist"], "peerDependencies": { "tinymce": "^6.0.0" } }

在多个Vue3项目中使用时,可以通过动态导入实现按需加载:

const loadTocPlugin = async () => { await import('tinymce-toc-plugin') tinymce.init({ plugins: ['toc'], // 其他配置... }) }
http://www.jsqmd.com/news/488181/

相关文章:

  • Qwen3-0.6B-FP8在中小企业落地:2GB显存支撑多实例并发问答
  • JavaScript输出技巧大揭秘
  • 汇川AM系列PLC与CODESYS平台:手轮与EtherCAT轴协同调试实战指南
  • 3步颠覆手柄限制:shadPS4键鼠映射的精准操控革命
  • GLM-4-9B-Chat-1M入门必看:本地化大模型环境配置详解
  • CLIP-GmP-ViT-L-14效果展示:跨模态检索的惊艳案例与性能评测
  • Ollama部署Granite-4.0-H-350m:5分钟搞定轻量级AI助手,新手零基础教程
  • 一键切换IP、MAC和主机名技巧
  • [C#] 解决jsencrypt RSA加密后C#解密长度异常问题
  • Yann LeCun 说 LLM 要过时?我用开源框架在 7 天复现「世界模型」雏形
  • StructBERT文本相似度WebUI实测:5分钟搭建,智能客服问答匹配实战
  • awesome-quincy-larson-emails深度剖析:邮件内容自动化处理的实现之道
  • oepnclaw安装遇到问题1:Health check failed(抄个作业) - 玲婉!-_
  • MedGemma X-Ray一键部署方案:3条命令完成从镜像拉取到服务上线
  • 实时手机检测-通用开源镜像:Apache License 2.0商用合规性使用说明
  • Filebeat+Kafka+ELK日志采集实战指南
  • Mosquitto持久引擎深度解析
  • Qwen-Image-2512-ComfyUI快速部署教程:一键启动脚本,内置工作流直接调用
  • SecGPT-14B部署案例:基于CSDN内置模型的GPU算力高效利用方案
  • Ollama平台宝藏模型:Phi-3-mini-4k-instruct零代码体验报告
  • Realistic Vision V5.1在独立设计师工作流中的整合:PS联动+批量导出实践
  • FluidNC:ESP32平台的下一代CNC运动控制固件
  • Reactive-Resume:开源简历工具如何提升90%制作效率
  • Java + RAG + LLM 实战:从零构建高可用智能客服系统
  • 颠覆传统重采样:Farrow滤波器如何实现-79dB超低失真音频转换
  • Anything to RealCharacters 2.5D转真人引擎实操手册:RGB格式自动转换与兼容性处理
  • Qwen3-4B模型实战:基于GitHub开源项目的代码理解与贡献指南生成
  • MogFace-large模型一键部署:基于Dify平台构建人脸检测AI应用
  • 基于深度学习的火焰检测系统(YOLOv12/v11/v8/v5模型)(源码+lw+部署文档+讲解等)
  • Edge浏览器竟是罪魁祸首?VS2017登录失败的隐藏原因与修复教程