最近在一个帝国CMS 7.5项目里,被一个老熟脸问题折腾得够呛:运营同事在Word里排好一篇图文,直接Ctrl+C/Ctrl+V贴到后台编辑器,提交之后前台页面瞬间变成施工工地。字号忽大忽小,表格横七竖八,有的段落前面空出一大截,源码区里横躺着各种xmlns、mso样式、span标签。说严重点,这种脏数据不止影响颜值,还拖慢页面加载、干扰SEO关键词密度。帝国CMS 7.5的前端开发里,Word粘贴优化一直是个绕不开的话题,这篇文章我就把自己在一线处理这类问题用过的技巧、踩过的坑、以及最终落地的一套方案完整捋一遍。
坦白说,市面上没有哪个编辑器开箱就能把Word内容干干净净地接住,Word的文档格式注定了复制粘贴必然是“带毒”的。但好消息是,我们可以用一层层防御把脏数据拦在进入数据库之前、渲染到页面之前。我这里的做法主要分四步:编辑器参数调整、前端Paste事件清洗、图片转存处理、模板层样式隔离,最后再用后台脚本给历史存量内容兜底。接下来每个环节挨个说。
1. Word粘贴为什么把页面变成“工地”——先搞懂脏代码从哪来
1.1 Word文档根本不是“所见即所得”的纯文本
很多人以为Word里看到的样子就是HTML里的样子,这个直觉在粘贴那一刻就会被现实击碎。从Word 2007开始,docx文档本身就是一个zip压缩包,里面装的是大量XML文件,正文、样式、主题、页眉页脚全都拆成了不同的命名空间。你复制一段文字时,Word会告诉系统“这段开头加粗、标题用Heading1、字体是宋体五号、行距固定值20磅、段前段后各6磅”,这些信息会被翻译成一段极其臃肿的富文本HTML传给浏览器。
浏览器接收后,无法理解Word私有格式,只能尽量把它转成带有内联style的HTML。结果就是:一段明明只有几十个字的正文,能产生几百行标签,全是这种画风:
<p class="MsoNormal" style="margin: 0cm; text-align: left; line-height: 24px; font-size: 10.5pt; font-family: '宋体';"> <span style="font-size: 14px; color: #333333;"> <strong>标题文字</strong> </span> </p>单看这一段还能忍,问题是整篇都这样,重叠的style一个套一个,最后页面渲染时优先级互相打架,审查元素一看全是垃圾。帝国CMS 7.5自带的编辑器和许多国产编辑器对Word粘贴默认走的是“尽量保留格式”的路线,相当于把Word的私人银行账单原封不动搬进了你的页面。
1.2 不同浏览器和粘贴方式,“脏度”完全不一样
我在不同环境下做粘贴测试时发现,脏数据的严重程度并不一致。老版本IE内核浏览器粘贴时,很可能带出完整的VML标签,比如<v:imagedata>、<o:OLEObject>、xmlns:o这类东西,直接从Word复制过来的图片,在很多老浏览器里就是靠这些对象标签引用的。现代Chrome和Firefox会好一些,但仍然会把<!--[if gte mso 9]>这种Word条件注释、mso-前缀的内联样式保留下来。
另外,粘贴路径也很关键:
| 粘贴方式 | 常见结果 |
|---|---|
| Word直接复制到编辑器 | 最脏,完整继承样式、XML命名空间、条件注释 |
| Word复制到记事本再复制到编辑器 | 相对干净,纯文本无样式 |
| 网页复制到Word再复制回来 | 半脏,残留网页样式和Word转换样式 |
| 从浏览器直接复制网页正文 | 偏干净,主要保留基础标签 |
这个差异决定了我们的优化策略不能只靠一个简单的“去格式”按钮,得从源头分层拦截。因为一旦脏数据进了帝国CMS的数据库,后续前台再怎么套样式都很难完全压住。
2. 编辑器参数先省一半事:我调过的配置和实测效果
2.1 帝国CMS 7.5后台编辑器相关参数调整
帝国CMS 7.5后台可以挂多种编辑器,常见的是官方自带编辑器、UEditor或者自己集成的CKEditor。多数前端开发者接手项目时,编辑器的配置还是默认状态,Word粘贴相关的参数根本没动过。如果你用的是UEditor,重点调这几个参数:
// ueditor.config.js 中相关的核心配置 window.UEDITOR_CONFIG = { // 纯文本粘贴模式,去样式力度最大,但不适合需要保留排版的场景 pasteplain: false, // 只保留粘贴内容的标签,过滤掉style/class等属性 retainOnlyLabelPasted: true, // 自定义过滤规则,可以针对 Word 的 mso 垃圾标签做处理 filterTxtRules: { "script": "0", "style": "0", "iframe": "0", "font": "0", "span": "0", "b": "1", "strong": "1", "p": "1", "br": "1", "a": "1" }, // 是否自动转存远程图片 catchRemoteImageEnabled: true };参数说明我用一个表格总结一下,方便对照:
| 参数名 | 作用 | 适用场景 |
|---|---|---|
pasteplain | 开启后粘贴内容强制变成纯文本 | 适合内部公告、不需要排版的栏目 |
retainOnlyLabelPasted | 粘贴时只保留白名单标签,删除多余属性 | 适合需要保留基本段落、加粗、链接的普通文章 |
filterTxtRules | 自定义过滤哪些标签彻底丢弃,哪些保留 | 适合针对Word粘贴做精细化清洗 |
catchRemoteImageEnabled | 远程图片自动抓取到本地服务器 | 适合正文引用外链图片较多的站点 |
需要提醒的是,retainOnlyLabelPasted开了之后,表格的宽度、单元格合并等属性可能会丢失,Word里精心做的表格到了网页上容易“散架”。所以我一般不会无脑全开,而是配合后面的前端Paste清洗脚本一起用。
2.2 如果用其它编辑器,插件怎么选
如果你项目里用的是CKEditor,官方有pastefromword插件,专门处理从Word粘贴的HTML,默认会剥离命名空间和Mso样式。TinyMCE则自带paste插件,里面有两个选项:paste_remove_styles和paste_remove_scripts。这些插件的核心原理跟我后面写的手动清洗脚本大同小异,只是封装好了而已。注意版本兼容问题,老版本编辑器配上新插件可能直接报错,尤其是帝国CMS 7.5这种老系统里经常遇到的“编辑器按钮加载不出来”的问题,很多就是插件版本不匹配引起的。
这里有个容易被忽略的点:无论用哪个编辑器,配置改完之后一定要在真实Word文件上测一遍,别只复制网页文字测。Word和网页复制出来的HTML完全不是一种东西,网页测试通过不代表Word测试没问题。
3. 前端Paste事件清洗:把Word专属垃圾拦在门外
3.1 监听粘贴事件,拿到HTML后先做检测和疏通
如果只靠编辑器配置,Word里大量的私有标签和条件注释还是拦不干净。更可靠的办法是:在前端页面上用JavaScript直接监听paste事件,在内容还没进入编辑器之前就检查剪贴板里的text/html,发现Word特征码就走一遍清洗函数。
先看核心的检测和拦截逻辑:
document.addEventListener('paste', function (e) { // 判断粘贴目标是否在编辑区域内,避免全局误伤 const target = e.target; if (!target.closest || !target.closest('.editor-container')) { return; } const cbData = e.clipboardData || window.clipboardData; const html = cbData && cbData.getData('text/html'); if (!html) return; // 检测是否来自 Word / WPS / MSO 的字样 const wordPattern = /mso-|MsoChrome|xmlns:o|xmlns:w|v:imagedata|office:word|Word.Document/i; if (!wordPattern.test(html)) return; // 是 Word 内容就走清洗逻辑,阻止默认粘贴 e.preventDefault(); const cleanHtml = cleanHtmlFromWord(html); // 按编辑器类型插入内容 const ue = window.ue || window.UEDITOR; if (ue) { ue.execCommand('inserthtml', cleanHtml); } else { document.execCommand('insertHTML', false, cleanHtml); } });这一段的作用是“精准识别”,只处理真正来自Word的粘贴内容。平常从网页复制的正文不会命中Word正则,也就不会被打乱原有格式,不影响正常编辑体验。
3.2 用DOMParser做白名单清洗,而不是用正则硬啃
很多人第一反应是用正则replace把style、font、span全部删掉。我试过,效果很差。因为Word生成的HTML结构非常不规则,标签可能多层嵌套、属性顺序不固定、还有大量条件注释和Self-Closing标签,正则写来写去总会漏。
正确做法是把HTML字符串交给DOMParser解析成真正的DOM树,然后遍历所有节点做白名单处理。这是我在项目里用的一套清洗函数:
function cleanHtmlFromWord(html) { // 解析成 DOM 树,之后的所有操作都基于真实节点 const doc = new DOMParser().parseFromString(html, 'text/html'); // 白名单之外的标签会被展开,内容保留,标签本身去掉 const allowed = new Set([ 'P', 'BR', 'DIV', 'STRONG', 'B', 'EM', 'I', 'U', 'A', 'IMG', 'TABLE', 'THEAD', 'TBODY', 'TR', 'TD', 'TH', 'UL', 'OL', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'BLOCKQUOTE' ]); function cleanNode(node) { const children = Array.from(node.childNodes); children.forEach(function (child) { // 注释节点直接删除,这里往往是 Word 的条件注释 if (child.nodeType === Node.COMMENT_NODE) { node.removeChild(child); return; } if (child.nodeType !== Node.ELEMENT_NODE) return; const tag = child.tagName.toUpperCase(); if (!allowed.has(tag)) { // 不在白名单里的标签,比如 span/font/o/v/script,展开子节点后删除 while (child.firstChild) { node.insertBefore(child.firstChild, child); } node.removeChild(child); // 注意:删掉后要继续处理被提升上来的节点,所以重新跑当前层 cleanNode(node); return; } // 白名单标签只保留必要属性 if (tag === 'A') { const href = child.getAttribute('href'); child.removeAttribute('href'); if (href) child.setAttribute('href', href); const title = child.getAttribute('title'); if (title) child.setAttribute('title', title); } else if (tag === 'IMG') { const src = child.getAttribute('src') || ''; child.removeAttribute('src'); // 只保留能够正常使用的链接或 base64,其余本地路径直接丢弃 if (/^(data:image|https?:|\/\/)/i.test(src)) { child.setAttribute('src', src); } else { child.remove(); return; } } else if (tag === 'TD' || tag === 'TH') { // 表格单元格的合并信息要保留,否则表格结构会坏 const colspan = child.getAttribute('colspan'); const rowspan = child.getAttribute('rowspan'); child.removeAttribute('colspan'); child.removeAttribute('rowspan'); if (colspan) child.setAttribute('colspan', colspan); if (rowspan) child.setAttribute('rowspan', rowspan); } // 统一清掉所有样式相关属性,这是“减负”的关键 ['style', 'class', 'id', 'lang', 'align', 'dir'].forEach(function (attr) { child.removeAttribute(attr); }); // 递归处理子节点 cleanNode(child); }); } cleanNode(doc.body); // 把清洗后的内容重新转成 HTML 字符串 const container = doc.createElement('div'); while (doc.body.firstChild) { container.appendChild(doc.body.firstChild); } return container.innerHTML; }这个方案的好处是:不用关心Word具体生成了多少种样式,只要没在白名单里的统统拆掉。span、font这类标签会被“拆”掉,里面的文字直接提升到父级,格式虽然丢了,但内容是完整的。
实测下来,一段约5000字的Word文章,清洗后源码体积能减少70%以上,页面渲染和前端加载速度都有明显改善。
3.3 清洗过程中的几个坑和补救方法
这套方案在帝国CMS 7.5前台投稿和后台编辑器场景都跑过,有几个坑必须提前讲。
第一,clipboardData不是所有浏览器都稳定返回text/html。极少数老版本浏览器里,你取不到HTML,只能拿到纯文本。这种情况干脆走纯文本粘贴,别强上清洗逻辑。
第二,编辑器的光标位置和焦点问题。直接调用document.execCommand('insertHTML')只对contenteditable区域有效,某些编辑器使用了iframe隔离,那就得用对应的编辑器API。上面代码里我判断了window.ue是否存在,如果项目里用的是别的编辑器,需要换成对应的insertHTML接口。
第三,大段内容粘贴时清洗耗时可能达到几百毫秒,用户会感觉卡顿。建议在粘贴后加一个轻量的“正在处理”状态,或者把清洗过程放到requestAnimationFrame里分批执行。我通常在内容超过3000字时会用异步队列,避免主线程卡死导致浏览器弹“无响应”。
4. Word图片的三种归宿:本地图片变成线上资源才算完
4.1 Word图片粘贴之后到底变成了什么
从Word复制带图的文档时,图片并不会老老实实变成一张线上图片链接。根据浏览器和文档类型的不同,你得到的可能是下面几种东西之一:file:///C:/Users/.../xxx.png这种本地路径、blob:http://...这种浏览器临时对象、v:imagedata这种老式VML引用,少数情况下是base64串。这些值直接塞进正文,前台访问者根本看不到图,要么显示一个破图标,要么一片空白。
帝国CMS 7.5默认的图片处理机制,并不会自动把Word粘贴过程中出现的本地图片变成可访问的URL。需要我们在前端或后台补上一层“图片转存”逻辑。
4.2 三种方案对比
| 方案 | 实现方式 | 优点 | 缺点 |
|---|---|---|---|
| base64内嵌 | 清洗时保留data:image,直接存进正文 | 实现最简单,懒人首选 | 正文体积急剧膨胀,一篇带图文章可能多出几MB,数据库和页面都遭殃 |
| 上传服务器替换src | 粘贴时检测到本地图片,走上传接口,把返回的URL写回src | 最通用,前端展示稳定 | 需要额外写上传逻辑,同步处理时要注意编辑内容时机 |
| 后台远程抓图 | 提交文章后由后台扫描正文,自动下载远程图片到本地 | 编辑无感,适合转载场景 | 抓图失败率高,受外链防盗链限制 |
一开始图省事我用了base64方案,后来实测发现:一张1MB的图片转成base64后大概1.33MB,编辑保存和前端加载都很痛苦,随便几组图就能把数据库字段撑爆。所以最终我放弃了这个方案。
4.3 我最后落地:上传服务器并替换src
结合帝国CMS 7.5的场景,我在前端Paste清洗之后,对img标签做二次处理:只要src不符合http(s)或//,就提取出来通过接口上传,上传成功后再把返回地址写回,然后再插入编辑器。关键代码如下:
function handleWordImages(html) { const tempDiv = document.createElement('div'); tempDiv.innerHTML = html; const images = tempDiv.querySelectorAll('img'); if (!images.length) return Promise.resolve(html); // 串行上传,避免一次性并发太多把服务器打挂 const uploadQueue = Array.from(images); let chain = Promise.resolve(); uploadQueue.forEach(function (img) { chain = chain.then(function () { const src = img.getAttribute('src') || ''; if (!/^data:image/i.test(src) && !/^https?:/i.test(src)) { // 不是 base64,也不是 http,说明是本地路径,跳过 img.remove(); return; } return uploadImage(src).then(function (url) { img.setAttribute('src', url); }).catch(function () { img.remove(); }); }); }); return chain.then(function () { return tempDiv.innerHTML; }); } function uploadImage(src) { // 注意:archon endpoint 按自己项目的后端接口调整 return fetch('/api/upload-paste-image', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ data: src }) }).then(function (res) { return res.json(); }).then(function (res) { if (res.code !== 0) throw new Error(res.msg); return res.url; }); }后端对应的上传接口我建议做四件事:校验文件类型,只允许常见图片格式;限制单张大小,比如不超过2MB;用随机文件名避免中文和空格;做一次简单压缩,把宽度超过内容区宽度的图等比缩放。这样前端展示时不会因为原图太大而撑破布局,也省流量。
一个容易被忽略的细节:上传是异步的,而编辑器内容的插入是同步的。如果你等到上传完再把最终HTML塞回编辑器,需要先把光标位置记下来。我用的是编辑器提供的focus加insertHtml组合,有的项目里干脆采用“先插占位图,上传完成后替换占位图”的方案,也能减少光标丢失问题。
5. 样式防御战:就算漏网之鱼进来,前端布局照样不崩
5.1 给文章内容区做一个“样式隔离区”
即便前端清洗做得再仔细,也总有漏网之鱼。有些运营会把Word里排版好的表格、多级列表、特殊字符直接复制进来,清洗规则没覆盖到的地方就可能带出少量格式。这时候前端模板层的CSS就成了最后一道防线。
我的做法是:在帝国CMS 7.5的模板里给正文容器加一个独立类名,比如.article-content,然后在这个类的作用域下把所有常用标签的样式重新“标准化”一遍。注意,这里不是用!important去暴力覆盖,而是让容器内的标签遵循一套确定性的规则:
.article-content { line-height: 1.75; word-break: break-word; font-size: 16px; color: #333; } .article-content p { margin: 0 0 1em; line-height: 1.75; } .article-content img { max-width: 100%; height: auto; display: block; margin: 0.5em 0; } .article-content table { width: auto; max-width: 100%; border-collapse: collapse; } .article-content td, .article-content th { padding: 6px 10px; border: 1px solid #ddd; word-break: break-word; }这套规则的核心逻辑是:把“未知”的内联样式尽量限制在内容区域里,防止它们溢出到页面其它部分。比如Word粘贴过来的段间距可能是line-height: 28px,在桌面端看着没问题,但在手机端就会显得特别挤或特别空。有了内容区的统一样式,页面就能维持大致的阅读节奏。
5.2 表格、长串英文、图片这些“重灾区”单独处理
有几个元素需要单独上“保险”。
表格是最容易让页面“开裂”的元凶。Word里一个表格可能设置了固定列宽、200%的宽度,粘贴过来之后直接把爹容器撑出水平滚动条。我建议在模板输出层面加一个处理:用JavaScript或者后端替换方式,给正文里所有的table外面包一层div.table-wrap,并让这个div实现overflow-x: auto。同时把表格本身的width强制改成max-width: 100%。这样哪怕表格内容很宽,也只是在容器内滚动,不会破坏整页布局。
长串英文和连续数字也是常见问题。Word里不会自动换行的地方粘贴过来,前端可能一个长URL就把宽度撑破了。所以我在.article-content里统一设置了word-break: break-word,同时给a标签加上word-wrap: anywhere,保证任何奇怪的超长字符串都能折行。
图片的max-width: 100%属于防呆措施。如果清洗函数或编辑器没把图片的固定宽高属性删干净,这个规则也能保证图不会超出容器。注意height: auto要跟max-width一起写,否则图片宽缩小后高还保持原样,看着会变形。
5.3 避免“补丁地狱”:别在一套残缺样式上无限加规则
我在早期项目里犯过一个错:发现Word粘贴的某类样式崩了,就往CSS末尾追加一条覆盖样式;再崩一个,再追加一条。一个月之后,样式表尾部堆了几十条孤零零的hack,连维护者自己都分不清哪条还在生效、哪条早就被更后面的规则覆盖了。
这种补丁地狱的解决方式不是继续打补丁,而是把正文容器做成一个相对独立的“小世界”。在这个小世界里,尽量少依赖全局样式,所有相关规则集中在一块,遇到新的异常标签先回看白名单,看是不是清洗层漏了某种标签,而不是无脑加CSS。前端的防御要跟清洗逻辑配合:清洗层负责“不放进脏东西”,CSS层负责“万一漏进来的东西不闹事”,各管一段,边界清晰。
6. 历史污点数据兜底:给帝国CMS 7.5写一个内容清洗函数
6.1 清理函数的PHP实现思路
前端的清洗只对“新粘贴”的内容生效,但很多站点上线前已经有大量历史文章,早就带着Word垃圾数据躺在数据库里了。这时候需要在后端写一个一次性的清洗脚本。
针对帝国CMS 7.5,文章内容一般存在phome_ecms_news_data或类似数据表的newstext字段里。清洗思路不难:读出来,用PHP正则和字符串处理把垃圾标签和属性去掉,再写回去。下面是我常用的一套函数:
function clean_mso_html($html) { if (!$html) return ''; // 1. 去掉 Word 条件注释块 $html = preg_replace('/<!--\[if[^\]]*\]>.*?<!\[endif\]-->/is', '', $html); // 2. 去掉常见的 MSO 属性、class、id、style、lang、align 等 $html = preg_replace( '/\s(?:mso-[a-z0-9-]+|xmlns:o|xmlns:w|xmlns:v|class|style|id|lang|align)="[^"]*"/i', '', $html ); // 3. 去掉 Word 引入的私有标签本身,保留内部内容 $html = preg_replace('/<\/?(?:span|font|o|v|w)[^>]*>/i', '', $html); // 4. 压缩连续空白 $html = preg_replace('/\s{2,}/', ' ', $html); return trim($html); }注意第2步会删掉所有style,包括必要的表格宽度。如果历史文章里有一部分真的需要保留固定宽度,可以调整正则,只删除包含mso-前缀的样式属性,保留其它内联样式。我在项目里通常是分两档:普通文章直接全删;带复杂表格的文章只删mso-相关属性,其余的交给CSS层控制。
6.2 一次性批量脚本:分批处理,别把数据库搞挂
清洗脚本跑数据库的时候,建议用分页方式处理,别一条SQL把所有数据全读进内存。帝国CMS 7.5的老库里可能几万条文章,一次性全量UPDATE轻则超时,重则把生产库锁死。
我一般这样处理:先取出所有ID列表,再按每100条一批处理。每批读出内容、调用clean_mso_html、更新字段,处理完一批打印一行日志。跑之前一定先备份数据库,别嫌麻烦,这类脚本一旦正则写漏了,删掉正文内容都没处说理。
跑完脚本之后还要验证两件事:一是总记录数和更新记录数对得上,二是有几个特殊字符是否被误伤,比如&、<、>这些HTML实体。正则直接操作HTML字符串有时会破坏实体编码,所以我的函数里保留了实体本身,只用标签层面的匹配,降低误伤率。
6.3 清理时容易误伤的地方
这个函数看起来简单,实际上手的坑一个不少。
第一个坑是正则贪婪匹配。条件注释的闭合可能有跨行情况,如果匹配不对会把大段正文吞掉。我建议在测试环境先跑一小批,肉眼检查几篇文章,确认没问题再全量。
第二个坑是表格属性。很多Word表格靠colspan和rowspan维持结构,如果清洗时把它们删了,表格会变成一团乱麻。所以只清理样式属性,表格结构属性必须保留。
第三个坑是图片。历史正文里的图片如果是v:imagedata,这个标签被删掉之后图片也跟着没了。遇到这种情况必须在脚本里把v:imagedata的src提取出来转成普通<img>标签。这个转换逻辑稍微复杂一点,但考虑到老站里Word粘贴图片并不少见,值得加进脚本。
7. 日常运维中的测试清单和几个值得养成的习惯
7.1 一张可以贴在工位上的粘贴测试表
处理完技术方案之后,我建议把测试标准化。每次改动编辑器或清洗脚本,都用同一份测试文档进行回归。测试文档里至少包含:普通段落、多级标题、三列表格、嵌入图片、加粗文字、超链接、编号列表、长URL。复制粘贴后逐一对照前台效果。
| 测试项 | 预期结果 | 常见失败表现 |
|---|---|---|
| Word 2007文档粘贴 | 段落结构保留、无mso垃圾、图片正常显示 | 首行缩进丢失、表格宽度异常 |
| WPS文档粘贴 | 同上 | 字体大小错乱、段落间距丢失 |
| 网页正文复制粘贴 | 正常网页格式不被打乱 | 清洗正则误判网页内容 |
| 多级标题粘贴 | H1/H2层级保留 | 标题全部变成P标签 |
| 带图片Word文档粘贴 | 图片上传成功,前台可访问 | 图片空白、破图标、样式丢失 |
| 长表格粘贴 | 容器内滚动,不撑破页面 | 页面出现水平滚动条 |
这张表的价值在于:你每次改完前端模板或清洗逻辑,能在十分钟内完成一次全场景回归,不用临时找素材。
7.2 我实测起来比较有效的工作习惯
最后说几个实战里帮我省过不少事的小习惯。
第一,给运营同事写一个“正确的粘贴流程”小贴士:从Word复制后,在编辑器里先按Ctrl+Shift+V粘贴成纯文本,再用编辑器工具栏重新排版标题和列表。刚开始他们可能不习惯,但配合前端的纯文本粘贴快捷键兜底,经过两周就能形成肌肉记忆,脏数据数量肉眼可见地下降。
第二,不要只依赖编辑器自带的“清除格式”按钮。很多清除格式功能只删除手动设置的样式,对Word粘贴进来的内联样式和嵌套标签非常无力。我的方案里清洗函数才是真正兜底的,编辑器自带的按钮反而是最后一道人工补救。
第三,上线新版本前一定要在测试环境做实弹测试。别用同一个网页内容反复测,要真的从Word里复制图文再粘贴。我遇到过好几次:前端清洗函数处理Word 2010版本没问题,但处理新版本Word粘贴出来的HTML时,因为对方改变了一些私有标签前缀,部分样式漏网。测试越贴近真实编辑操作,踩坑概率越低。
把这些步骤组合起来之后,帝国CMS 7.5前端开发中的Word粘贴问题基本就能收敛了。编辑器的参数调整和Paste事件清洗解决掉80%的脏数据,图片转存解决掉图片显示问题,CSS隔离兜住漏网之鱼,脚本处理掉历史存量。整套方案跑下来,运营那边再也不会交上来一篇“车祸现场”文章,前台的页面加载速度和SEO表现也跟着好转。别嫌这套东西繁琐,老系统做内容治理,本来就是要多点布防、层层兜底。