-
拖了很久想做的事,在今天开始落地了,从今天开始,从这篇文章开始
起因
-
事情的起因还得从一只蝙蝠说起(就是说在疫情前的时候),微信公众号的文章页面背景颜色改为识别系统主题色变化,之前一直是白色的,这导致习惯白底黑字的我甚是不适(生理上都不适了)
-
然后在知道原因后,我去找解决方案
- 原因是微信做了媒体查询,通过 prefers-color-scheme 适配了系统主题色
- 为啥要找解决方案呢,因为我喜欢深色的系统主题色,但不喜欢深色的网页适配,我既要也要
- 对比看下设置和导致的效果(随便找的公众号文章)
-
在有以上原因后我便找寻到了答案,答案就是系统主题色不变,单独设置某个应用的主题色
-
贴下文章里提到的 Gray 这个软件的 github 地址
- 看最近一次更新已经是一年前了
- 下载体验了下确实比控制台里敲代码方便,ui 界面也还不错
- 但是有个问题就是滚动很生硬不流畅的感觉,不知道为何会这样,很影响用户体验
-
但我还是觉得不爽,我想要有些网页保持深色,有些浅色(比如微信公众号),属实是既要还要更要了
- 强迫症的我忍不了于是我自己写了个油猴脚本改成了我想要的方式,且留下评论给他人使用
- 评论看上面文章里的第一个评论就是我
-
结果就是大家都挺喜欢这个,从疫情开始到结束不断有人回复表示感谢(评论确实都是感谢的哈哈),我自己也用的很爽
-
但是最近发现适配有些问题(有人留言反馈了),其他一些网站的页面的适配也有问题了,一些网站也无法切换主题,于是我想要彻底解决这些个问题
为什么要开发一个扩展
-
之前一个油猴脚本就能做到的事,再修修改改不就行了(又不是不能用)
-
原因有以下几点
- 首先油猴脚本的开发调试不方便,除申请权限外还有一些限制
- 还有就是需要获取到外链 css 样式文件,导致初次使用会弹框提醒“一个用户脚本试图访问跨源资源“,每有新的域名都需要用户点击确认加入白名单一次,这个是油猴脚本的权限提示,总之就是体验很差
-
基于以上问题,自己也忍了三年了,刚好最近也出新问题了,是时候一劳永逸的解决了
-
于是正如标题所言,我决定自己开发一个扩展去完美解决此问题,要完美(重点)
前置调研
开发前准备
-
技能储备
- 作为一个前端开发,我会开发个扩展很正常吧
-
Chrome 应用市场调研
-
我搜索关键字主题切换,没有找到相关的,说明没人去做,不算重复造轮子,且中英文搜索都没有(难道大家都没有此需求吗。)
-
发布应用,我之前刚好开发过扩展,因此对发布流程还是了解的
-
但是需要准备 5 美元才能发布到扩展应用市场
-
-
扩展开发调研
-
虽然我之前开发过,但有段时间了,之前的代码能跑,但是已经看不懂了(这就是我要重构的借口)
-
不过我还是跑了下,然后不出所料的一堆报错(由于一些其他原因,正常一般锁了依赖版本应该是不会的)
-
简单修复了下跑起来了,但是但是但是,开发体验很差,之前的开发是基于 chrome 扩展 v2 的构建工具,现在只能开发 v3 了,导致开发调试时的热更新不是很好用,这不能忍啊
-
于是我又重复开启了之前开发扩展时的调研工作,不同的是这次我有做记录,下次再开发就能复用成果
-
搜索扩展热更新相关文章,看看大家的思路
-
首先我之前开发是基于我以前公司同事(虽然从来没见过)的模板代码开发的,这次去扫一眼
-
贴下文章使用 webpack 构建 chrome 扩展的热更新问题,里面提到的模版项目地址
- 然后又看了模版项目,最近的一次是6个月之前,有个 manifest-v3 分支来支持 v3 版本
-
-
之前看到的还有这个文章如何实现 chrome extension 的热更新,提到的项目地址
- 看了下此项目升级到了 v3,但最后一次提交已经是去年了,还是有点旧了
-
再看看新文章,一番搜索我在 stackoverflow 上看到了一个回答,里面提到了 crxjs 这个开源库,官网地址,这里作为 vite 插件来支持扩展的开发
- 为什么是 vite,因为我加了 vite 的关键字去搜索,vite 之前用了一下,开发体验真的很快,比 webpack 好太多了,而且目前 vite 也比较成熟了,可以用于生产环境
-
-
有了以上搜索到的情报后我停止了继续搜索,对比了下后,打算使用 vite 来进行开发扩展,开发体验应该会好很多,且 vite 对于 vue 支持更好,我选择使用 vue
-
作为一个 react 人,会使用 vue 很正常吧
-
开发过程
项目搭建
-
参考 crxjs 官网 vue 方式 创建项目,vite 3 支持还在测试阶段,所以还是使用 vite2 的方式创建
-
官方给出的命令是
npm init vite@^2.9.4,但我测试发现是不能加^符号泛指一个版本的,要去掉具体到某一个版本才可以,或者使用npm init vite@2.x这样也会找大版本下的最新一个小版本 -
后续:一番测试后我发现在 zsh 的 shell 环境下执行才会报错,bash shell 就不会报错,结合看报错原因:
zsh: no matches found: vite@^2.9.4,-
搜索后找到原因是因为 zsh 会自动解释 ^ 字符,不会传给 npm init 去解释,解决办法为 ~/.zshrc 配置中加入
setopt no_nomatch,然后source ~/.zshrc一下再运行就不报错了 -
之前我貌似也有类似的问题都没去解决,这次刚好学习了,顺便更新我的 zsh 配置文件
-
-
-
继续参考配置一番修改后跑起来了,开发体验很不错,过程也没踩什么坑(那是因为坑都在后面)
功能实现
-
首先先调研如何切换网站的样式,这里分 css 和 js,css 通过
@media (prefers-color-scheme: dark)来匹配修改样式,js 则通过window.matchMedia('(prefers-color-scheme: dark)')来查询当前主题模式,因此要操作 css 的媒体查询和重写 matchMedia 方法来达到可以切换样式 -
之前油猴脚本的逻辑是通过 document.styleSheets 来读取所有的样式规则,然后将媒体查询到的深色/浅色规则使用 deleteRule/insertRule 来删除/添加即可切换
-
但是这里有2个问题会导致切换失败
CORS 导致的问题
- 浏览器里的 CORS 规则会限制外部资源的读取,即非同源的资源引用可以,但用脚本读取不行
- 所以当 document.styleSheets 里读取 link 标签里的样式时,如果 link 标签未添加 crossorigin 属性,则会报错
[Exception: DOMException: Failed to read the 'cssRules' property from 'CSSStyleSheet': Cannot access rules]
-
这里之前我是直接走 fetch 请求 link 资源(所以会有上面油猴脚本的提示),然后读取 css 文件纯文本,再转成 style 标签插入,这样就可以规避掉 CORS 的问题
-
但再请求一次资源然后还要操作 dom 太浪费了,这次我使用给所有没有 crossorigin 的 link 标签添加此属性重新插入就行了,但是如果资源本身不支持跨域那还是不行,但是这个问题不大,一般网站的资源都是支持本网站跨域的
CSS 变量
- 当在媒体查询里设置 css 的变量值时,即使删除掉此规则,css 变量依旧是修改后的值,不会恢复到之前的值,这一点让我百思不得其解,去网上也没查询到什么信息
body {
--bg: white;
color: white;
}
@media (prefers-color-scheme: dark) {
body {
--bg: black;
color: black;
}
}
/*
这里如果删除上面的媒体查询规则,--bg 变量值依然会是 black,但 color 会恢复为 white
*/
-
这也是我之前写的油猴脚本失败的原因,微信公众号的样式之前是改样式,现在部分改成使用 css 变量了所以不行了
-
这里思考可能如同代码的变量赋值操作一样,媒体查询匹配,css 变量被修改,此时即使删除规则,css 变量计算不会保存之前的值,所以没法恢复,但样式会恢复是因为样式是叠加合并计算的,不是想变量直接赋值修改,当然这些是我猜测的
解决问题
-
以上第一个问题好解决,关键是第二个问题,有 css 变量的情况下基本是用不了之前的方案了,试了下只有将整个 link/style 标签删除才行
-
于是我打算读取所有的样式文件,然后将媒体查询主题色的样式规则给挑出来,且分为白色主题样式和黑色主题样式,其他样式原封不动新建一个 style 标签按顺序插入(防止样式覆写问题),然后删除原标签,最后再插入一个 style 标签放入匹配当前的主题模式的样式,切换时仅操作此主题样式标签即可
-
代码验证后没问题,算是解决了,由于保证了顺序也不会有网站样式错乱
其他问题
-
此外有些网站还会动态插入 link/style 标签(说的就是微信公众号网页),因此还需要监听新的标签插入,这里用 MutationObserver 来监听即可
UI 设计
-
插件的 ui 参考了下 Adblock plus 这个广告插件的,我和他的操作差不多,需要展示一些信息和开关即可,不过没什么设计经验,简单写了写样式,自己看还算美观
-
图标还是挺重要的,本来打算用 ai 生成一个结果都不满意,然后想着还是用 mac 设置里主题色的图标结果也没找到一样的,最后发现苹果官网有个介绍页面,里面有关于深色主题的介绍和图标,那就是他了,图标确定
踩坑记录
-
这里还是记录下一些踩坑过程,可能帮助到一些人
-
其次本以为一个简单的项目,但真正做下来还是能学到很多的,很多问题都可以去深入研究下的
TypeError: Illegal invocation 报错
-
当使用 Reflect.get 代理 MediaQueryList 对象获取 matches 属性时,此时如果使用
Reflect.get(target, property, receiver)来获取值会报错TypeError: Illegal invocation -
但直接使用 target[property] 则可以获取到值而不会报错
-
相关搜索结果
-
结论
-
这里的关键原因在第三个参数 receiver 上面,本质上则是 this 问题,可以看下上面的文章
-
当调用时 this 指向代理对象而非 targe 对象,如果使用
Reflect.get(target, property, target)就不会报错,这里就等于使用 target[property],this 都是指向 target
-
Crxjs 相关问题
-
MAIN 方式的内容脚本如何更新
- 结果是不能,只能使用没有热更新的方式开发
- 直接则引用会报错,因为 MAIN 里没有 chrome 的上下文
- 相关 issue
-
有些使用方式连官方网站都没有说明怎么使用
- 翻看 issue 后发现有些方法在作者自己的文章里才有写
-
每次热更新所有网站都会刷新一遍,如何只在当前激活的网页热更新
- 我自己使用上发现的问题,应该挺好解决的,不过 crxjs 没有提供
-
manifest 文件配置会被 crxjs 强制修改,没有提供参数做可配置化
-
结果是只能自己写脚本再去修改配置文件才行
-
发布上线
-
一番折腾终于写好了,不过还有可优化的地方,提示也做的不够好,后续还需要再更新
- 比如有些网站自带主题切换,或者有些网站压根就没有主题相关的样式代码,这时点击切换看不到效果
- 不过好在我能只能能不能切换,后续会再优化一版体验
-
此外其实想改当前页面的主题色官方就有提供方法,不过麻烦一点一般人肯定不会这样每次去切换
- 官方模拟媒体查询方式
- stackoverflow 相关问题描述修改
- 但是这种方式浏览器没开放接口,所以扩展不能直接调官方的方法去修改主题色
-
最后附上项目 github 地址,欢迎 star
-
使用过程中有问题可以提 issue
-
后续
-
把扩展推荐给朋友用了下,还是体验有些差,然后我说主要是给公众号泳道,被吐槽一般查看公众号的内容就直接在微信内嵌的浏览器里看了,谁还专门跑浏览器看。。。
-
感觉大家不都用浏览器看的吗,谁用微信内嵌的浏览器啊(手动狗头)
安装使用
-
下载地址
-
默认只针对以下网站启用,其他网站需要用户授权使用,点击开启即可
mp.weixin.qq.com
developer.mozilla.org
theme-next.org
sspai.com
learn.microsoft.com
-
默认使用当前系统主题色,可直接点击按钮来切换主题色
-
增强模式为针对一些没有 css 样式切换,完全使用 js 来切换样式的网站,现在为手动开关,后续会自动识别后开启或关闭
最后
- 附上自己的微信号,欢迎加好友联系
- 也可以关注我的公众号“贤也是也”,也是刚开始做,欢迎关注