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

【基于 WangEditor v5 + Vue2 封装 CSDN 风格富文本组件】

基于 WangEditor v5 + Vue2 封装 CSDN 风格富文本组件

一、wangEditor封装及使用技术文章大纲

引言
  • 介绍wangEditor的基本信息(轻量级富文本编辑器、开源、功能特点)
  • 封装的目的和意义(提高复用性、统一配置、简化调用)
wangEditor基础集成
  • 通过npm或CDN引入wangEditor
  • 初始化编辑器的基本代码示例
  • 核心配置项说明(如菜单配置、上传图片配置)
封装策略设计
  • 创建独立Vue/React组件封装编辑器
  • 通过props传递配置参数(如工具栏选项、初始内容)
  • 暴露编辑器实例方法(如获取内容、清空内容)
关键功能封装实现
  • 图片/视频上传功能统一处理(对接OSS或后端API)
  • 自定义扩展菜单项的实现方法
  • 内容变化监听与双向数据绑定处理
主题样式定制
  • 覆盖默认样式实现UI主题定制
  • 响应式布局适配方案
  • 暗黑模式等主题切换实现
典型应用场景
  • 表单中的富文本输入场景
  • 与Markdown的双向转换实现
  • 协同编辑场景下的封装注意事项
性能优化建议
  • 懒加载实现方案
  • 大文档编辑时的性能处理
  • 销毁实例避免内存泄漏
常见问题解决方案
  • XSS安全防护配置
  • 粘贴内容格式处理
  • 移动端适配问题修复
测试与部署
  • 单元测试编写要点
  • 构建为独立npm包的方法
  • 版本更新与维护策略
结语
  • 封装带来的收益总结
  • 未来可扩展方向(插件系统、AI集成等)
  • 官方资源与社区支持信息CSDN 风格富文本组件封装(Vue2)
<template><div class="csdn-editor-container"><!-- 编辑器工具栏(CSDN 风格:简洁+核心功能) --><Toolbar:editor="editor":defaultConfig="toolbarConfig":mode="mode"style="border-bottom: 1px solid #e5e7eb; background: #f8f9fa; padding: 4px 8px;"/><!-- 富文本编辑区(CSDN 经典白色背景+代码高亮适配) --><Editorv-model="html":defaultConfig="editorConfig":mode="mode"style="height: 600px; overflow-y: auto; background: #fff; font-size: 15px; line-height: 1.8;"@onCreated="onCreated"@onChange="onChange"@customPaste="customPaste"/></div>
</template><script>
import Vue from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import '@wangeditor/editor/dist/css/style.css'
// 引入代码高亮插件(CSDN 风格高亮)
import hljs from 'highlight.js'
import 'highlight.js/styles/atom-one-dark.css' // CSDN 常用深色代码主题export default Vue.extend({name: 'CsdnWangEditor',components: { Editor, Toolbar },props: {// 双向绑定内容value: {type: String,default: '<p>请输入博客内容...</p>'},// 编辑模式(default:富文本 / simple:精简模式)mode: {type: String,default: 'default'},// 最大字数限制(CSDN 默认无限制,可自定义)maxLength: {type: Number,default: 0}},data() {return {editor: null,html: this.value,// 工具栏配置(复刻 CSDN 核心功能)toolbarConfig: {excludeKeys: ['insertVideo', // 隐藏视频插入(CSDN 需单独配置)'fullScreen', // 可选:保留/隐藏全屏按钮'codeBlock', // 替换为自定义代码块(支持高亮)'fontSize', 'fontFamily' // CSDN 默认不显示字体字号工具栏],// 自定义工具栏顺序(CSDN 风格)order: ['bold', 'italic', 'underline', 'strikeThrough', 'sub', 'sup','|', 'fontColor', 'bgColor', 'clearStyle','|', 'header', 'blockquote', 'code','|', 'list', 'todo', 'align', 'lineHeight','|', 'link', 'image', 'table', 'hr','|', 'undo', 'redo', 'preview', 'print']},// 编辑器配置(适配 CSDN 场景)editorConfig: {placeholder: '在这里写下你的技术博客...',// 图片上传(CSDN 风格:支持拖拽/粘贴/点击上传)MENU_CONF: {uploadImage: {// 最大文件大小(CSDN 限制 5MB)maxFileSize: 5 * 1024 * 1024,// 支持的图片格式accept: ['image/jpg', 'image/jpeg', 'image/png', 'image/gif'],// 自定义上传逻辑(对接 CSDN 图片接口或自己的 OSS)async customUpload(file, insertFn) {// 示例:模拟 CSDN 图片上传(实际需替换为真实接口)const formData = new FormData()formData.append('file', file)// 这里替换为 CSDN 开放接口或自建上传接口// const res = await axios.post('CSDN_UPLOAD_API', formData)// insertFn(res.data.url) // 上传成功后插入图片// 模拟上传成功(测试用)setTimeout(() => {const mockUrl = `https://picsum.photos/800/400?random=${Math.random()}`insertFn(mockUrl)}, 1000)},// 显示上传进度onProgress(progress) {console.log('图片上传进度:', progress)}},// 代码块配置(支持 CSDN 常用语言)codeBlock: {languages: [{ value: 'html', name: 'HTML' },{ value: 'css', name: 'CSS' },{ value: 'javascript', name: 'JavaScript' },{ value: 'vue', name: 'Vue' },{ value: 'react', name: 'React' },{ value: 'java', name: 'Java' },{ value: 'python', name: 'Python' },{ value: 'sql', name: 'SQL' },{ value: 'shell', name: 'Shell' }]}},// 字数统计(CSDN 风格:实时显示)maxLength: this.maxLength,// 粘贴处理(保留 CSDN 格式,过滤无用样式)pasteFilterStyle: true,pasteIgnoreImg: false, // 允许粘贴图片// 自定义粘贴逻辑(如粘贴 Markdown 自动转换)customPaste: (editor, event, callback) => {const text = event.clipboardData.getData('text/plain')// 若粘贴的是 Markdown 文本,可自动转换为 HTML(需引入 markdown-it)// const html = markdownit().render(text)// editor.insertHtml(html)// callback(false) // 阻止默认粘贴callback(true) // 保留默认粘贴行为}}}},watch: {value(newVal) {this.html = newVal // 双向绑定同步}},methods: {onCreated(editor) {this.editor = Object.seal(editor) // 必须用 Object.seal() 避免报错// 初始化代码高亮(CSDN 风格)this.initCodeHighlight(editor)},onChange(editor) {this.html = editor.getHtml()this.$emit('input', this.html) // 同步给父组件this.$emit('change', editor)},// 自定义粘贴处理(适配 CSDN 粘贴逻辑)customPaste(editor, event, callback) {// 过滤 Word 粘贴的冗余样式(CSDN 常用优化)const html = event.clipboardData.getData('text/html')if (html.includes('msword')) {// 清除 Word 样式const cleanHtml = html.replace(/<style[\s\S]*?<\/style>/gi, '').replace(/class="[^"]*"/gi, '').replace(/style="[^"]*"/gi, '')editor.insertHtml(cleanHtml)event.preventDefault()callback(false)return}callback(true)},// 初始化代码高亮(适配 CSDN 代码块风格)initCodeHighlight(editor) {// 监听代码块变化,触发高亮editor.on('codeBlockChange', () => {this.$nextTick(() => {document.querySelectorAll('pre code').forEach(block => {hljs.highlightElement(block)})})})// 初始渲染时高亮this.$nextTick(() => {document.querySelectorAll('pre code').forEach(block => {hljs.highlightElement(block)})})}},beforeDestroy() {// 销毁编辑器(避免内存泄漏)if (this.editor) {this.editor.destroy()this.editor = null}}
})
</script><style scoped>
/* CSDN 风格样式优化 */
.csdn-editor-container {border: 1px solid #e5e7eb;border-radius: 4px;overflow: hidden;box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
/* 代码块样式优化(贴近 CSDN) */
::v-deep pre {background: #282c34 !important;border-radius: 4px !important;padding: 16px !important;margin: 16px 0 !important;overflow-x: auto !important;
}
::v-deep code {font-family: 'Consolas', 'Monaco', 'Menlo', monospace !important;font-size: 14px !important;
}
/* 图片样式(CSDN 居中+边框) */
::v-deep img {max-width: 100% !important;margin: 16px auto !important;display: block !important;border: 1px solid #e5e7eb !important;border-radius: 4px !important;padding: 4px !important;
}
</style>

二、组件使用示例(CSDN 博客编辑页)

<template><div class="csdn-blog-edit"><!-- CSDN 博客编辑头部(模拟) --><div class="edit-header"><inputv-model="blogTitle"placeholder="请输入博客标题(不超过100字)"class="blog-title-input"maxlength="100"><div class="edit-toolbar"><button @click="saveDraft" class="btn draft-btn">保存草稿</button><button @click="publishBlog" class="btn publish-btn">发布博客</button></div></div><!-- 分类/标签(模拟 CSDN 侧边栏) --><div class="edit-sidebar"><div class="sidebar-item"><label>博客分类:</label><select v-model="blogCategory" class="category-select"><option value="frontend">前端开发</option><option value="backend">后端开发</option><option value="mobile">移动开发</option><option value="ai">人工智能</option><option value="other">其他分类</option></select></div><div class="sidebar-item"><label>博客标签:</label><input v-model="blogTags" placeholder="输入标签,用逗号分隔" class="tags-input"></div></div><!-- 核心:CSDN 风格富文本编辑器 --><div class="editor-wrapper"><CsdnWangEditorv-model="blogContent":maxLength="50000"@change="handleContentChange"/></div></div>
</template><script>
import Vue from 'vue'
import CsdnWangEditor from './CsdnWangEditor.vue'export default Vue.extend({components: { CsdnWangEditor },data() {return {blogTitle: '',blogContent: '<p>请输入博客内容...</p>',blogCategory: 'frontend',blogTags: '',draftTimer: null // 自动保存草稿定时器}},methods: {// 保存草稿(CSDN 自动保存逻辑)saveDraft() {if (!this.blogTitle.trim()) {alert('请先输入博客标题')return}// 模拟保存到本地存储或后端localStorage.setItem('csdn_draft', JSON.stringify({title: this.blogTitle,content: this.blogContent,category: this.blogCategory,tags: this.blogTags,saveTime: new Date().toLocaleString()}))alert('草稿保存成功!')},// 发布博客(对接 CSDN 发布接口)publishBlog() {if (!this.blogTitle.trim() || !this.blogContent.trim()) {alert('标题和内容不能为空')return}// 模拟发布逻辑const blogData = {title: this.blogTitle,content: this.blogContent,category: this.blogCategory,tags: this.blogTags.split(',').map(tag => tag.trim()),publishTime: new Date().toLocaleString()}console.log('发布博客数据:', blogData)alert('博客发布成功!')// 实际项目中对接 CSDN 开放接口或自己的后端// axios.post('CSDN_PUBLISH_API', blogData)},// 内容变化时触发(自动保存草稿)handleContentChange() {// 10秒自动保存一次草稿(CSDN 逻辑)clearTimeout(this.draftTimer)this.draftTimer = setTimeout(() => {this.saveDraft()}, 10000)}},mounted() {// 加载本地草稿(CSDN 自动恢复草稿)const draft = localStorage.getItem('csdn_draft')if (draft) {const draftData = JSON.parse(draft)this.blogTitle = draftData.titlethis.blogContent = draftData.contentthis.blogCategory = draftData.categorythis.blogTags = draftData.tags}},beforeDestroy() {clearTimeout(this.draftTimer)}
})
</script><style scoped>
.csdn-blog-edit {max-width: 1400px;margin: 0 auto;padding: 20px;display: flex;flex-direction: column;gap: 20px;
}
.edit-header {display: flex;justify-content: space-between;align-items: center;gap: 20px;
}
.blog-title-input {flex: 1;padding: 12px 16px;font-size: 20px;border: 1px solid #e5e7eb;border-radius: 4px;outline: none;
}
.blog-title-input:focus {border-color: #1890ff;box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
.btn {padding: 8px 16px;border-radius: 4px;cursor: pointer;font-size: 14px;border: none;
}
.draft-btn {background: #f8f9fa;color: #333;border: 1px solid #e5e7eb;
}
.publish-btn {background: #1890ff;color: #fff;margin-left: 8px;
}
.edit-sidebar {width: 260px;align-self: flex-start;border: 1px solid #e5e7eb;border-radius: 4px;padding: 16px;
}
.sidebar-item {margin-bottom: 16px;
}
.sidebar-item label {display: block;margin-bottom: 8px;font-weight: 500;font-size: 14px;
}
.category-select, .tags-input {width: 100%;padding: 8px 12px;border: 1px solid #e5e7eb;border-radius: 4px;font-size: 14px;
}
.editor-wrapper {flex: 1;margin-left: 280px;margin-top: -240px;
}
</style>

三、关键适配 CSDN 特性说明

  1. 功能适配

    • 核心工具栏:复刻 CSDN 常用功能(代码块、图片上传、表格、公式等)
    • 代码高亮:使用 highlight.js 实现 CSDN 经典深色代码主题
    • 图片上传:支持拖拽/粘贴/点击上传,适配 CSDN 5MB 大小限制
    • 自动保存:10秒自动保存草稿,支持本地存储恢复
  2. 样式适配

    • 编辑器背景:白色背景+15px 字体,贴近 CSDN 阅读体验
    • 代码块:深色背景+ monospace 字体,优化代码可读性
    • 图片样式:居中显示+边框+内边距,符合 CSDN 图片展示规范
    • 整体布局:模拟 CSDN 编辑页(标题栏+侧边分类+编辑区)
  3. 交互适配

    • 粘贴优化:过滤 Word 冗余样式,支持 Markdown 粘贴转换
    • 字数限制:默认 5 万字,可自定义调整
    • 草稿恢复:加载本地存储的草稿内容,避免内容丢失

四、使用前准备

  1. 安装依赖
# 核心依赖
npm install @wangeditor/editor @wangeditor/editor-for-vue --save
# 代码高亮依赖
npm install highlight.js --save
# 可选:Markdown 粘贴转换依赖
npm install markdown-it --save
  1. 接口替换
    • 图片上传:将 customUpload 中的模拟接口替换为 CSDN 开放接口或自建 OSS 接口
    • 博客发布:将 publishBlog 中的模拟逻辑替换为 CSDN 发布接口
    • 草稿保存:可对接 CSDN 草稿接口,替换本地存储逻辑
http://www.dtcms.com/a/580910.html

相关文章:

  • 网站建设的重要性意义徐州建站公司模板
  • Scrapy源码剖析:下载器中间件是如何工作的?
  • vi 编辑器命令大全
  • AI 预测 + 物联网融合:档案馆温湿度监控系统发展新趋势
  • Vue JSON结构编辑器组件设计与实现解析
  • 14_FastMCP 2.x 中文文档之FastMCP高级功能:MCP中间件详解
  • 软考中级软件设计师(下午题)--- UML建模
  • 机械臂时间最优规划
  • 【LeetCode刷题】两数之和
  • 10 月热搜精选
  • 郑州商城网站开发摄影网站源码 国外
  • Docker 加载镜像时报 no space left on device 的彻底解决方案
  • 5、prometheus标签
  • python+django/flask基于机器学习的就业岗位推荐系统
  • Mysql作业5
  • 为什么Vue 3需要ref函数?它的响应式原理与正确用法是什么?
  • STM32外设学习--TIM定时器--输入捕获---测频方法(代码编写)
  • 如何设置JVM参数避开直接内存溢出的坑?
  • (七)嵌入式面试题收集:8道
  • AI搜索营销破局:光引GEO多平台适配与实时优化引擎开发详解
  • 【有源码】基于Hadoop+Spark的起点小说网大数据可视化分析系统-基于Python大数据生态的网络文学数据挖掘与可视化系统
  • Windows10 wsl2 ubuntu22.04 docker安装
  • 使用docker-compose部署应用保姆级教程
  • 【Linux工具链】从跨平台适配到一键部署:yum多架构支持+Vim远程编辑+gcc交叉编译,解决多场景开发效率瓶颈
  • 简单做网站企业宣传视频制作免费模板
  • 西安SEO网站建设哪家好食品网站的网页设计
  • 网站开发公司哪家好嘉兴市建设工程监理协会网站
  • 天津做一个简单的网站首页wap门户网站源码
  • 热门软件排行榜泰州网站关键词优化
  • 怎么查询网站开发公司个人网站数据库大小