什么是 TinyMCE?
TinyMCE 是一个功能强大的所见即所得(WYSIWYG)富文本编辑器,广泛应用于 Web 应用程序中。它提供了丰富的文本编辑功能,包括格式化、插入媒体、表格操作等,让用户能够轻松创建和编辑富文本内容。
核心特性
- 轻量级:压缩后仅约 150KB,加载速度快
- 高度可定制:支持插件系统和主题定制
- 跨浏览器兼容:支持所有主流浏览器
- 移动端友好:响应式设计,支持触摸操作
- 丰富的 API:提供完整的 JavaScript API
- 国际化支持:支持 40+ 种语言
安装方式
方式一:CDN 引入
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.tiny.cloud/1/YOUR_API_KEY/tinymce/6/tinymce.min.js" referrerpolicy="origin"></script>
</head>
<body>
<textarea id="mytextarea">Hello, World!</textarea>
<script>
tinymce.init({
selector: '#mytextarea'
});
</script>
</body>
</html>方式二:npm 安装
npm install tinymceimport tinymce from 'tinymce/tinymce';
import 'tinymce/themes/silver';
import 'tinymce/plugins/paste';
import 'tinymce/plugins/link';
tinymce.init({
selector: '#mytextarea',
plugins: 'paste link',
toolbar: 'undo redo | bold italic | link'
});方式三:下载本地文件
从 TinyMCE 官网 下载完整包,解压后引入:
<script src="path/to/tinymce/tinymce.min.js"></script>基础配置
最简配置
tinymce.init({
selector: '#mytextarea', // 目标元素选择器
height: 300, // 编辑器高度
menubar: false, // 隐藏菜单栏
plugins: [
'advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table paste code help wordcount'
],
toolbar: 'undo redo | formatselect | bold italic backcolor | \
alignleft aligncenter alignright alignjustify | \
bullist numlist outdent indent | removeformat | help'
});常用配置选项
tinymce.init({
selector: '#editor',
// 基础设置
height: 400,
width: '100%',
language: 'zh_CN', // 中文界面
// 工具栏配置
toolbar_mode: 'sliding', // 工具栏模式:floating, sliding, scrolling, wrap
toolbar: [
'undo redo | formatselect fontselect fontsizeselect',
'bold italic underline strikethrough | forecolor backcolor',
'alignleft aligncenter alignright alignjustify',
'bullist numlist | outdent indent | blockquote',
'link image media table | code preview fullscreen'
],
// 插件配置
plugins: [
'advlist', 'autolink', 'lists', 'link', 'image', 'charmap',
'preview', 'anchor', 'searchreplace', 'visualblocks',
'code', 'fullscreen', 'insertdatetime', 'media', 'table',
'paste', 'code', 'help', 'wordcount', 'autosave'
],
// 内容样式
content_style: `
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
font-size: 14px;
line-height: 1.6;
}
`,
// 图片上传配置
images_upload_url: '/upload',
images_upload_handler: function (blobInfo, success, failure) {
// 自定义图片上传逻辑
const formData = new FormData();
formData.append('file', blobInfo.blob(), blobInfo.filename());
fetch('/api/upload', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(result => {
success(result.location);
})
.catch(error => {
failure('上传失败: ' + error.message);
});
},
// 自动保存
autosave_interval: '30s',
autosave_prefix: 'tinymce-autosave-{path}{query}-{id}-',
// 其他配置
branding: false, // 隐藏 "Powered by TinyMCE"
resize: true, // 允许调整大小
statusbar: true, // 显示状态栏
elementpath: false, // 隐藏元素路径
// 内容过滤
paste_data_images: true, // 允许粘贴图片
paste_as_text: false, // 不强制纯文本粘贴
// 快捷键配置
setup: function(editor) {
editor.addShortcut('ctrl+s', '保存', function() {
console.log('保存内容:', editor.getContent());
});
}
});框架集成
React 集成
安装 React 组件:
npm install @tinymce/tinymce-react使用示例:
import React, { useRef } from 'react';
import { Editor } from '@tinymce/tinymce-react';
function MyEditor() {
const editorRef = useRef(null);
const handleSave = () => {
if (editorRef.current) {
console.log(editorRef.current.getContent());
}
};
return (
<>
<Editor
apiKey='your-api-key'
onInit={(evt, editor) => editorRef.current = editor}
initialValue="<p>初始内容</p>"
init={{
height: 500,
menubar: false,
plugins: [
'advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table paste code help wordcount'
],
toolbar: 'undo redo | formatselect | ' +
'bold italic backcolor | alignleft aligncenter ' +
'alignright alignjustify | bullist numlist outdent indent | ' +
'removeformat | help',
content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }'
}}
/>
<button onClick={handleSave}>保存内容</button>
</>
);
}
export default MyEditor;Vue 集成
安装 Vue 组件:
npm install @tinymce/tinymce-vue使用示例:
<template>
<div>
<Editor
api-key="your-api-key"
v-model="content"
:init="editorConfig"
@init="onEditorInit"
/>
<button @click="saveContent">保存</button>
</div>
</template>
<script>
import Editor from '@tinymce/tinymce-vue';
export default {
components: {
Editor
},
data() {
return {
content: '<p>初始内容</p>',
editorConfig: {
height: 500,
menubar: false,
plugins: [
'advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table paste code help wordcount'
],
toolbar: 'undo redo | formatselect | bold italic backcolor | ' +
'alignleft aligncenter alignright alignjustify | ' +
'bullist numlist outdent indent | removeformat | help',
language: 'zh_CN'
}
};
},
methods: {
onEditorInit(editor) {
this.editor = editor;
},
saveContent() {
console.log('保存内容:', this.content);
}
}
};
</script>Angular 集成
安装 Angular 组件:
npm install @tinymce/tinymce-angular在 app.module.ts 中导入:
import { EditorModule } from '@tinymce/tinymce-angular';
@NgModule({
imports: [
EditorModule
]
})
export class AppModule { }组件使用:
import { Component } from '@angular/core';
@Component({
selector: 'app-editor',
template: `
<editor
apiKey="your-api-key"
[init]="editorConfig"
[(ngModel)]="content"
(onInit)="onEditorInit($event)"
></editor>
<button (click)="saveContent()">保存</button>
`
})
export class EditorComponent {
content = '<p>初始内容</p>';
editorConfig = {
height: 500,
menubar: false,
plugins: [
'advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table paste code help wordcount'
],
toolbar: 'undo redo | formatselect | bold italic backcolor | ' +
'alignleft aligncenter alignright alignjustify | ' +
'bullist numlist outdent indent | removeformat | help'
};
onEditorInit(event: any) {
console.log('编辑器初始化完成');
}
saveContent() {
console.log('保存内容:', this.content);
}
}高级配置
自定义插件开发
// 创建自定义插件
tinymce.PluginManager.add('custombutton', function(editor, url) {
editor.ui.registry.addButton('custombutton', {
text: '自定义按钮',
onAction: function() {
editor.insertContent('<p>插入自定义内容</p>');
}
});
});
// 使用自定义插件
tinymce.init({
selector: '#editor',
plugins: 'custombutton',
toolbar: 'custombutton'
});内容验证和过滤
tinymce.init({
selector: '#editor',
// 有效元素配置
valid_elements: 'p,br,strong,em,ul,ol,li,a[href],img[src|alt|width|height]',
// 扩展有效元素
extended_valid_elements: 'iframe[src|width|height|frameborder|allowfullscreen]',
// 内容过滤器
setup: function(editor) {
editor.on('BeforeSetContent', function(e) {
// 内容设置前的 处理
e.content = e.content.replace(/\[custom\]/g, '<span class="custom">自定义标签</span>');
});
editor.on('GetContent', function(e) {
// 获取内容时的处理
e.content = e.content.replace(/<span class="custom">自定义标签<\/span>/g, '[custom]');
});
}
});主题定制
tinymce.init({
selector: '#editor',
// 皮肤配置
skin: 'oxide-dark', // 暗色主题
// 自定义 CSS
content_css: [
'//fonts.googleapis.com/css?family=Lato:300,300i,400,400i',
'/css/custom-editor-styles.css'
],
// 内容样式
content_style: `
body {
font-family: 'Lato', sans-serif;
font-size: 16px;
line-height: 1.6;
color: #333;
background-color: #fff;
margin: 20px;
}
h1, h2, h3, h4, h5, h6 {
color: #2c3e50;
margin-top: 1.5em;
margin-bottom: 0.5em;
}
blockquote {
border-left: 4px solid #3498db;
padding-left: 20px;
margin: 20px 0;
font-style: italic;
background-color: #f8f9fa;
}
`
});常见问题与解决方案
问题1:编辑器无法加载
原因:API Key 无效或网络问题
解决方案:
// 检查 API Key 是否正确
tinymce.init({
selector: '#editor',
apiKey: 'your-valid-api-key', // 确保 API Key 有效
// 添加错误处理
init_instance_callback: function(editor) {
console.log('编辑器初始化成功:', editor.id);
},
setup: function(editor) {
editor.on('LoadError', function(e) {
console.error('编辑器加载失败:', e);
});
}
});问题2:中文输入法兼容性
解决方案:
tinymce.init({
selector: '#editor',
language: 'zh_CN',
// 中文输入法优化
setup: function(editor) {
let isComposing = false;
editor.on('compositionstart', function() {
isComposing = true;
});
editor.on('compositionend', function() {
isComposing = false;
});
editor.on('input', function(e) {
if (!isComposing) {
// 处理非输入法输入
}
});
}
});问题3:图片上传失败
解决方案:
tinymce.init({
selector: '#editor',
images_upload_handler: function(blobInfo, success, failure, progress) {
const formData = new FormData();
formData.append('file', blobInfo.blob(), blobInfo.filename());
const xhr = new XMLHttpRequest();
xhr.upload.onprogress = function(e) {
if (e.lengthComputable) {
progress(e.loaded / e.total * 100);
}
};
xhr.onload = function() {
if (xhr.status === 200) {
const response = JSON.parse(xhr.responseText);
success(response.location);
} else {
failure('上传失败: HTTP ' + xhr.status);
}
};
xhr.onerror = function() {
failure('网络错误,上传失败');
};
xhr.open('POST', '/api/upload');
xhr.send(formData);
},
// 图片上传限制
images_file_types: 'jpg,jpeg,png,gif,webp',
images_upload_credentials: true
});性能优化建议
1. 按需加载插件
// 只加载必要的插件
tinymce.init({
selector: '#editor',
plugins: 'lists link image table code', // 仅加载需要的插件
toolbar: 'undo redo | bold italic | bullist numlist | link image'
});2. 延迟初始化
// 在需要时才初始化编辑器
function initEditor() {
if (!window.editorInitialized) {
tinymce.init({
selector: '#editor',
// 配置选项...
});
window.editorInitialized = true;
}
}
// 用户点击编辑按钮时初始化
document.getElementById('editButton').addEventListener('click', initEditor);3. 内容缓存
tinymce.init({
selector: '#editor',
setup: function(editor) {
let contentCache = '';
// 定期缓存内容
setInterval(function() {
const currentContent = editor.getContent();
if (currentContent !== contentCache) {
localStorage.setItem('editor_cache', currentContent);
contentCache = currentContent;
}
}, 5000);
// 初始化时恢复缓存
editor.on('init', function() {
const cachedContent = localStorage.getItem('editor_cache');
if (cachedContent) {
editor.setContent(cachedContent);
}
});
}
});最佳实践
1. 安全性考虑
tinymce.init({
selector: '#editor',
// 内容安全策略
valid_elements: 'p,br,strong,em,ul,ol,li,a[href],img[src|alt]',
// 禁用危险功能
plugins: 'lists link image table', // 不包含 code 插件
// URL 验证
urlconverter_callback: function(url, node, on_save, name) {
// 验证 URL 安全性
if (url.startsWith('javascript:') || url.startsWith('data:')) {
return ''; // 移除危险 URL
}
return url;
}
});2. 响应式设计
tinymce.init({
selector: '#editor',
// 响应式工具栏
toolbar_mode: 'sliding',
// 移动端优化
mobile: {
theme: 'mobile',
plugins: 'lists link image',
toolbar: 'undo redo | bold italic | bullist numlist | link image'
},
// 自适应高度
height: 'auto',
min_height: 300,
max_height: 800,
// 窗口大小变化时调整
setup: function(editor) {
window.addEventListener('resize', function() {
editor.getContainer().style.width = '100%';
});
}
});3. 国际化配置
// 下载语言包
// https://www.tiny.cloud/get-tiny/language-packages/
tinymce.init({
selector: '#editor',
language: 'zh_CN',
language_url: '/js/langs/zh_CN.js',
// 自定义翻译
setup: function(editor) {
editor.on('init', function() {
// 自定义按钮文本
tinymce.util.I18n.add('zh_CN', {
'Custom Button': '自定义 按钮',
'Insert Custom Content': '插入自定义内容'
});
});
}
});总结
TinyMCE 是一个功能强大且灵活的富文本编辑器,通过合理的配置和优化,可以为用户提供优秀的编辑体验。在实际项目中,建议:
- 根据需求选择合适的安装方式:CDN 适合快速原型,npm 适合生产环境
- 按需配置插件和工具栏:避免加载不必要的功能
- 重视安全性:合理配置内容过滤和验证
- 优化性能:使用延迟加载和内容缓存
- 考虑移动端体验:配置响应式设计
通过本指南的学习,你应该能够成功集成 TinyMCE 并根据项目需求进行定制配置。如果你正在寻找一个强大的代码编辑和开发环境,推荐尝试
(此内容由 AI 辅助生成,仅供参考)