GWT代码片段(.cache.js)不会触发服务工作者获取事件

时间:2017-11-15 10:36:50

标签: gwt service-worker progressive-web-apps service-worker-events

GWT编译器会因代码拆分而产生代码片段(请参阅详细信息here

初始下载文件在下图中以{alphanumeric} .cache.js(例如1805B2053A824F148DB6D05B2186F955.cache.js)和其他片段{number} .cache.js(例如1.cache.js)命名。

GWT Code fragments

我正在尝试使用Service Worker开发GWT应用程序,因为我希望应用程序脱机工作。缓存和脱机后,初始下载文件的GET请求会触发ServiceWorker中的FETCH事件,但单个代码片段不会触发该事件。出于某种原因,他们似乎绕过了服务工作者。

要进行演示,请检查https://gwt-pwa-demo.herokuapp.com/pwademo.html处的应用。它是一个使用Service Worker的GWT应用程序。它不是我的应用程序,我只是用它来进行演示。

加载应用时,所有文件都会被缓存。不要从chrome dev工具离线,并刷新应用程序。该应用程序仍然显示,您将注意到初始下载文件是从Service Worker加载的。现在点击,巴黎/柏林/伦敦,它将加载片段1.cache.js,依此类推。请注意,这些来自磁盘缓存而不是来自ServiceWorker的缓存。如果查看控制台输出,则无法查看从ServiceWorker Fetch事件打印的这些片段的日志。

为了澄清更多,该应用程序只能脱机工作,因为片段是从磁盘缓存中提取的。如果磁盘缓存中不存在这些片段,则该应用程序无法正常工作。即使片段存在于缓存存储中,由于ServiceWorker无法拦截这些请求,因此它们不会被加载。请按照以下步骤查看此操作。

  1. 假设您已经加载了一次应用程序,请打开开发工具 - >申请 - >清除存储并清除站点数据。确保缓存存储中的缓存也已清除。
  2. 现在再次加载应用https://gwt-pwa-demo.herokuapp.com/pwademo.html
  3. 将再次创建缓存,您将在缓存中看到片段。
  4. 使用"清空缓存&重新加载应用程序Hard Reload"您的开发工具处于打开状态时在Chrome中。
  5. 这将清除磁盘缓存,但缓存存储仍然存在。
  6. 离线,刷新应用程序,初始应用程序仍然从缓存存储加载。
  7. 但当你点击,巴黎或伦敦,它会触发加载代码片段时,它会失败。
  8. enter image description here

2 个答案:

答案 0 :(得分:1)

好的,我想我发现了@ https://bugs.chromium.org/p/chromium/issues/detail?id=439697的铬虫报告和相关的ServiceWorker规范问题@ https://github.com/w3c/ServiceWorker/issues/612

GWT通过在内部创建iframe来加载代码片段。这些iframe不受父母服务工作者的控制。 Chrome bug仍处于打开状态,但是w3c关闭了规范问题@ https://github.com/w3c/ServiceWorker/issues/1163

答案 1 :(得分:0)

您必须使用xhr请求加载片段。

1)首先,您必须扩展CrossSiteFrameLinker

/**
 * Created by Gernot Pansy <gernot.pansy@ut11.net> on 07.08.17.
 */
@LinkerOrder(LinkerOrder.Order.PRIMARY)
@Shardable
public class ProgressiveWebAppLinker extends CrossSiteIframeLinker {

    @Override
    public String getDescription() {
        return "Progressive-Web-App";
    }

    @Override
    protected String wrapDeferredFragment(final TreeLogger logger, final LinkerContext context, final int fragment,
                                        final String js, final ArtifactSet artifacts) {
        return js;
    }

    @Override
    protected String getJsInstallLocation(final LinkerContext context) {
        return "com/google/gwt/core/ext/linker/impl/installLocationMainWindow.js";
    }

    @Override
    protected String wrapPrimaryFragment(final TreeLogger logger, final LinkerContext context, final String script,
                                        final ArtifactSet artifacts, final CompilationResult result)
            throws UnableToCompleteException {
        return script;
    }

    @Override
    protected String getJsInstallScript(final LinkerContext context) {
        return "xxxxxxx/progressiveWebAppInstallScript.js";
    }

    @Override
    protected String getJsIsBodyLoaded(final LinkerContext context) {
        return "";
    }

    @Override
    protected String getJsWaitForBodyLoaded(final LinkerContext context) {
        return "";
    }

    @Override
    protected String getScriptChunkSeparator(TreeLogger logger, LinkerContext context) {
        return "";
    }

    @Override
    protected boolean shouldInstallCode(final LinkerContext context) {
        return true;
    }
}

2)您需要修改后的javascript安装脚本(progressiveWebAppInstallScript.js):

function installScript(filename) {

    function installCode(code) {
        function removeScript(body, element) {
        // Unless we're in pretty mode, remove the tags to shrink the DOM a little.
        // It should have installed its code immediately after being added.

        __START_OBFUSCATED_ONLY__
        body.removeChild(element);
        __END_OBFUSCATED_ONLY__
        }

        var doc = getInstallLocationDoc();

        var script = doc.createElement('script');
        script.language='javascript';
        script.text = code;
        doc.body.appendChild(script);
        removeScript(doc.body, script);
    }

    sendStats('moduleStartup', 'moduleRequested');
    var xhr = new $wnd.XMLHttpRequest()
    xhr.open("GET", filename);
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
        // Clearing onreadystatechange otherwise it may cause memory leak (e.g. in IE8).
        xhr.onreadystatechange = function() {}; // Clear callback
        if (xhr.status == 200) {
            installCode(xhr.responseText);
        } else if (__MODULE_FUNC__.__errFn) {
            __MODULE_FUNC__.__errFn('__MODULE_FUNC__', new Error("Failed to load " + code));
        }
        }
    };
    xhr.send(null);
}