GWT可以优化原始javascript及其生成的输出吗?

时间:2014-06-03 00:45:02

标签: java javascript gwt

由于几个原因,我有兴趣编写一个混合应用程序,该应用程序部分用Java编码(通过Google Web Toolkit)并部分用JavaScript编码。我打算使用GWT Exporter从JavaScript调用Java库。

麻烦的是,这会破坏很多代码优化和压缩的机会。 GWT主要用于优化它生成的JavaScript,第三方Javascript压缩库在给定GWT输出时可能会崩溃。

有没有办法告诉GWT编译器“嘿,将这些Javascript文件传递到优化传递中”? GWT有一个标志,用于在引擎盖下使用Closure Compiler(显然支持优化常规javascript),所以感觉这应该是可能的。

3 个答案:

答案 0 :(得分:0)

只要您可以要求Closure编译源代码,就有可能,但您必须得到其他'其他'将源代码转换为GWT(以及后来的闭包)正在编译的代码。目前,这意味着将该代码放在JSNI中,否则它只是文件系统上的另一个文件,并且编译器无法知道进出的依赖性,因为它同样不能告诉您何时/如何正在加载该文件。

如果我没记错的话,Closure中依赖项的标准用法是通过文件顶部的JavaScript中的goog.require()方法调用 - 这两者都声明了依赖关系,如果需要,则加载文件。如果没有这个,你的基本HTML页面需要为你可能使用的每个文件都有<script>个标签,而且实际上没有运行那个页面,Closure不知道这些文件要加载的顺序,或者如何在编译源代码时要远远地运行。


GWT本身(即在Closure之外)只对Java源代码中作为JSNI包含的原始JS进行了一小组优化:

  • com.google.gwt.dev.js.JsStaticEval - 简单的常量折叠和其他表达式简化
  • com.google.gwt.dev.js.JsInliner - 特定复杂度阈值下的内联函数和其他各种清理
  • com.google.gwt.dev.js.JsUnusedFunctionRemover - 对未引用的函数/变量的简单修剪
  • com.google.gwt.dev.js.JsDuplicateCaseFolder - 在多个case块中查找相同的正文,并将其合并为一个直通案例。

GWT有三种主要方式来包含JS源:JSNI,.gwt.xml中的标记(不是所有链接器都支持)和com.google.gwt.core.client.ScriptInjector来从字符串常量或远程URL中提取。只有第一个将代码视为实际源代码 - 第二个/第三个代码来自任何源代码,并且不依赖编译时静态可用的代码。 JSNI有其自身的局限性 - 它不支持with块,并且必须使用$wnd$doc来引用主机页面windowdocument

答案 1 :(得分:0)

首先,虽然这不是您的问题,但您必须注意,要使用gwt-exporter,您必须为每个要填充的类调用GWT.create,或者调用exportAll()方法导出标记为可导出的所有内容。这意味着你要对编译器说你将使用这些类,即使你的JS应用程序最终也不会使用它们。所以你不会利用删除未使用的代码导致大的js输出。您可以使用代码拆分来分割碎片中的单独代码。

其次,与你的问题有关,正如@Colin在他的回答中所说,只有编写在JSNI块中的代码才会被GWT编译器优化,但默认优化非常简单,尽管如果使用闭包编译器它是一个有点强。我没有尝试过,但我认为不允许关闭注释,因为GWT编译器可能会在将js传递给闭包编译器之前删除它们。

无论如何,将这些文件包含在JSNI块中的主要问题是,您必须手动将代码复制并粘贴到java类中,然后执行其他一些技巧来解决$ wnd等问题。

我们在gwt-query中有一个JsniBundle生成器能够从文件系统或任何url中获取.js个文件,并且在编译时包含JSNI片段中的代码并制作一些技巧它在GWT运行的iframe中工作。它适用于我使用的几乎库和插件,但有时我不得不修改javascript源以允许打沙它。

这里有一个如何包含jquery和highcharts的例子:

public interface JQueryBundle extends JsniBundle {
  @LibrarySource(value = 
    "http://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js")
  public void initJQuery();
}

public static abstract class HighCharts implements JsniBundle {
  @LibrarySource("js/highcharts.src.js")
  public abstract void initHighcharts();

  public void drawChart(String id, JavaScriptObject props) {
    JavaScriptObject $container = JsUtils.runJavascriptFunction(window, "$", "#" + id);
    JsUtils.runJavascriptFunction($container, "highcharts", props);
  }
}

public void testHighCharts() {
  JQueryBundle jQuery = GWT.create(JQueryBundle.class);
  HighCharts highCharts = GWT.create(HighCharts.class);

  jQuery.initJQuery();
  highCharts.initHighcharts();
  highCharts.drawChart("chart", charData);
}

使用此方法的一些优点在我们的GWT.create-2013演示文稿的this slide中列举。

答案 2 :(得分:0)

您可以使用GWT编译器优化任意JavaScript代码,只需使用适当命名的LinkerContext.optimizeJavaScript(TreeLogger, String)方法。 LinkerContext对象在Linkers中可用,它们是在编译期间运行的自定义代码片段。这是关于如何编写一个的最小例子:

@LinkerOrder(LinkerOrder.Order.POST)
public class ScriptOptimizer extends AbstractLinker {

    @Override
    public String getDescription() {
        return "Optimizes external JavaScript files.";
    }

    @Override
    public ArtifactSet link(TreeLogger logger, LinkerContext context,
            ArtifactSet artifacts) throws UnableToCompleteException {

        // This is some arbitrary JavaScript code you'd probably want to read
        // from a static file in your classpath
        String script = "var foobar = 1; for (var i = foobar; i < 5; i++) alert(1);";

        // Do the optimizations
        script = context.optimizeJavaScript(logger, script);

        // Create an Artifact from the optimized JavaScript string
        ArtifactSet newArtifacts = new ArtifactSet(artifacts);
        newArtifacts.add(emitString(logger, script, "example.js"));

        return newArtifacts;
    }

}

然后,要在GWT编译过程中包含链接器,请将其添加到* .gwt.xml:

<define-linker name="scriptoptimizer"
    class="com.example.ScriptOptimizer" />
<add-linker name="scriptoptimizer" />

结果将是一个名为example.js的编译文件。您当然可以根据需要生成任意数量的文件,或者为了获得最佳结果,将所有脚本连接成一个并将其编译为单个输出文件。