Service Workers还能这么用

Service Workers还能这么用

十一月 10, 2021 阅读 471 字数 3415 喜欢 1

什么是service worker,以下简称sw,是浏览器在后台独立于网页运行的脚本,它打开了通向不需要网页或用户交互的功能的大门。现在,它们已包括如推送通知和后台同步等功能。将来,sw将会支持如定期同步或地理围栏等其他功能。更详细的介绍请看这里

简单来说,sw是用js写的一个可以拦截请求,推送通知,离线浏览的API,当然它是运行在浏览器里的,兼容性看这里,除了IE基本都能用,功能的话能实现比如服务器离线了,sw替用户先访问web server,检测到服务器不在线用户仍然可以打开你事先在sw里写好的一些内容或者其他策略、虽然没打开推,但是浏览器给你发出通知说你喜欢的某某更新了什么、根据不同的浏览器版本,拦截并返回支持度更好的格式或功能等,比如之前在web server或者后端实现的webp自适应现在可以用sw来实现,这次的玩法属于拦截请求的一种,相当于js部署的反向代理服务器,把对a的域名请求拦截转发给b上,作者来自这位https://github.com/ChenYFan/ClientWorker

使用sw前有这么几个前提条件和缺点

这次以typecho为例子。首先是a,a服务器上就两个文件:404.html和sw.js,对a的访问都跳转到404.html上,在这里注册sw.js成功后,对a的访问就全部被sw拦截转发给b了,所以a的话可以放在一些静态网站托管服务商,比如cloudflare pagegithubheroku

源站b就放在一个速度快,或者成本低的地方,阿里云、腾讯云或者一些nat vps上,b的web服务器添加CORS头,这里以nginx为例
添加的header

    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
    add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
    
    if ($request_method = 'OPTIONS') {
        return 204;
    }

Access-Control-Allow-Origin这一行最好不要用通配符*,写准确一点就写a最好,比如这样

    add_header Access-Control-Allow-Origin https://a.com;

是一个安全问题,想了解详细的可以看这里:Web安全之CORS

兼容性和收录暂时没好办法,用IE的还是占少数,收录的话等搜索引擎支持吧

然后typecho里倒是有个跨域功能,不过实测并不用打开,只需要在设置里将检查评论来源页 URL 是否与文章链接一致关掉即可
typecho的跨域功能是在config.inc.php加上这一行,有需要可以打开测试

    define('__TYPECHO_DYNAMIC_SITE_URL__',true);

然后文章的链接都是b的,需要修改成a的,nginx有http_sub_module功能,可以替换字符串后在输出html,但是这样会造成后台异常,所以不推荐,在js实现也会遇到有些页面是ajax获取后在替换比较复杂,也不推荐。所以最后还是修改主题php解决,我的逻辑是比较当前路径的域名和typecho的rootUrl是否一致,不一致就输出当前的,大概是这样

    <?php echo str_replace(explode('/',Helper::options() -> rootUrl)[2],explode('/',$this->options->siteUrl)[2],$pages->permalink); ?>

把主页的,文章的,时间线的几个改掉,我的功能比较简单,改起来还挺快的,评论表单提交的地方不要改,这就是接下来要说的post请求的问题了

之前说到post请求非常困难,原因是被sw拦截之后并不能获取到body然后转发给b(存疑?),b收到的body就是空的,解决方案是有,Mozilla推荐的方法是:先把post发送给IndexeDB,这个也是运行在前端的一个数据库,然后sw通过message通信拿到IndexeDB的数据后在转发给b,这里面还实现到上锁的问题,防止重复发送,实现起来大概是这样

https://medium.com/@jono/cache-graphql-post-requests-with-service-worker-100a822a388a
https://blog.formpl.us/how-to-handle-post-put-requests-in-offline-applications-using-service-workers-indexedb-and-da7d0798a9ab
demo

这样实现的话太复杂了,评论这种场景就不用拦截了,直接在a跨域提交form表单给b即可,这样的话在typecho有一个小bug就是,redirect之后的location有点问题,提交完评论后会redirect到首页,也可以修改typecho重定向那部分解决,这就算目前遇到的一些小坑吧,希望对其他人有帮助

我改好的版本在这里https://github.com/BapiGso/Typecho-ClientWorker,放到a根目录修改下sw.js里的interceptdomain为a域名,proxylist改为b域名,最后一段的Response应该就能用了

发表评论