【基于 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)

<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: '

请输入博客内容...

' }, // 编辑模式(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(//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>

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


<script>
import Vue from 'vue'
import CsdnWangEditor from './CsdnWangEditor.vue'
export default Vue.extend({
  components: { CsdnWangEditor },
  data() {
    return {
      blogTitle: '',
      blogContent: '

请输入博客内容...

', 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.title this.blogContent = draftData.content this.blogCategory = draftData.category this.blogTags = draftData.tags } }, beforeDestroy() { clearTimeout(this.draftTimer) } }) </script>

三、关键适配 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 草稿接口,替换本地存储逻辑
posted on 2026-01-28 09:27  ljbguanli  阅读(3)  评论(0)    收藏  举报