从零编写油猴脚本:定制微信读书网页版阅读体验

发布时间:2026/8/14 3:16:55
从零编写油猴脚本:定制微信读书网页版阅读体验 1. 项目缘起一个阅读者的朴素需求作为一名重度阅读爱好者我几乎每天都会花上几个小时泡在微信读书里。它的网页版是我在电脑前工作学习时的主要阅读工具毕竟大屏幕看起来更舒服查资料、做笔记也更方便。但用久了一些体验上的“小疙瘩”就越来越明显。比如那个默认的阅读背景色长时间盯着看眼睛容易疲劳又比如我想快速调整一下行间距或者字体得点好几下鼠标才能找到设置项最让我头疼的是网页版似乎没有提供一个纯粹的、无干扰的“沉浸模式”侧边栏、顶部的工具栏总是杵在那里分散注意力。这些问题说大不大但累积起来确实影响阅读的专注度和舒适度。我寻思着既然微信读书官方没有提供更细致的自定义选项那我能不能自己动手改改作为一个有点技术背景的用户我第一时间想到了油猴脚本。这玩意儿就像一个给浏览器开的“外挂”能让我们在访问特定网站时注入自己的JavaScript代码从而修改网页的样式、增加或删除功能。它不需要你懂后端、搞服务器所有操作都在本地浏览器完成安全又灵活。于是“自己动手写一个油猴脚本来优化微信读书网页版的阅读体验”这个想法就落地了。目标很明确不搞复杂的功能就针对我感受到的几个痛点做几个简单但有效的优化比如一键切换护眼背景、微调排版、简化界面。下面我就把这次从零开始构思、编写到调试的完整过程拆解出来哪怕你只有一点点前端基础跟着做也能拥有属于自己的定制化阅读环境。2. 油猴脚本与微信读书网页版技术栈初探在动手写代码之前我们得先搞清楚两件事油猴脚本能做什么以及微信读书网页版是怎么构成的。2.1 油猴脚本浏览器端的“手术刀”油猴官方名称是Tampermonkey它是一个浏览器扩展。它的核心能力是允许用户编写并在特定网站加载时自动运行的JavaScript脚本。你可以把它理解为一个高度定制化的、针对单个或一批网站的自动化工具。它的工作原理是“注入”当浏览器访问匹配脚本中定义的网址规则的页面时Tampermonkey会将你的脚本代码插入到该页面的上下文中执行。这意味着你的脚本可以操作DOM查找、修改、删除或新增网页上的任何HTML元素。这是我们优化样式、隐藏元素的基础。监听事件响应用户的点击、滚动、键盘输入等操作实现交互功能。修改样式通过动态添加CSS规则彻底改变页面的外观。劫持与修改请求可以拦截网页发出的网络请求修改其参数或返回值但这属于进阶功能本次不涉及。一个最基本的油猴脚本包含两部分元数据块和执行代码。元数据块定义了脚本的名称、描述、匹配的网站、权限等信息用特定的注释格式包裹。执行代码就是普通的JavaScript在页面加载后运行。2.2. 微信读书网页版结构浅析要修改它我们得先看看它的“骨架”。打开微信读书网页版按F12打开开发者工具。整体布局通常阅读区域被包裹在一个具有特定类名或ID的容器div内。工具栏、目录、侧边栏等元素是它的兄弟或子节点。样式控制文字大小、行高、背景色、字体等几乎都是通过CSS类来控制的。微信读书本身提供了几个主题如白天、护眼、夜间切换主题本质上是给body或阅读容器切换不同的CSS类名。关键元素我们需要找到以下几个关键元素的选择器阅读主体区域存放正文文本的容器。它的选择器可能像.reader_content、#readerContainer或更复杂的嵌套路径。工具栏顶部或底部包含字体调整、目录、主题切换按钮的条状区域。侧边栏可能包含笔记、想法、目录展开面板的区域。我们的脚本工作流将是等待页面核心元素加载完成 → 使用JavaScript查询document.querySelector定位这些关键元素 → 通过修改其样式element.style或类名element.classList来实现视觉改造。3. 脚本设计与核心功能规划基于“简单优化”的定位我决定先实现三个最迫切、最易实现的功能点确保脚本轻量、有效且互不干扰。3.1 功能一一键切换柔和护眼背景微信读书自带的“护眼”模式是偏绿色的但我个人更喜欢一种更柔和的、类似羊皮纸或浅鹅黄的色调这种色调在长时间阅读时能进一步减轻对比度带来的刺激。实现思路不依赖微信读书自带的主题切换而是直接向页面head部分注入一段style标签定义我们自己的背景色和文字颜色。同时在页面某个角落比如右下角添加一个固定的、不显眼的切换按钮。点击按钮就在body或阅读容器上切换一个自定义的类名例如.custom-eye-care从而应用我们定义的样式。技术要点CSS注入使用GM_addStyle函数油猴API或创建style节点并插入document.head。前者更简洁。按钮创建与定位用document.createElement创建按钮元素设置其样式position: fixed; bottom: 20px; right: 20px;等并添加到document.body。事件监听为按钮添加click事件监听器在事件处理函数中对阅读容器执行classList.toggle(custom-eye-care)。3.2 功能二自定义阅读区域排版默认的排版可能不适合所有人。我希望能够微调行高、段间距和最大宽度让文本布局更符合我的阅读习惯。实现思路同样通过注入CSS来实现。我们可以定义一组CSS变量或具体的规则应用到阅读主体区域。为了更灵活可以设计两到三套预设排版方案如“紧凑”、“舒适”、“宽松”通过另一个按钮或下拉菜单进行切换。技术要点CSS变量在自定义样式里使用CSS自定义属性变量如--line-height: 1.8; --max-width: 800px;。这样只需修改变量值就能整体改变排版。方案切换类似于背景切换通过切换阅读容器的另一个类名如.layout-comfortable来应用不同的CSS规则。样式优先级需要注意我们注入的CSS选择器的优先级要确保能覆盖微信读书原有的样式。通常可以通过增加选择器的特异性如body.reader .reader_content或使用!important声明谨慎使用来实现。3.3 功能三沉浸式阅读模式简化界面目标是隐藏所有与当前阅读无关的UI元素只留下最纯粹的文本内容。实现思路识别并隐藏工具栏、侧边栏、广告、页脚等元素。同时可以考虑让阅读区域居中并适当放大。技术要点元素选择利用开发者工具仔细找出需要隐藏的各个元素的稳定选择器。注意有些元素可能在页面滚动或交互时动态出现/消失需要选择其最外层的持久容器。隐藏方式直接设置element.style.display none是最彻底的方式。也可以设置element.style.opacity 0或visibility hidden但前者通常更干净。触发方式可以为这个功能绑定一个快捷键例如按F11这是沉浸模式的常见交互。油猴脚本支持监听键盘事件document.addEventListener(keydown, ...)。4. 分步实操从零编写你的第一个优化脚本下面我们一步步将上述设计转化为真实的代码。请先在浏览器中安装好Tampermonkey扩展。4.1 创建新脚本与编写元数据点击Tampermonkey图标选择“创建新脚本”。你会看到一个预设好的模板。我们修改其元数据部分// UserScript // name 微信读书网页版阅读体验优化 // namespace http://tampermonkey.net/ // version 1.0.0 // description 自定义背景、排版与沉浸模式提升微信读书网页版阅读体验。 // author YourName // match https://weread.qq.com/* // grant GM_addStyle // grant GM_notification // license MIT // /UserScript (function() { use strict; // 你的代码将写在这里 })();关键参数解析match https://weread.qq.com/*这行至关重要它指定脚本只在微信读书域名下运行。*是通配符。grant GM_addStyle声明需要使用油猴的GM_addStyleAPI来安全地添加CSS样式。grant GM_notification声明权限可用于脚本操作后给用户一个简单的提示可选。整个脚本被包裹在一个立即执行的函数表达式(function(){...})()中并使用‘use strict’;模式这是为了避免污染全局作用域是编写油猴脚本的最佳实践。4.2 实现柔和护眼背景切换在元数据块之后的函数内部我们开始编写功能代码。// 1. 定义并注入自定义护眼样式 GM_addStyle( .custom-eye-care { background-color: #f7f3e9 !important; /* 浅鹅黄背景 */ color: #5c4b37 !important; /* 深棕色文字 */ } .custom-eye-care .reader_content, /* 根据实际选择器调整 */ .custom-eye-care .app_content { background-color: inherit !important; color: inherit !important; } ); // 2. 创建切换按钮 function createBgToggleButton() { // 等待页面主体加载完成 if (!document.body) { setTimeout(createBgToggleButton, 100); return; } // 防止重复添加按钮 if (document.getElementById(wr-custom-bg-toggle)) { return; } const toggleBtn document.createElement(button); toggleBtn.id wr-custom-bg-toggle; toggleBtn.innerHTML ; // 使用一个简单的图标 toggleBtn.title 切换柔和护眼背景; // 设置按钮样式 Object.assign(toggleBtn.style, { position: fixed, bottom: 80px, right: 20px, zIndex: 9999, width: 40px, height: 40px, borderRadius: 50%, border: 1px solid #ddd, backgroundColor: #fff, cursor: pointer, fontSize: 18px, boxShadow: 0 2px 5px rgba(0,0,0,0.2), opacity: 0.7, transition: opacity 0.3s }); toggleBtn.onmouseenter () toggleBtn.style.opacity 1; toggleBtn.onmouseleave () toggleBtn.style.opacity 0.7; // 点击事件切换类名 toggleBtn.addEventListener(click, () { document.body.classList.toggle(custom-eye-care); // 可选保存状态到 localStorage下次进入页面时自动应用 const isActive document.body.classList.contains(custom-eye-care); localStorage.setItem(wr-eye-care, isActive); // 可选给出简单反馈 toggleBtn.innerHTML isActive ? ☀️ : ; }); // 将按钮添加到页面 document.body.appendChild(toggleBtn); // 页面加载时检查本地存储并恢复状态 if (localStorage.getItem(wr-eye-care) true) { // 稍等片刻确保样式已注入再添加类 setTimeout(() document.body.classList.add(custom-eye-care), 300); toggleBtn.innerHTML ☀️; } } // 启动创建按钮的函数 window.addEventListener(load, createBgToggleButton); // 同时监听DOMContentLoaded确保在单页应用SPA路由切换时也能运行 document.addEventListener(DOMContentLoaded, createBgToggleButton);注意上述代码中的.reader_content和.app_content是示例选择器你需要使用开发者工具检查微信读书网页版的实际HTML结构替换成正确的选择器。这是脚本能否生效的关键。4.3 实现自定义排版方案接下来我们在同一个脚本里添加排版功能。为了更清晰我们将排版定义为几套预设。// 3. 定义并注入排版样式 GM_addStyle( /* 紧凑型 */ .layout-compact .wr-reader { line-height: 1.5 !important; max-width: 700px !important; margin: 0 auto !important; } .layout-compact .wr-reader p { margin-bottom: 0.8em !important; } /* 舒适型 (默认推荐) */ .layout-comfortable .wr-reader { line-height: 1.8 !important; max-width: 800px !important; margin: 0 auto !important; letter-spacing: 0.02em !important; } .layout-comfortable .wr-reader p { margin-bottom: 1.2em !important; } /* 宽松型 */ .layout-spacious .wr-reader { line-height: 2.0 !important; max-width: 900px !important; margin: 0 auto !important; letter-spacing: 0.03em !important; } .layout-spacious .wr-reader p { margin-bottom: 1.5em !important; } ); // 4. 创建排版切换控件这里用一个简单的下拉菜单示例 function createLayoutSelector() { if (!document.body || document.getElementById(wr-layout-select)) return; const container document.createElement(div); container.id wr-layout-select; container.title 选择排版方案; Object.assign(container.style, { position: fixed, bottom: 130px, // 放在背景切换按钮上方 right: 20px, zIndex: 9999, backgroundColor: #fff, border: 1px solid #ddd, borderRadius: 4px, padding: 5px, fontSize: 12px, opacity: 0.7, transition: opacity 0.3s }); container.onmouseenter () container.style.opacity 1; container.onmouseleave () container.style.opacity 0.7; const select document.createElement(select); [默认, 紧凑, 舒适, 宽松].forEach((text, index) { const option document.createElement(option); option.value [default, compact, comfortable, spacious][index]; option.textContent text; select.appendChild(option); }); // 读取保存的偏好 const savedLayout localStorage.getItem(wr-layout) || comfortable; select.value savedLayout; if (savedLayout ! default) { document.body.classList.add(layout-${savedLayout}); } select.addEventListener(change, (e) { // 移除所有排版类 document.body.classList.remove(layout-compact, layout-comfortable, layout-spacious); const value e.target.value; if (value ! default) { document.body.classList.add(layout-${value}); } localStorage.setItem(wr-layout, value); }); container.appendChild(select); document.body.appendChild(container); } // 将排版控件创建也加入到页面加载监听中 const initUI () { createBgToggleButton(); createLayoutSelector(); }; window.addEventListener(load, initUI); document.addEventListener(DOMContentLoaded, initUI);4.4 实现沉浸式阅读模式快捷键触发最后我们添加通过按F11键或自定义其他键来触发沉浸模式的功能。// 5. 沉浸模式功能 function toggleImmersiveMode() { // 这里需要你根据实际页面结构找到需要隐藏的元素的选择器 const elementsToHide [ .wr-header, // 顶部头部示例 .wr-footer, // 底部示例 .wr-sidebar, // 侧边栏示例 .reader_toolbar, // 阅读工具栏示例 // 添加更多你需要隐藏的元素选择器 ]; const isImmersive document.body.classList.toggle(immersive-mode); if (isImmersive) { // 进入沉浸模式隐藏元素并可能扩大阅读区域 elementsToHide.forEach(selector { const el document.querySelector(selector); if (el) { el.dataset.originalDisplay el.style.display; // 保存原始display值 el.style.display none; } }); // 可选让阅读区域居中并变宽 const readerMain document.querySelector(.wr-reader); // 替换为实际选择器 if (readerMain) { readerMain.dataset.originalMaxWidth readerMain.style.maxWidth; readerMain.style.maxWidth 95%; readerMain.style.margin 0 auto; } console.log(沉浸模式已开启); } else { // 退出沉浸模式恢复元素显示 elementsToHide.forEach(selector { const el document.querySelector(selector); if (el el.dataset.originalDisplay ! undefined) { el.style.display el.dataset.originalDisplay; } }); // 恢复阅读区域样式 const readerMain document.querySelector(.wr-reader); if (readerMain readerMain.dataset.originalMaxWidth ! undefined) { readerMain.style.maxWidth readerMain.dataset.originalMaxWidth; } console.log(沉浸模式已关闭); } } // 6. 监听键盘事件绑定F11键 document.addEventListener(keydown, function(event) { // 注意F11键本身是浏览器全屏快捷键这里我们使用CtrlF11或AltF11避免冲突 if (event.key F11 (event.ctrlKey || event.altKey)) { event.preventDefault(); // 阻止浏览器默认行为如全屏 toggleImmersiveMode(); } }); // 7. 在页面上添加一个简单的沉浸模式状态提示可选 function addImmersiveStatusIndicator() { const indicator document.createElement(div); indicator.id wr-immersive-indicator; indicator.innerHTML 沉浸模式 (CtrlF11); Object.assign(indicator.style, { position: fixed, top: 10px, right: 10px, backgroundColor: rgba(0,0,0,0.7), color: #fff, padding: 5px 10px, borderRadius: 3px, fontSize: 12px, zIndex: 10000, display: none // 默认隐藏 }); document.body.appendChild(indicator); // 监听模式变化更新指示器 const observer new MutationObserver(() { indicator.style.display document.body.classList.contains(immersive-mode) ? block : none; }); observer.observe(document.body, { attributes: true, attributeFilter: [class] }); } // 在UI初始化中加入 const initUIWithImmersive () { createBgToggleButton(); createLayoutSelector(); addImmersiveStatusIndicator(); }; window.addEventListener(load, initUIWithImmersive); document.addEventListener(DOMContentLoaded, initUIWithImmersive);5. 调试、适配与问题排查实录脚本写好了但第一次运行往往不会一帆风顺。下面是我在开发过程中遇到的一些典型问题及解决方法。5.1 选择器失效页面结构动态变化微信读书网页版是单页应用SPA当你切换书籍或章节时页面内容是通过JavaScript动态替换的而非整个页面刷新。这导致我们之前在window.onload或DOMContentLoaded事件中添加的按钮在路由切换后可能失效因为新内容加载后旧按钮可能被移除或不在新DOM树中。解决方案使用MutationObserverAPI来监听DOM变化。// 通用函数当特定元素出现时执行回调 function waitForElement(selector, callback) { if (document.querySelector(selector)) { callback(); return; } const observer new MutationObserver(() { if (document.querySelector(selector)) { observer.disconnect(); callback(); } }); observer.observe(document.body, { childList: true, subtree: true }); } // 使用示例等待阅读容器出现后再添加我们的按钮 waitForElement(.wr-reader, () { // 确保按钮只添加一次 if (!document.getElementById(wr-custom-bg-toggle)) { createBgToggleButton(); } if (!document.getElementById(wr-layout-select)) { createLayoutSelector(); } });将主要的UI初始化逻辑放在这样的观察器中可以确保在页面动态加载后我们的功能依然能挂载上去。5.2 样式覆盖不生效CSS优先级问题有时候我们添加的CSS规则被微信读书原有的样式覆盖了。排查与解决在开发者工具的“元素”面板中找到目标元素查看“样式”选项卡。可以看到所有应用在该元素上的CSS规则以及哪些被覆盖有删除线。提高我们规则的选择器特异性。例如如果微信读书的规则是.reader-content { color: black; }我们可以写成body .reader-content { color: brown !important; }。增加父级选择器或使用!important。谨慎使用!important虽然它能强制覆盖但滥用会导致后续维护困难。优先通过增加选择器特异性来解决问题。确保我们的样式表在微信读书的样式表之后加载。油猴的GM_addStyle通常能保证这一点。5.3 脚本冲突与性能如果你安装了多个针对微信读书的油猴脚本或者脚本本身写得不够高效可能会引起冲突或页面卡顿。最佳实践命名隔离所有我们创建的元素的ID、类名都使用独特的前缀如wr-避免与其他脚本冲突。事件委托如果可能将事件监听器绑定在稳定的父元素上而不是动态创建的子元素上以减少监听器数量并适应动态内容。防抖与节流如果脚本需要监听滚动、窗口大小改变等频繁触发的事件务必使用防抖或节流函数来优化性能。清理观察器使用MutationObserver后在适当的时候如脚本禁用时调用observer.disconnect()来清理。5.4 微信读书更新导致脚本失效这是用户脚本的常见风险。微信读书前端一旦更新类名或HTML结构可能改变导致选择器失效。应对策略使用更稳健的选择器优先选择具有稳定ID的元素或者通过属性选择器如[data-testidreader-content]来定位。观察哪些类名是功能性的如reader-toolbar哪些是样式或哈希值如css-1a2b3c后者极易变化。备用选择器在代码中提供一组备选选择器依次尝试。function getReaderElement() { const selectors [.wr-reader, .reader-main, #readerContainer]; for (const sel of selectors) { const el document.querySelector(sel); if (el) return el; } return null; }关注与更新定期检查脚本是否工作。如果失效需要重新使用开发者工具分析新的页面结构更新选择器。6. 进阶思路与扩展可能性完成基础优化后你可以根据个人需求继续扩展脚本的功能让它变得更加强大和贴心。6.1 集成更多自定义选项可以创建一个统一的设置面板将所有功能背景色、排版方案、隐藏元素列表等集中管理并提供实时预览。这需要更复杂的UI构建和状态管理可以尝试使用一些轻量级的UI库或直接操作DOM。6.2 增强阅读辅助功能阅读进度增强在页面角落显示更精确的章节进度、阅读时间预估。自定义快捷键除了沉浸模式可以绑定快捷键来快速调整字体大小、跳转章节。文本选择增强修改选中文本的背景色或添加一键复制选中文本并自动添加引用信息的功能。6.3 数据同步与备份目前我们将偏好保存在localStorage中但这仅限于当前浏览器。可以考虑使用油猴的GM_setValue和GM_getValueAPI进行存储或者如果涉及笔记导出等可以集成浏览器的下载API。6.4 脚本发布与分享如果你觉得自己的脚本对他人也有帮助可以将其发布到Greasy Fork等用户脚本共享平台。在发布前请务必完善元数据描述、更新日志。添加详细的配置说明。确保代码整洁移除调试用的console.log。遵守平台和微信读书的相关使用条款。编写油猴脚本优化微信读书本质上是一次“夺回控制权”的实践。它不需要高深的全栈知识只需要一点前端基础、耐心观察和解决问题的热情。这个过程不仅能让你获得一个完全符合个人口味的阅读环境更能深刻理解前端技术如何与具体的网页应用交互。当你再次遇到其他网站上有让你不爽的小细节时你可能会会心一笑然后自信地打开Tampermonkey的管理面板。

相关新闻