# CSS/JS/字体资源分析 本文档对微信读书 EPUB 阅读器的前端资源文件进行全面分析,涵盖 CSS 样式表、JavaScript 脚本和字体文件。 --- ## 一、CSS 文件分析 ### 1.1 样式层叠架构 微信读书的 CSS 采用分层架构: ``` default.css (基础HTML样式) ├── replace.css (EPUB替换样式) ├── replaceForLatinLanguageBook.css (拉丁语书籍替换样式) ├── replaceForMPChapter.css (公众号章节替换样式) └── MediaPlatform.css (公众号文章基础样式) └── MPExtra.css (公众号文章扩展样式) dark.css (暗黑主题 - 覆盖层) Style/style.css (富文本编辑器样式) Style/normalize.css (标准化样式) Style/preview.css (预览模式样式) xcode.min.css (代码高亮主题) WREpubMPVideo.css (视频播放器样式) ``` ### 1.2 default.css - 基础HTML样式 **路径**: `output/resources/css/default.css` (222行) Safari 派生的基础 HTML 默认样式,为 WebView 渲染提供标准化起点。 **核心特性**: - HTML5 元素设置 `display:block`(article, aside, footer, header, nav, section 等) - 隐藏非显示元素:`head, title, style, link, meta, script { display:none }` - body 默认字体大小 `16px` - 链接颜色 `#2262A3` - 代码块样式:`background-color:rgba(0,0,0,.05)`, `border-radius:0.3em`, `padding:0.2em` - 表格边框合并:`border-collapse:collapse` ### 1.3 replace.css - EPUB替换样式 **路径**: `output/resources/css/replace.css` (172行) EPUB 内容的核心替换样式,定义了微信读书特有的排版规则。 **字体设置**: - 标题和版权页使用 `"SourceHanSerifCN-Medium"` 字体 - 代码块使用 `Consolas, "Liberation Mono", Menlo, Courier, monospace`,大小 `0.7em!important` **标题层级**: | 类名 | 字号 | 字重 | |------|------|------| | `.firstTitle` | 1.5em | normal | | `.secondTitle` | 1.3em | normal | | `.thirdTitle` | 1.15em | normal | | `.fourthTitle` | 1.1em | normal | | `.fifthTitle` | 1.05em | normal | | `.sixthTitle` | 1em | normal | **微信读书自定义CSS属性**: ```css /* 垂直居中样式 - 值2用于正文图片 */ .bodyPic { wr-vertical-center-style: 2; } /* 垂直居中样式 - 值1用于强制垂直居中 */ .wr-vertical-center { wr-vertical-center-style: 1 !important; } /* 页面关联标记 */ .weread-page-relate { weread-page-relate: true; } ``` **其他重要样式**: - `.ftext` - 首字母放大效果(2.38em, float:left) - `.book-chapter-tool` - 章节结束工具栏(78px高度) - `p { text-align:justify; }` - 段落两端对齐 - `.eepub-single-image-title` - 单图片标题,使用 `"FZFSJW--GB1-0"` 字体 ### 1.4 dark.css - 暗黑主题 **路径**: `output/resources/css/dark.css` (57行) 纯黑背景的暗黑主题样式。 **核心样式**: ```css /* 所有元素强制黑色背景 */ * { background-color: rgba(0, 0, 0, 1) !important; } /* 业务容器特殊背景 */ .business-container { background-color: rgb(28, 28, 29) !important; } /* 文字颜色 */ body, p, div, span, a { color: rgba(180, 180, 182, 1) !important; } /* 链接颜色 */ a { color: rgba(84, 127, 176, 1) !important; } /* 引用块 */ blockquote { color: rgb(218, 220, 224); background-color: rgb(10, 14, 18); } ``` **特殊处理**: - 兼容搜狗/百度搜索页面的暗黑模式 ### 1.5 replaceForLatinLanguageBook.css - 拉丁语书籍样式 **路径**: `output/resources/css/replaceForLatinLanguageBook.css` (113行) 拉丁语系书籍的特殊替换样式。 **与replace.css的关键差异**: - 标题不设置 `font-family`(使用浏览器默认字体而非 SourceHanSerifCN-Medium) - `.eepub-single-image-title` 使用 `"FZFSJW--GB1-0"` 字体 - 保留相同的 `wr-vertical-center-style` 属性 ### 1.6 replaceForMPChapter.css - 公众号章节样式 **路径**: `output/resources/css/replaceForMPChapter.css` (137行) 微信公众号文章章节的特殊样式。 **字体设置**: - 使用 `"Source Han Serif CN"` 字体族(带空格,与 replace.css 的 `"SourceHanSerifCN-Medium"` 不同) - 标题 `font-weight: bold`(replace.css 中为 normal) **特殊样式**: ```css /* 打赏区域 */ .chapter-reward { height: 120px; } /* 强制透明背景 - epub-custom-rule-21 */ p, strong, span { background-color: transparent !important; } /* 强制透明背景 - epub-custom-rule-26 */ section, .rich_media_area_primary { background-color: transparent !important; } ``` ### 1.7 MediaPlatform.css - 公众号文章基础样式 **路径**: `output/resources/css/MediaPlatform.css` (141行) 微信公众号文章的基础样式,包含标注系统。 **主要内容区域**: ```css .rich_media_area_primary { font-size: 18px; line-height: 30px; font-family: -apple-system; } ``` **标注系统样式**: | 类名 | 用途 | 样式 | |------|------|------| | `.highlight` | 高亮标注 | `background-color: rgba(255, 116, 0, .25)` | | `.review` | 我的想法 | 红色虚线边框 `border: 1px dashed rgba(220, 53, 69, 1)` | | `.friend-review` | 好友想法 | 灰色虚线边框 `border: 1px dashed rgba(130, 130, 130, 1)` | | `.reference` | 引用标注 | `background-color: rgba(255, 214, 0, .25)` | | `.tts` | TTS朗读高亮 | `background-color: rgba(27, 136, 238, .25)` | **复合标注样式**: ```css /* review 覆盖 highlight 的样式 */ .highlight.review { background-color: transparent; border: 1px dashed rgba(220, 53, 69, 1); } ``` **其他重要样式**: - `-webkit-tap-highlight-color: transparent` - 禁用所有标注类型的点击高亮 - `.h5_image_link { pointer-events: none; }` - 禁用图片链接点击 ### 1.8 MPExtra.css - 公众号文章扩展样式 **路径**: `output/resources/css/MPExtra.css` (673行) 完整的微信公众号文章 CSS,从微信网页版复制。 **核心特性**: ```css /* 内容区域最大宽度限制 */ .rich_media_content * { max-width: 100% !important; box-sizing: border-box !important; } ``` **WeUI 对话框系统**: - `.weui-mask` - 遮罩层 - `.weui-dialog` - 对话框 **音频播放器样式**: - `.audio_container` - 音频容器 - `.audio_button` - 播放按钮 - `.audio_progress` - 进度条 **其他组件**: - Loading spinner(base64 SVG 动画) - GIF 图片处理(`.gif_img_wrp`) - 设备特定媒体查询(iPhone6/6P) - `.unsupported_iframe` 不支持元素样式 ### 1.9 xcode.min.css - 代码高亮主题 **路径**: `output/resources/css/xcode.min.css` (1行,压缩) highlight.js 的 xcode 主题,用于代码语法高亮。 - 白色背景,黑色文字 - 彩色语法标记(关键字、字符串、注释等) ### 1.10 WREpubMPVideo.css - 视频播放器样式 **路径**: `output/resources/css/WREpubMPVideo.css` (191行) 腾讯视频播放器的自定义样式覆盖。 **核心组件**: - `.tvp_container` - 视频容器 - `.tvp_controlbar` - 控制栏 - `.tvp_playbtn` / `.tvp_pausebtn` - 播放/暂停按钮 - `.tvp_progress_slider` - 进度滑块 **资源引用**: ```css /* 使用腾讯CDN的雪碧图 */ background-image: url(https://rescdn.qqmail.com/weread/cover/icon/WREpubMPVideo.png); ``` **广告隐藏**: ```css /* 隐藏广告和应用下载提示 */ .tvp_ads_go, .tvp_app_download_onpause { display: none !important; } ``` ### 1.11 Style/style.css - 富文本编辑器样式 **路径**: `output/resources/css/Style/style.css` (408行) 富文本编辑器的基础样式。 **编辑器模式**: ```css /* 编辑模式 */ .re_Write { /* 可编辑状态样式 */ } /* 显示模式 */ .re_Display { /* 只读状态样式 */ } /* 夜间模式 */ .re_Night { color: rgba(180, 180, 182, 1); background-color: rgba(0, 0, 0, 1); } ``` **书籍卡片组件**: ```css .re_bookItem { /* 书籍信息展示 */ } .re_bookItem_cover { /* 封面图片 */ } .re_bookItem_title { /* 书籍标题 */ } .re_bookItem_author { /* 作者信息 */ } ``` **图片处理**: ```css .re_img { /* 图片样式 */ } .bodyPic { /* 正文图片 */ } ``` **高DPI边框处理**: ```css /* 使用transform:scale(0.5)实现0.5px边框 */ .retina-border { border: 1px solid #ccc; transform: scale(0.5); } ``` ### 1.12 Style/normalize.css - 标准化样式 **路径**: `output/resources/css/Style/normalize.css` (412行) 标准的 normalize.css v3.0.2,最小修改。 ### 1.13 Style/preview.css - 预览模式样式 **路径**: `output/resources/css/Style/preview.css` (174行) 预览模式的特殊样式。 **核心样式**: ```css .re { font-size: 18px; line-height: 30px; font-family: -apple-system; } ``` **书籍卡片**: - 使用 0.5px 边框技术(retina 显示屏优化) **占位符文本**: ```css .re_placeholder:before { content: attr(placeholder); color: #999; } ``` **引用块**: ```css .blockquote { border-left: 3px solid #ccc; background-color: #f9f9f9; padding: 10px; } ``` --- ## 二、JavaScript 文件分析 ### 2.1 JS 架构概览 ``` WeReadApi.js (JS Bridge 核心) ├── weread-highlighter.js (高亮/标注引擎) │ ├── rangy-highlighter.js (Rangy高亮模块) │ ├── rangy-classapplier.js (Rangy类应用器) │ ├── rangy-textrange.js (Rangy文本范围) │ └── rangy-core.js (Rangy核心库) ├── rich_editor.js (富文本编辑器) ├── rich_display.js (富文本显示控制器) ├── MediaPlatform.js (公众号工具函数) ├── mpForArticle.js (公众号内容调整) ├── mp_interceptor.js (点击事件拦截器) ├── Readability.js (文章内容提取) ├── HtmlGenerator.js (HTML生成器) ├── cssInjector.js (CSS注入器) └── 其他工具脚本 ``` ### 2.2 WeReadApi.js - JS Bridge 核心 **路径**: `output/resources/js/Text/WeReadApi.js` (238行) WebView 与原生应用之间的核心通信桥接。 **WeReadBridge 类**: ```javascript WeReadBridge = function() { this._sendMessageQueue = []; // 消息队列 this._callback_count = 1000; // 回调计数器 this._callback_map = {}; // 回调映射表 this.available_func = {}; // 可用函数 this._iframe = document.createElement("iframe"); this._resultIframe = document.createElement("iframe"); } ``` **通信协议**: - 发送消息:`wereadapijs://dispatch_message/` - 接收结果:`wereadapijs://private/setresult/` **核心方法**: | 方法 | 用途 | |------|------| | `handleWithRichEditor(apiName, params, successCallback, failCallback)` | 通用API调用 | | `execMPReaderMethod(apiName, params, successCallback, failCallback)` | 公众号阅读器方法调用 | | `fetchQueue()` | 获取队列中的消息(JSON字符串) | | `_sendMessage(message)` | 将消息加入队列并触发原生调用 | | `_call(func, params, callback)` | 创建带callbackId的消息并发送 | | `handleMessage(message)` | 处理来自原生的回调响应 | **消息格式**: ```javascript { "func": "apiName", "params": { "key": "value", "callbackId": "1001" } } ``` **数据编码**: - `_utf8Encode(str)` - UTF-8编码 - `_base64Encode(str)` - Base64编码 - 用于通过iframe URL传递数据 **Ready机制**: ```javascript // 检查原生是否已注入信息 if (window["__WRB_INFO__"]) { _onReady(); } else { window["__WRB_INFO_CALL__"] = function() { _onReady(); }; } ``` **全局实例**: ```javascript window.wereadBridge = new WeReadBridge(); ``` ### 2.3 weread-highlighter.js - 高亮/标注引擎 **路径**: `output/resources/js/weread-highlighter.js` (1530行) 公众号文章的核心高亮和标注引擎,基于 Rangy 库构建。 **初始化**: ```javascript // 初始化Rangy,使用TextRange模式 rangy.init(); highlighter = new rangy.Highlighter(); ``` **标注类型**: | 类名 | 用途 | 对应CSS | |------|------|---------| | `highlight` | 高亮标注 | `rgba(255, 116, 0, .25)` | | `review` | 我的想法 | 红色虚线边框 | | `friend-review` | 好友想法 | 灰色虚线边框 | | `reference` | 引用标注 | `rgba(255, 214, 0, .25)` | | `tts` | TTS朗读高亮 | `rgba(27, 136, 238, .25)` | | `blue-clickable` | 可点击蓝色 | 用于书籍标题等 | **核心函数**: ```javascript // 初始化标注(从JSON加载) initAnnotations(items) // 创建高亮 didTapCreateHighlightButton() // 创建想法 didTapCreateReviewButton(start, end) // 删除标注 didTapDeleteAnnotationButton(start, end, type) // 点击处理 click(el, event) ``` **TTS系统**: ```javascript // TTS状态追踪 var lastTTSInfo = { text: '', index: 0, startY: 0 }; // TTS相关类 class TTSRange { constructor(start, end, text) { ... } } class TTSText { constructor(text, index, startY) { ... } } // 获取TTS文本 getTTSText(startY) // 高亮TTS文本 highlightTTSText(text, index, startY) ``` **书籍标题检测**: ```javascript // 查找所有《》包围的书籍标题 findAllBookTitles() // 高亮选中的书籍标题 highlightSelectedBookTitles(indexArray) ``` **链接处理**: ```javascript // 获取所有前向链接 getAllForwardLinks() // 转换H5链接为原生数据属性 transferH5Link() // 解密微信读书加密链接 WRLink_decrypt(str) // type '3' = 数字 // 其他 = 十六进制编码文本 ``` **选择变化处理**: ```javascript // 防抖处理(250ms) document.addEventListener("selectionchange", debounce(function(e) { // 发送选择变化事件到原生 wereadBridge.handleWithRichEditor("selectionChange", param, "", ""); }, 250)); ``` **Bridge命令**: - `selectionChange` - 选择变化 - `didTapHighlight` - 点击高亮 - `didTapReview` - 点击想法 - `didTapImage` - 点击图片 - `didTapBook` - 点击书籍 - `scrollToOffsetTop` - 滚动到偏移位置 - `didTapHighlightBookTitle` - 点击高亮书籍标题 ### 2.4 Rangy 模块 #### 2.4.1 rangy-highlighter.js (711行) Rangy 高亮器模块,带微信读书自定义修改。 **CharacterRange 类**: ```javascript class CharacterRange { constructor(start, end) { ... } intersects(otherRange) { ... } isContiguousWith(otherRange) { ... } isContain(otherRange) { ... } union(otherRange) { ... } intersection(otherRange) { ... } getComplements(orderedRanges) { ... } } ``` **Highlighter 类**: ```javascript class Highlighter { highlightCharacterRanges(className, charRanges, ...) { ... } highlightRanges(className, ranges, ...) { ... } highlightSelection(selection, ...) { ... } serialize() { ... } deserialize(serialized) { ... } } ``` **自定义方法**: - `getMergedCharacterRange` - 合并重叠的高亮 - `highghtWhichContainSelection` - 查找包含当前选择的高亮 **序列化格式**: ``` type:TextRange|start$end$id$className[|$containerId] ``` #### 2.4.2 rangy-classapplier.js (1105行) Rangy 类应用器模块。 **ClassApplier 类**: ```javascript class ClassApplier { constructor(className, options) { ... } applyToRange(range) { ... } undoToRange(range) { ... } applyToSelection(selection) { ... } undoToSelection(selection) { ... } isAppliedToRange(range) { ... } isAppliedToSelection(selection) { ... } toggleRange(range) { ... } toggleSelection(selection) { ... } } ``` **暴露的工具方法**: ```javascript rangy.dom.util.hasClass(node, className) rangy.dom.util.addClass(node, className) rangy.dom.util.removeClass(node, className) rangy.dom.util.getClass(node) rangy.dom.util.splitNodeAt(node, index, ...) ``` #### 2.4.3 rangy-textrange.js (1946行) Rangy 文本范围模块,支持基于字符的范围操作。 **Position 类**: ```javascript class Position { constructor(node, offset) { ... } // 字符类型检测 get characterType() { ... } // NON_SPACE, COLLAPSIBLE_SPACE, etc. get isImage() { ... } // 图片文本支持 } ``` **字符类型常量**: - `NON_SPACE` - 非空格字符 - `COLLAPSIBLE_SPACE` - 可折叠空格 - `NON_COLLAPSIBLE_SPACE` - 不可折叠空格 - `IMAGE_TEXT` - 图片文本 **Range 扩展方法**: ```javascript range.selectCharacters(containerNode, startIndex, endIndex) range.toCharacterRange(containerNode) range.text // 获取文本 range.move(unit, count) // 移动范围 range.expand(unit, options) // 扩展范围 range.findText(text, options) // 查找文本 ``` ### 2.5 rich_editor.js - 富文本编辑器 **路径**: `output/resources/js/Text/rich_editor.js` (1710行) 用于编写想法/评论的完整富文本编辑器。 **RE 命名空间**: ```javascript RE = { editor: document.getElementById('editor'), dataSeparator: 'r_e_ds', // ... 其他属性和方法 } ``` **核心方法**: | 方法 | 用途 | |------|------| | `RE.setHtml(contents)` | 设置HTML内容 | | `RE.getHtml()` | 获取HTML内容 | | `RE.getText()` | 获取纯文本 | | `RE.setBold()` | 设置粗体 | | `RE.setItalic()` | 设置斜体 | | `RE.setHeading(level)` | 设置标题级别 | | `RE.setBlockquote()` | 设置引用块 | | `RE.setUnorderedList()` | 设置无序列表 | | `RE.setOrderedList()` | 设置有序列表 | | `RE.setJustifyLeft()` | 左对齐 | | `RE.setJustifyCenter()` | 居中对齐 | | `RE.setJustifyRight()` | 右对齐 | | `RE.insertBook(id, title, author, cover)` | 插入书籍卡片 | | `RE.insertImage(imgArray)` | 插入图片 | | `RE.insertQQEmoticon(url, alt, w, h)` | 插入QQ表情 | | `RE.insertLink(text, href, title)` | 插入链接 | | `RE.unlink()` | 移除链接 | | `RE.updateLink()` | 更新链接 | | `RE.insertHTML(html)` | 插入HTML | | `RE.insertText(text)` | 插入纯文本 | | `RE.backuprange()` | 备份选择范围 | | `RE.restorerange()` | 恢复选择范围 | | `RE.focus()` | 获取焦点 | | `RE.blurEditor()` | 失去焦点 | **数据分隔符**: ```javascript RE.dataSeparator = 'r_e_ds'; // 用于连接多个值 ``` **内容变化通知**: ```javascript RE.contentChanged = function(e) { // 通知原生格式状态变化 wereadBridge.handleWithRichEditor("onSelectionChange", param, "", ""); } ``` **EPUB HTML导出**: ```javascript RE.getHtmlForEpub = function() { // 移除临时类并应用correctArticle return correctedHtml; } ``` **粘贴处理**: ```javascript document.addEventListener('paste', function(e) { // 拦截粘贴,发送到原生处理 wereadBridge.handleWithRichEditor("onPaste", {}, "", ""); }); ``` ### 2.6 rich_display.js - 富文本显示控制器 **路径**: `output/resources/js/Text/rich_display.js` (262行) 富文本内容的显示控制器,处理书籍和图片的交互。 **书籍交互**: ```javascript RDisplay.onBookToucheStart = function(ele) { ele.classList.add('re_bookItem_Touched'); } RDisplay.onBookToucheEnd = function(ele) { ele.classList.remove('re_bookItem_Touched'); } RDisplay.onBookClick = function(ele) { var id = ele.getAttribute('data-id'); wereadBridge.handleWithRichEditor("onGotoBookDetail", {"data-id": id}, "", ""); } ``` **图片交互**: ```javascript RDisplay.onImageClick = function(ele) { var src = ele.getAttribute('src'); var index = ele.getAttribute('index'); var position = RDisplay.getCoords(ele); wereadBridge.handleWithRichEditor("onGotoImageDetail", { "src": src, "index": index, "top": position.top, "height": position.height, "width": position.width, "left": position.left }, "", ""); } ``` **文本提取**: ```javascript RDisplay.getText = function() { // 过滤零宽连字符 // 将书籍替换为[书籍] // 将图片替换为[图片] return tempDom.innerText; } ``` **选择变化追踪**: ```javascript document.addEventListener("selectionchange", function(e) { RDisplay.contentChanged(e); }); RDisplay.contentChanged = function(e) { // 检测链接点击 if (nodeName == 'a') { param['link:'] = href; param['link-title'] = title; param['link-text'] = node.innerText; } wereadBridge.handleWithRichEditor("onSelectionChange", param, "", ""); } ``` **DOMContentLoaded通知**: ```javascript document.addEventListener("DOMContentLoaded", function(event) { wereadBridge.handleWithRichEditor("onDOMContentLoaded", "", "", ""); }); ``` ### 2.7 MediaPlatform.js - 公众号工具函数 **路径**: `output/resources/js/MediaPlatform.js` (126行) 公众号文章的实用工具函数。 **初始化**: ```javascript mediaPlatform.init = function() { // 更新空白高度 // 处理图片空白 // 保护懒加载 } ``` **信息提取**: ```javascript mediaPlatform.getAuthor = function() { // 为作者名称添加点击处理器 document.getElementById('js_name').addEventListener('click', ...); } mediaPlatform.getMPInfo = function() { // 提取标题、缩略图URL、公众号名称 return { title: title, thumbnailUrl: thumbnailUrl, accountName: accountName }; } ``` **媒体控制**: ```javascript pauseMedia() pauseCurrentAudio() pauseCurrentVideo() ``` ### 2.8 mpForArticle.js - 公众号内容调整 **路径**: `output/resources/js/mpForArticle.js` (349行) 公众号文章内容的调整和优化。 **DOMContentLoaded处理**: ```javascript document.addEventListener('DOMContentLoaded', function() { adjustMPContent(); }); ``` **内容调整**: ```javascript adjustMPContent = function() { // 1. 修复懒加载图片 (data-src → src) // 2. 转换OAuth2授权链接为重定向URI // 3. 设置视频iframe正确尺寸 // 4. 移除投票区域、小程序、小应用链接 // 5. 为创建音频播放器 } ``` **音频播放器模板**: ```javascript WRAUDIO_TEMPLATE = `
${title}
${duration}
`; createWRAudio = function(title, src, duration) { // 创建音频播放器DOM } ``` ### 2.9 mp_interceptor.js - 点击事件拦截器 **路径**: `output/resources/js/mp_interceptor.js` (74行) 公众号文章的点击事件拦截器。 **拦截目标**: ```javascript // 作者点击 document.getElementById('js_name').addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); wereadBridge.handleWithRichEditor("clickMpInterceptor", { contentType: "profile", data: authorData }, "", ""); }); // 版权信息点击 document.getElementById('copyright_info').addEventListener('click', function(e) { // ... }); // 公众号资料卡点击 document.querySelector('MP-COMMON-PROFILE').addEventListener('click', function(e) { // ... }); // 微信链接点击 document.querySelectorAll('a[href*="weixin.qq.com"]').forEach(function(el) { el.addEventListener('click', function(e) { wereadBridge.handleWithRichEditor("clickMpInterceptor", { contentType: "link", data: { href: el.href } }, "", ""); }); }); ``` ### 2.10 Readability.js - 文章内容提取 **路径**: `output/resources/js/Readability.js` (1775行) Arc90 Readability v1.7.1,用于从网页中提取可读内容。 **核心算法**: - 基于段落内容的评分系统 - 类名权重(article, story, content 等正面词 vs comment, meta 等负面词) - 链接密度分析 - 多页支持与自动翻页 **使用场景**: ```javascript // 从微信公众号文章中提取正文 var readable = new Readability(document); var article = readable.parse(); // article.content - 提取的HTML // article.title - 文章标题 // article.author - 作者 ``` ### 2.11 HtmlGenerator.js - HTML生成器 **路径**: `output/resources/js/HtmlGenerator.js` (27行) HTML生成工具,支持nonce注入。 ```javascript generator.generateHtml = function(content, js, css) { // 移除headTrap/tailTrap注释 // 提取nonce: window.__nonce_str // 注入JS和CSS return html; } generator.generateHtmlForMPReview = function(content) { // 特殊处理评论版权 } ``` ### 2.12 cssInjector.js - CSS注入器 **路径**: `output/resources/js/cssInjector.js` (25行) 动态CSS样式管理工具。 ```javascript window.cssInjector = { inject: function(json) { // 创建/更新