【问题标题】:GWT code splitting synchronous requestGWT 代码拆分同步请求
【发布时间】:2015-12-23 14:24:48
【问题描述】:

我在 GWT 中使用代码拆分来减少初始 JavaScript 的大小。 当我的应用程序初始化时,我想按照文档 (www.gwtproject.org/doc/latest/DevGuideCodeSplitting.html) 中的说明预取代码的其他(更大)部分。

private void doSth(final boolean prefetch) {
    GWT.runAsync(new RunAsyncCallback() {
        public void onFailure(Throwable caught) {
            Log.error("Loading the code failed!");
        }

        public void onSuccess() {
            if(prefetch)
                return; //do nothing. just a prefetch
            //here is the loaded code
        }
    });
}

但我无法识别性能改进。当我分析浏览器日志时,我意识到加载 JavaScript 的请求没有被标记为 XHR。 GWT 是否同步加载分割点的代码?

【问题讨论】:

    标签: javascript java ajax gwt


    【解决方案1】:

    性能改进在于初始下载的代码,假设没有其他引用该代码。如果//here is the loaded code 的工作还有其他功能,那么将很少或没有代码可以分解为单独下载的 JS 文件。

    可以通过多种方式禁用此功能,包括使用开发模式或设置编译器标志以跳过此过程。在这种情况下,是的,分割点同步运行,因为等待是没有意义的。此外,文件加载一次后,下次在同一页面加载中调用代码时不需要加载。

    如果您的服务器设置为正确缓存,那么在第一次访问后节省的时间会更少,因为无需下载 - 您只需节省将代码解析到浏览器的 JS VM 中所花费的时间。

    但除此之外,我们还需要更多信息。


    这里有一个快速演示,展示了如何编写拆分点并添加更多内容,并让您使用浏览器注意拆分点代码是如何单独引入的。

    public class SampleEntryPoint implements EntryPoint {
      public void onModuleLoad() {
        Label label = new Label("Hello, World!");
        label.addClickHandler(new ClickHandler() {
          @Override
          public void onClick(ClickEvent event) {
            GWT.runAsync(new RunAsyncCallback() {
              public void onFailure(Throwable var1) {/*ignore*/}
              public void onSuccess() {
                Window.alert("Clicked, and loaded in split point!");
              }
            });
          }
        });
        RootPanel.get().add(label);
      }
    }
    

    代码和示例: https://viola.colinalworth.com/proj/755e224e7f48a047703d44eb6903d926/project/client/SampleEntryPoint.java

    独立样本: https://viola.colinalworth.com:444/compiled/755e224e7f48a047703d44eb6903f76c/

    当您加载此页面时,nocache 文件会加载,初始下载也会加载(通过 Chrome 的检查器的“网络”选项卡可以看到):

    然后,当您单击 Label 小部件时,会触发 onClick 触发 runAsync 并下载额外的分割点(加上“剩余”片段):

    将这两个新条目添加到“网络”选项卡后,您会看到警告消息出现。后续点击不会导致这种轻微的延迟,也不会强制重新下载这个额外的 JS。

    还请注意,这些不是作为 AJAX/XHR 调用加载的,而是作为要添加到页面的脚本标记。单击启动器列中的详细信息(未显示)会导致此信息(为便于阅读而格式化):

    function fb(a) {
        var b, c, d;
        d = (bb(), window);
        b = d.document;
        c = b.createElement('script');
        (!!a.a || a.b) && cb(c, a.a, a.b);
        eb(c, a.c);
        (b.head || b.getElementsByTagName('head')[0]).appendChild(c);
        return c
    }
    

    通过混淆代码,我们看到一个<script>标签被创建,并附加到页面的<head>


    深入挖掘,我们可以发现AsyncFragmentLoader.LoadingStrategy 接口描述了如何去获取这个片段,并且com/google/gwt/core/AsyncFragmentLoader.gwt.xml 默认连接到XhrLoadingStrategy。但是,xsxsiframe 链接器都将其更改为 CrossSiteLoadingStrategyScriptTagLoadingStrategy。从最新版本的 GWT(您没有指定,所以我假设您使用的是最新版本),xsiframe 链接器是默认的。来自 Core.gwt.xml:

    <add-linker name="xsiframe" />
    

    我们可以通过切换到旧的链接器来自定义它,或者只是替换策略。请注意,切换 XHR 策略会导致跨域加载无法正常工作(例如 SuperDevMode),因此请注意这一点。

    就像AsyncFragmentLoader.gwt.xml 将接口连接到XhrLoadingStrategyCrossSiteIframeLinker.gwt.xml 将其更改为ScriptTagLoadingStrategy,我们可以将其更改回来。我们创建一个规则,将LoadingStrategy 替换为XhrLoadingStrategy,并在我们的GWT 继承我们的.gwt.xml 文件中的语句之后列出它:

    <replace-with class="com.google.gwt.core.client.impl.XhrLoadingStrategy">
        <when-type-is class="com.google.gwt.core.client.impl.AsyncFragmentLoader.LoadingStrategy" />
    </replace-with>
    

    这是过去作为 std 链接器 (com.google.gwt.core.linker.IFrameLinker) 的一部分所依赖的旧默认值,尽管不再鼓励这样做,并且可能会在以后的版本中删除。

    【讨论】:

    • 感谢您的精彩回答。正是我需要的。是否可以在 gwt.xml 中定义 XhrLoadingStrategy(并覆盖默认设置)?
    • 是的,我会在答案中澄清,但您为什么希望使用 XHR?这将破坏跨域加载(除非托管拆分点的服务器支持 CORS),这将阻止 SDM 按预期工作。
    • 应用程序的代码非常庞大。分割点的代码是2MB!为了提高启动性能,我想异步加载分割点的代码,同时客户端向服务器请求一些其他参数。
    • 我不确定你在告诉/问我什么。拆分点(启用时)始终是异步的。但是,如果您在服务器上启用了缓存,它们将永远不会加载第二次(除非应用程序被更改/重新编译)。
    • 这很难解释。这是一个非常特殊的情况。但是你已经帮了我很多了。谢谢
    猜你喜欢
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-25
    • 2012-09-02
    • 2020-11-05
    • 2019-02-25
    • 2017-11-11
    相关资源
    最近更新 更多