ReadViewSDK/Doc/WXRead/analysis/CSS_JS资源分析.md
2026-05-21 19:40:51 +08:00

35 KiB
Raw Blame History

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:blockarticle, 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属性:

/* 垂直居中样式 - 值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行)

纯黑背景的暗黑主题样式。

核心样式:

/* 所有元素强制黑色背景 */
* {
    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: boldreplace.css 中为 normal

特殊样式:

/* 打赏区域 */
.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行)

微信公众号文章的基础样式,包含标注系统。

主要内容区域:

.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)

复合标注样式:

/* 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从微信网页版复制。

核心特性:

/* 内容区域最大宽度限制 */
.rich_media_content * {
    max-width: 100% !important;
    box-sizing: border-box !important;
}

WeUI 对话框系统:

  • .weui-mask - 遮罩层
  • .weui-dialog - 对话框

音频播放器样式:

  • .audio_container - 音频容器
  • .audio_button - 播放按钮
  • .audio_progress - 进度条

其他组件:

  • Loading spinnerbase64 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 - 进度滑块

资源引用:

/* 使用腾讯CDN的雪碧图 */
background-image: url(https://rescdn.qqmail.com/weread/cover/icon/WREpubMPVideo.png);

广告隐藏:

/* 隐藏广告和应用下载提示 */
.tvp_ads_go, .tvp_app_download_onpause {
    display: none !important;
}

1.11 Style/style.css - 富文本编辑器样式

路径: output/resources/css/Style/style.css (408行)

富文本编辑器的基础样式。

编辑器模式:

/* 编辑模式 */
.re_Write {
    /* 可编辑状态样式 */
}

/* 显示模式 */
.re_Display {
    /* 只读状态样式 */
}

/* 夜间模式 */
.re_Night {
    color: rgba(180, 180, 182, 1);
    background-color: rgba(0, 0, 0, 1);
}

书籍卡片组件:

.re_bookItem {
    /* 书籍信息展示 */
}
.re_bookItem_cover {
    /* 封面图片 */
}
.re_bookItem_title {
    /* 书籍标题 */
}
.re_bookItem_author {
    /* 作者信息 */
}

图片处理:

.re_img {
    /* 图片样式 */
}
.bodyPic {
    /* 正文图片 */
}

高DPI边框处理:

/* 使用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行)

预览模式的特殊样式。

核心样式:

.re {
    font-size: 18px;
    line-height: 30px;
    font-family: -apple-system;
}

书籍卡片:

  • 使用 0.5px 边框技术retina 显示屏优化)

占位符文本:

.re_placeholder:before {
    content: attr(placeholder);
    color: #999;
}

引用块:

.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 类:

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) 处理来自原生的回调响应

消息格式:

{
    "func": "apiName",
    "params": {
        "key": "value",
        "callbackId": "1001"
    }
}

数据编码:

  • _utf8Encode(str) - UTF-8编码
  • _base64Encode(str) - Base64编码
  • 用于通过iframe URL传递数据

Ready机制:

// 检查原生是否已注入信息
if (window["__WRB_INFO__"]) {
    _onReady();
} else {
    window["__WRB_INFO_CALL__"] = function() {
        _onReady();
    };
}

全局实例:

window.wereadBridge = new WeReadBridge();

2.3 weread-highlighter.js - 高亮/标注引擎

路径: output/resources/js/weread-highlighter.js (1530行)

公众号文章的核心高亮和标注引擎,基于 Rangy 库构建。

初始化:

// 初始化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 可点击蓝色 用于书籍标题等

核心函数:

// 初始化标注从JSON加载
initAnnotations(items)

// 创建高亮
didTapCreateHighlightButton()

// 创建想法
didTapCreateReviewButton(start, end)

// 删除标注
didTapDeleteAnnotationButton(start, end, type)

// 点击处理
click(el, event)

TTS系统:

// 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)

书籍标题检测:

// 查找所有《》包围的书籍标题
findAllBookTitles()

// 高亮选中的书籍标题
highlightSelectedBookTitles(indexArray)

链接处理:

// 获取所有前向链接
getAllForwardLinks()

// 转换H5链接为原生数据属性
transferH5Link()

// 解密微信读书加密链接
WRLink_decrypt(str)
// type '3' = 数字
// 其他 = 十六进制编码文本

选择变化处理:

// 防抖处理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 类:

class CharacterRange {
    constructor(start, end) { ... }
    intersects(otherRange) { ... }
    isContiguousWith(otherRange) { ... }
    isContain(otherRange) { ... }
    union(otherRange) { ... }
    intersection(otherRange) { ... }
    getComplements(orderedRanges) { ... }
}

Highlighter 类:

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

class ClassApplier {
    constructor(className, options) { ... }
    applyToRange(range) { ... }
    undoToRange(range) { ... }
    applyToSelection(selection) { ... }
    undoToSelection(selection) { ... }
    isAppliedToRange(range) { ... }
    isAppliedToSelection(selection) { ... }
    toggleRange(range) { ... }
    toggleSelection(selection) { ... }
}

暴露的工具方法:

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

class Position {
    constructor(node, offset) { ... }
    // 字符类型检测
    get characterType() { ... }  // NON_SPACE, COLLAPSIBLE_SPACE, etc.
    get isImage() { ... }        // 图片文本支持
}

字符类型常量:

  • NON_SPACE - 非空格字符
  • COLLAPSIBLE_SPACE - 可折叠空格
  • NON_COLLAPSIBLE_SPACE - 不可折叠空格
  • IMAGE_TEXT - 图片文本

Range 扩展方法:

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 命名空间:

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() 失去焦点

数据分隔符:

RE.dataSeparator = 'r_e_ds';  // 用于连接多个值

内容变化通知:

RE.contentChanged = function(e) {
    // 通知原生格式状态变化
    wereadBridge.handleWithRichEditor("onSelectionChange", param, "", "");
}

EPUB HTML导出:

RE.getHtmlForEpub = function() {
    // 移除临时类并应用correctArticle
    return correctedHtml;
}

粘贴处理:

document.addEventListener('paste', function(e) {
    // 拦截粘贴,发送到原生处理
    wereadBridge.handleWithRichEditor("onPaste", {}, "", "");
});

2.6 rich_display.js - 富文本显示控制器

路径: output/resources/js/Text/rich_display.js (262行)

富文本内容的显示控制器,处理书籍和图片的交互。

书籍交互:

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}, "", "");
}

图片交互:

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
    }, "", "");
}

文本提取:

RDisplay.getText = function() {
    // 过滤零宽连字符
    // 将书籍替换为[书籍]
    // 将图片替换为[图片]
    return tempDom.innerText;
}

选择变化追踪:

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通知:

document.addEventListener("DOMContentLoaded", function(event) {
    wereadBridge.handleWithRichEditor("onDOMContentLoaded", "", "", "");
});

2.7 MediaPlatform.js - 公众号工具函数

路径: output/resources/js/MediaPlatform.js (126行)

公众号文章的实用工具函数。

初始化:

mediaPlatform.init = function() {
    // 更新空白高度
    // 处理图片空白
    // 保护懒加载
}

信息提取:

mediaPlatform.getAuthor = function() {
    // 为作者名称添加点击处理器
    document.getElementById('js_name').addEventListener('click', ...);
}

mediaPlatform.getMPInfo = function() {
    // 提取标题、缩略图URL、公众号名称
    return {
        title: title,
        thumbnailUrl: thumbnailUrl,
        accountName: accountName
    };
}

媒体控制:

pauseMedia()
pauseCurrentAudio()
pauseCurrentVideo()

2.8 mpForArticle.js - 公众号内容调整

路径: output/resources/js/mpForArticle.js (349行)

公众号文章内容的调整和优化。

DOMContentLoaded处理:

document.addEventListener('DOMContentLoaded', function() {
    adjustMPContent();
});

内容调整:

adjustMPContent = function() {
    // 1. 修复懒加载图片 (data-src → src)
    // 2. 转换OAuth2授权链接为重定向URI
    // 3. 设置视频iframe正确尺寸
    // 4. 移除投票区域、小程序、小应用链接
    // 5. 为<mpvoice>和<qqmusic>创建音频播放器
}

音频播放器模板:

WRAUDIO_TEMPLATE = `
<div class="audio_container" onclick="...">
    <div class="audio_button"></div>
    <div class="audio_info">
        <div class="audio_title">${title}</div>
        <div class="audio_duration">${duration}</div>
    </div>
    <div class="audio_progress"></div>
</div>
`;

createWRAudio = function(title, src, duration) {
    // 创建音频播放器DOM
}

2.9 mp_interceptor.js - 点击事件拦截器

路径: output/resources/js/mp_interceptor.js (74行)

公众号文章的点击事件拦截器。

拦截目标:

// 作者点击
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 等负面词)
  • 链接密度分析
  • 多页支持与自动翻页

使用场景:

// 从微信公众号文章中提取正文
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注入。

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样式管理工具。

window.cssInjector = {
    inject: function(json) {
        // 创建/更新<style>元素按ID
        var style = document.getElementById(json.id) || document.createElement('style');
        style.id = json.id;
        style.textContent = json.css;
        document.head.appendChild(style);
    },
    remove: function(identifier) {
        // 移除<style>元素按ID
        var style = document.getElementById(identifier);
        if (style) {
            style.parentNode.removeChild(style);
        }
    }
};

2.13 其他工具脚本

patch_6.2.1.4.js (7行)

补丁脚本,修复版权文本大小写:

// 将 "rights" 和 "reserved" 首字母大写
WRAboutViewController.viewDidLoad = function() {
    // "All rights reserved" → "All Rights Reserved"
}

tencent-news-weread-custom.js (15行)

腾讯新闻自定义脚本:

newsInjector.removeUnnecessary = function() {
    // 隐藏"查看全文"提示
    document.querySelectorAll('.detail-link').forEach(function(el) {
        el.style.display = 'none';
    });
}

InnerMP.js (27行)

内部公众号处理:

(function() {
    // 添加viewport meta标签
    var meta = document.createElement('meta');
    meta.name = 'viewport';
    meta.content = 'width=device-width, initial-scale=1.0';
    document.head.appendChild(meta);

    // 隐藏标题前的空段落
    // 隐藏第一个h1/h2/h3标题元素
})();

三、字体文件分析

3.1 字体清单

路径: output/resources/fonts/

字体文件 用途
SourceHanSerifCN-Medium.ttf 默认正文字体(思源宋体)
Lora-Regular.ttf Lora 衬线字体(常规)
Lora-Italic.ttf Lora 衬线字体(斜体)
PlayfairDisplay-Regular.ttf Playfair Display 字体
WeReadLS-Regular.otf WeRead LS 系列(常规)
WeReadLS-Medium.otf WeRead LS 系列(中等)
WeReadLS-Bold.otf WeRead LS 系列(粗体)
FZJuZhenXinFang.ttf 方正聚珍新仿字体
OpenDyslexic-Regular.otf 阅读障碍友好字体
WeRead-Icon.ttf WeRead 图标字体
WeRead-Rating-Icon.ttf WeRead 评分图标字体
WeReadRN-Regular.ttf WeRead RN 字体
WeChatNumber.ttf 微信数字字体
SharpGroteskTRIAL*.ttf Sharp Grotesk 系列
icon_font.ttf 通用图标字体
weread_iconfont.ttf WeRead 图标字体

3.2 字体使用模式

EPUB内容字体栈:

/* 标题和版权页 */
font-family: "SourceHanSerifCN-Medium";

/* 代码块 */
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;

/* 拉丁语书籍标题 */
/* 不设置font-family使用浏览器默认 */

/* 单图片标题 */
font-family: "FZFSJW--GB1-0";

公众号文章字体栈:

/* 主要内容 */
font-family: -apple-system;

/* 公众号章节 */
font-family: "Source Han Serif CN";

编辑器字体栈:

/* 预览模式 */
font-family: -apple-system;
font-size: 18px;
line-height: 30px;

四、关键发现与实现细节

4.1 微信读书自定义CSS属性

wr-vertical-center-style:

  • 1.wr-vertical-center 类,强制垂直居中
  • 2.bodyPic 类,正文图片垂直居中样式
  • 原生端通过解析此属性决定垂直对齐方式

weread-page-relate:

  • true:标记元素与页面相关
  • 用于分页计算时的元素识别

4.2 JS Bridge 通信机制

iframe URL方案:

// 发送消息
iframe.src = 'wereadapijs://dispatch_message/';

// 接收结果
resultIframe.src = 'wereadapijs://private/setresult/' + scene + '&' + encodedData;

消息队列:

  • 所有消息先进入 _sendMessageQueue
  • 原生端通过 fetchQueue() 批量获取
  • 每个消息带有唯一 callbackId 用于回调匹配

数据编码:

  • UTF-8编码 → Base64编码 → URL拼接
  • 确保中文等特殊字符正确传输

4.3 高亮系统架构

基于Rangy的扩展:

  1. rangy-core.js - 核心范围操作
  2. rangy-textrange.js - 字符级范围操作(支持图片文本)
  3. rangy-classapplier.js - CSS类应用
  4. rangy-highlighter.js - 高亮管理(序列化/反序列化)
  5. weread-highlighter.js - 微信读书业务逻辑

标注持久化:

// 序列化格式
"type:TextRange|start$end$id$className[|$containerId]"

// 反序列化
highlighter.deserialize(serializedString);

4.4 TTS实现

文本提取:

getTTSText = function(startY) {
    // 从Y坐标位置开始遍历DOM
    // 提取文本节点
    // 返回TTS文本和位置信息
}

渐进高亮:

highlightTTSText = function(text, index, startY) {
    // 移除之前的TTS高亮
    // 根据index计算新的高亮位置
    // 应用tts类
}

4.5 富文本编辑器设计

基于contentEditable:

  • 使用 document.execCommand 执行格式化命令
  • 通过 backuprange/restorerange 保持选择状态
  • 粘贴拦截 → 发送到原生处理

书籍/图片插入:

// 插入书籍卡片
RE.insertBook = function(id, title, author, cover) {
    var html = `<div class="re_bookItem" data-id="${id}">
        <img class="re_bookItem_cover" src="${cover}"/>
        <div class="re_bookItem_title">${title}</div>
        <div class="re_bookItem_author">${author}</div>
    </div>`;
    RE.insertHTML(html);
}

4.6 暗黑主题实现

全局覆盖策略:

/* 所有元素强制黑色背景 */
* {
    background-color: rgba(0, 0, 0, 1) !important;
}

特殊元素处理:

  • 业务容器使用 rgb(28, 28, 29) 背景
  • 文字颜色统一为 rgba(180, 180, 182, 1)
  • 链接颜色为 rgba(84, 127, 176, 1)

4.7 公众号文章处理流程

1. 加载HTML内容
2. MediaPlatform.init() - 初始化
3. adjustMPContent() - 内容调整
   ├── 修复懒加载图片
   ├── 转换OAuth链接
   ├── 设置视频尺寸
   ├── 移除不需要的元素
   └── 创建音频播放器
4. mp_interceptor.js - 设置点击拦截
5. weread-highlighter.js - 初始化标注系统
6. Readability.js - 可选的内容提取

4.8 代码高亮

使用 highlight.js 的 xcode 主题:

  • 白色背景
  • 彩色语法标记
  • 支持多种编程语言

4.9 视频播放器定制

腾讯视频覆盖:

  • 自定义控制栏样式
  • 隐藏广告和应用下载提示
  • 使用腾讯CDN雪碧图
  • 设备特定适配iPhone6/6P

五、依赖关系图

┌─────────────────────────────────────────────────────────────┐
│                      Native App (iOS)                        │
│                                                             │
│  ┌───────────────────────────────────────────────────────┐  │
│  │                    WKWebView                          │  │
│  │                                                       │  │
│  │  ┌─────────────────────────────────────────────────┐  │  │
│  │  │              WeReadApi.js (Bridge)              │  │  │
│  │  │         wereadapijs:// protocol                 │  │  │
│  │  └─────────────────────────────────────────────────┘  │  │
│  │                         │                             │  │
│  │         ┌───────────────┼───────────────┐             │  │
│  │         │               │               │             │  │
│  │         ▼               ▼               ▼             │  │
│  │  ┌────────────┐  ┌────────────┐  ┌────────────┐      │  │
│  │  │ EPUB渲染   │  │ 公众号渲染  │  │ 富文本编辑  │      │  │
│  │  │            │  │            │  │            │      │  │
│  │  │ replace.css│  │ MediaPlat  │  │ rich_editor│      │  │
│  │  │ default.css│  │ form.css   │  │ .js        │      │  │
│  │  │ dark.css   │  │ MPExtra.css│  │ rich_dis   │      │  │
│  │  │            │  │            │  │ play.js    │      │  │
│  │  │            │  │ highlighter│  │            │      │  │
│  │  │            │  │ .js        │  │            │      │  │
│  │  └────────────┘  └────────────┘  └────────────┘      │  │
│  │                                                       │  │
│  │  ┌─────────────────────────────────────────────────┐  │  │
│  │  │              Rangy Library                      │  │  │
│  │  │  core → textrange → classapplier → highlighter  │  │  │
│  │  └─────────────────────────────────────────────────┘  │  │
│  │                                                       │  │
│  └───────────────────────────────────────────────────────┘  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

六、总结

微信读书的前端资源架构体现了以下设计特点:

  1. 分层样式系统:通过 default.css → replace.css/MediaPlatform.css → dark.css 的层叠,实现内容类型和主题的灵活切换

  2. 原生桥接机制WeReadApi.js 通过 iframe URL scheme 实现 JS 与原生的高效通信,支持异步回调

  3. 扩展的Rangy库:在标准 Rangy 基础上添加了图片文本支持、高亮合并、序列化等微信读书特有功能

  4. 完整的标注系统支持高亮、想法、好友想法、引用、TTS五种标注类型每种有独立的样式和交互

  5. 富文本编辑能力rich_editor.js 提供了完整的编辑功能,包括书籍/图片插入、格式化、链接管理等

  6. 公众号文章优化:通过 Readability.js 提取内容、mpForArticle.js 调整布局、mp_interceptor.js 拦截交互,实现公众号文章的原生体验

  7. 多字体支持提供思源宋体、Lora、Playfair Display等多种字体选择支持阅读障碍友好字体

  8. 性能优化选择变化防抖250ms、懒加载图片修复、iframe消息队列批量处理等


分析完成时间: 2026年5月18日 分析文件数量: 25个23个完整分析2个因过大无法读取