最近在给我的博客接入评论系统,因为只考虑基于 Github Issue 的评论框架,最后决定在 Gitalk 和 Utterances 中选一个。之前用过一段时间 Gitalk,当时没有设置每篇文章的 id,结果现在更改域名以后全都识别不了了,折腾了一会儿没解决,再加上它对 ts 和 React 的支持并不好,所以决定尝试一下 Utterances。
因为我的博客有切换 light
和 dark
主题色的功能,于是想让 Utterances 也随博客主题一起变化。想实现的最终效果是这样的:
解决思路
Utterances
用在静态博客上还是非常简单易用的,一个 script 标签就可以解决,它还做了一个配置生成器,只需要把下面的代码加入到网页中就可以用了
我想应该有人和我遇到一样的问题,于是在 utterances 的 issue#549 里找到了一个解决方法,使用 iframe.postMessage()
给评论子窗口发送消息,可以实现动态变化。
确实是不错的解决办法,但是如何在页面第一次加载的时候就设置正确的主题呢?server 渲染时还不知道 client 使用什么主题,所以没办法直接初始化 script 标签里的变量。难道只能写一段 client js,动态生成这个 script 标签吗?感觉有些丑陋。
于是我决定看看它的 script 在做什么,可以看到 theme
是它的一个 attribute,把脚本下载下来发现其实它拼凑了一个 iframe
的 url 链接,然后插入到 document 中,像下面这段代码。
这个 theme 是 iframe url 的一个查询参数,所以只要拼凑出符合规则的链接就可以避免使用它的 script sdk 了,然后就可以将 js 变量插入到 iframe src 里。
那直接修改这个 url 的参数是不是就可以做到动态修改主题了?虽然可以,但是 url 变化会导致 iframe 整体重新加载,对我这种强迫症来说不能接受,还是要用 iframe.postMessage()
来实现动态修改。
React 版本实现
于是实现了一个 React 版本的动态主题 utterances
,贴代码
使用时只需要把这个组件放到需要的地方就可以了
其中写了 NOTICE 注释的几处代码是需要根据自己的情况改的,我用的是 astro
框架,修改主题会触发一个 CustomEvent
,所以在 React
代码里是监听了这个事件来处理的。
Astro 版本实现
在写 React 这段代码的时候我发现 utterances
初始化完成后会给 window
发送一个 resize
消息,那其实就可以通过这个消息判断它初始化完成了,不需要在 server 端知道初始化主题。
于是又写了一个简化版的 astro
实现,贴代码: