2

首先:我知道这个 SO question以及这里的 externs extractor

我正在使用ThreeJS开发我的第一个游戏项目。由于我以模块化方式构建它,因此我打算使用Google 的 Closure Compiler来打包我的结果。一个小测试表明编译(使用高级优化)也会缩小构造对象的方法和属性,例如THREE.GeometryTHREE.Color.

检查生成的 externs 文件,这是我THREE.Color在第 78 行找到的(ThreeJS r60):

"Color": function () {},

显然它缺少方法和属性,所以闭包编译器不会知道它不应该重命名这些。

我有另一种解决方案,即在单独的文件中定义曝光并将其与源代码一起编译。它工作得很好,但是以这种方式定义每个使用的方法和属性需要大量的手动工作。这也只是一个骇人听闻的解决方法。闭包编译器根本不重命名对象上出现的任何这些名称。

例如,所有这些都具有相同的效果:

/** @expose */
THREE.Geometry.vertices;

/** @expose */
THREE.vertices;

/** @expose */
Object.vertices;

window.vertices = function() {
    console.log("foo");
};

window.vertices不会被重命名。因此,这不是一个理想的解决方案。

实际的问题是:闭包编译器甚至能够做到这一点吗?还是我应该将这些依赖项放入我的编译中并让它重命名我使用的所有方法和属性?

4

1 回答 1

2

如果我正确理解了您的问题,那么您正在尝试编译您的 awesomeGame.js - 它依赖于threejs.js - 但是您对threejs.js 的引用被闭包的重命名过程破坏了。

我同意自动化外部脚本可能有很多开发工作,可能不是一个强大的解决方案。

我的项目有一个非常相似的问题。我可以提供两种可能的解决方案。每个都有取舍。我个人不是threejs用户,这里只说明一般约定。

1)从window对象开始,用括号语法调出你需要的对象

根据这个讨论,在库执行并将其根对象放置在全局上下文中之后,您可以通过从window- 开始找到它,并且窗口对象永远不会被重命名。此外,括号语法永远不会被重命名。因此,您的编译代码可以THREE在 atwindow['THREE']THREE.Geometryatwindow['THREE']['Geometry']等处找到。此外,jQuery 将在 window['jQuery'] 中找到,并在 window['_'] 处找到下划线。

我建议任何时候你需要从编译的代码中访问外部对象或函数,比如threejs.js,在文件顶部声明一些全局定义,如下所示:

// awesomeGame.js:

// global defines:
var three_vertex        = window['THREE']['Geometry']['vertex'],
    three_rectangle     = window['THREE']['Geometry']['rectangle'],
    three_crossproduct  = window['THREE']['Math']['crossproduct'],
    jquery_ajax         = window['jQuery']['ajax'];

// game assets:
var myVertex         = new three_vertex(10,10),
    myRectangle      = new three_rectangle(10,10,10,10);

// do stuff:
console.log(myVertex['x'] + ' ' + myVertex['y']);
console.log(three_crossproduct(myVertex, myVertex));

myRectangle['paint']('black');

如果您将以上内容粘贴到漂亮/高级模式下的编译器服务,您将产生以下内容。

var a = window.THREE.Geometry.rectangle,
    b = window.THREE.Math.crossproduct,
    c = new window.THREE.Geometry.vertex(10, 10),
    d = new a(10, 10, 10, 10);

console.log(c.x + " " + c.y);
console.log(b(c, c));
d.paint("black");

正面: 完整的threejs.js 引用仅在全局定义中打印一次。从那时起,闭包将打印一个容器“a”或“b”。因此,与您讨论的外部方法不同,您可以获得大部分压缩优势。

否定: threejs.js 对象的方法和属性 - 必须在括号语法中一致地命名。请注意,如果 ['x'] 和 ['y'] 属性以及 ['paint'] 方法没有被括号括起来,它们将被重命名。将 Closure 设置为--warning_level=VERBOSE,注意JSC_INEXISTENT_PROPERTY错误,并将它们括起来。如果你忘记了什么,编译器会提醒你。错误参考

如果你的linter对所有的括号都不满意,用 说服它--sub,就像“容忍下标”一样。所有 jsLint 错误/选项

2) 将threejs.js 和 awesomeGame.js 连接成一个temp.js

另一种方法:为自己设置一个构建脚本,将所有源文件按依赖顺序连接到一个大临时文件中。如果您是 unix-y,您的脚本可能如下所示:

#!/bin/bash
# build_and_run.sh
# get updates to google closure: http://code.google.com/p/closure-compiler/downloads/list
#
# local jslint:
# sudo apt-get install nodejs npm
# sudo npm jslint -g

rm temp.js
rm final.js

cat threejs.js \
    awesomeGame_moduleOne.js \
    awesomeGame_moduleTwo.js \
    > temp.js

# docs: http://www.jslint.com/lint.html

jslint  temp.js \
        --maxerr=50 --sloppy --white --sub --plusplus \
        --nomen --bitwise --browser \
        --predef unescape \
        --predef Uint8Array \
        --predef Blob

java    -jar                compiler-20130823.jar \
        --compilation_level ADVANCED_OPTIMIZATIONS \
        --formatting        pretty_print \
        --language_in=ECMASCRIPT5 \
        --js                temp.js \
        --js_output_file    final.js

#maybe
nodejs  final.js

# or
# https://developers.google.com/chrome/web-store/docs/get_started_simple

chromium --load-and-launch-app=./

# rinse, repeat

优点: 没有括号,没有定义,没有扩展,没有中间件。谷歌关闭看到了一切。最终产品的最大压缩。运输项目的完整 linting(扫描原始的、分割的源文件可能会使 linter 感到困惑)。发送单个集成文件。

负面: 沉浸在上游源代码中。这可能会或可能不会让您同意。对上游源代码进行检查会特别诱使您在上游强加代码样式。反抗,反抗。另一方面,了解您所依赖的中间件具有优势。

还涉及调试 master.js 文件,因为将为 final.js 报告行号,并且您需要在阅读时识别源文件。使用漂亮的模式、更温和的编译设置、 @preserve注释和大量的console.log,你会得到它的处理。

最后,根据 threejs 的优先级和代码约定,可能不支持 Closure 中的编译,或者可能会引入细微的行为变化。如果是这种情况,请排除此选项。例如,正在积极讨论jQuery 与闭包的兼容性。


无论如何,我希望这能回答你的问题。如果我不符合要求,请回复。那里也有很多“包含/模块化”框架。也许有人可以填写该主题。

于 2013-09-12T17:49:51.937 回答