Ruby Rails & service worker

Ruby on Rails & service worker

我正尝试在 Ruby 的 Rails 应用程序中使用 service worker。

我需要在 app/javascripts/service-worker.js.erb 文件中使用一些 erb 功能。 Service Worker 注册脚本如下所示:

var registerServiceWorker = function() {
  navigator.serviceWorker.register(
    '<%= asset_path('service-worker.js') %>',
    { scope: '/assets/' }
  )
  .then(function() {
    console.info('Service worker successfully registered');
  })
  .catch(function(error) {
    console.warn('Cannot register sercie worker. Error = ', error);
  });
}

这行不通;我在这里从来没有得到承诺:

navigator.serviceWorker.ready.then

我也尝试了 .// 范围,但我得到了这个错误:

DOMException: Failed to register a ServiceWorker: The path of the provided scope ('/') is not under the max scope allowed ('/assets/'). Adjust the scope, move the Service Worker script, or use the Service-Worker-Allowed HTTP header to allow the scope.

如果我将 service-worker.js 移动到 public 文件夹,删除 .erb 扩展名并将范围更改为 ./,一切正常,但我没有模板引擎在那里。

有什么建议吗?

出于安全考虑,您不能在高于执行位置的范围内注册 ServiceWorker。

如果你真的需要模板引擎,你可以尝试从 /public 文件夹中的文件动态加载 JS 文件 (How do I include a JavaScript file in another JavaScript file?). Currently Service-Worker-Allowed HTTP header is not implemented yet in Firefox (https://bugzilla.mozilla.org/show_bug.cgi?id=1130101)

在根目录下即可。比如我放了一条route

get 'service-worker(.format)', to: 'core#service_worker'

然后只需放入您的控制器(core_controller.rb 在我的示例中)

def service_worker
end

然后创建 app/views/core/service_worker.js.erb 并将您的 JS 放在那里(包括任何 <%= stuff %>)。

我现在也 运行 遇到了这个问题。根据我的研究,我认为 Rails 应用程序处理服务工作者的正确方法是通常使用资产管道为服务工作者 javascript 文件提供服务,使用范围选项(如你已经完成了),然后使用 Service-Worker-Allowed HTTP header 明确允许新范围。

不幸的是,目前实施此方法存在几个障碍:

  1. Rails 4(显然)不允许将 HTTP header 添加到其服务的资产中,除了 cache-control header。要解决这个问题,您可以添加一个 Rack 插件,详见 S.O. answer,或者在生产环境中,如果您使用的是 Nginx 等资产服务器,您可以通过让 Nginx 添加 HTTP header 来解决这个问题.虽然这在开发过程中没有帮助。
  2. Rails 5 确实允许将 HTTP headers 添加到资产,但是,如 detailed in this blog post.
  3. 浏览器对 service worker 的支持和 Service-Worker-Allowed HTTP header 是 currently poor

现在有一个 gem、serviceworker-rails 允许您在资产管道中代理对 serviceworker 脚本的请求。由于浏览器注册 serviceworker 脚本的方式,最好避免缓存它们。

Sprockets 会对资产进行指纹识别,Rails(或您的网络服务器)通常会使用来自 /assets 目录的积极缓存 headers 来提供已编译的文件。

我们想要的是自定义 service worker 路径和响应的能力 headers 仍然得到 Sprockets 预处理,即 CoffeeScript/ES2015 到 JavaScript 转译。

serviceworker-rails gem 将中间件插入到您的 Rails 堆栈中,它将 /serviceworker.js 的请求(例如)代理到生产中相应的指纹编译资产.在开发中,您会得到预期的 auto-reloading 行为。您也可以在不同的范围内设置多个 serviceworker。

https://github.com/rossta/serviceworker-rails

将 service worker 文件保留在项目结构强加的任何目录中,并将 scope 选项设置为 / 并将 Service-Worker-Allowed HTTP header 添加到响应中你的 service worker 文件。

在 ASP.Net 中,我将以下内容添加到 web.config 文件中:

<location path="assets/serviceWorker.js">
    <system.webServer>
        <httpProtocol>
            <customHeaders>
                <add name="Service-Worker-Allowed" value="/" />
            </customHeaders>
        </httpProtocol>
    </system.webServer>
</location>

并通过以下方式注册工人:

navigator.serviceWorker.register('/assets/serviceWorker.js', { scope: '/' })
        .then(function (registration)
        {
          console.log('Service worker registered successfully');
        }).catch(function (e)
        {
          console.error('Error during service worker registration:', e);
        });

已在 Firefox 和 Chrome 上测试。

参考Service worker specification

中的例子

我最近写了一篇关于如何使用 Webpacker 完全做到这一点的文章。请找到我的指南Progressive Web App with Rails。 简而言之:

使用 yarn add webpacker-pwa 添加 webpacker-pwa npm 包并使用

编辑 environment.js
const { resolve } = require('path');
const { config, environment, Environment } = require('@rails/webpacker');
const WebpackerPwa = require('webpacker-pwa');
new WebpackerPwa(config, environment);
module.exports = environment;

这将允许您使用服务工作者。请同时查看项目的自述文件:https://github.com/coorasse/webpacker-pwa/tree/master/npm_package