【问题标题】:No exported symbols with es6 modules library compiled by Closure CompilerClosure Compiler 编译的 es6 模块库没有导出符号
【发布时间】:2017-09-07 09:07:55
【问题描述】:

起点:许多 js 文件被 Closure Compiler(ADVANCED_OPTIMIZATIONS 级别)成功编译(无警告/错误)在单个库文件中。

在这些js文件中:

  • goog.requiregoog.provide 用于在它们之间导入/导出东西
  • /** @export */ 前面使用whatever (const/function/Class/var) 是库外需要的。

一些 HTML 文件包括该库和一些未编译的 js 成功访问该库中定义的所有 whatever

我想要的:迁移到 es6 模块语法

我对每个 js 文件做了什么:

  • goog.requireimport 替换为 Class 列表,来自另一个 js 文件的函数
  • goog.provide 被移除,export 添加在另一个 js 文件所需的每个类、函数等前面
  • 尝试 1:每次在库外都需要 whatever 时,不要更改 /** @export */
  • 尝试2:将所有/** @export */ whatever替换为goog.exportSymbol('whatever', whatever)

这已成功编译(没有警告/错误,仍处于 ADVANCED_OPTIMIZATIONS 级别)。

问题:现在,对于相同的 HTML 文件,库中定义的所有 whatever 都被浏览器视为“未定义”。事实上,当我在控制台中输入 Object.keys( window ) 时,我可以看到编译器更改的所有符号名称(aabaca 等),但我导出的符号 whatever 一个都没有。

示例:demoVisitors 是在库中定义并在外部需要的 const array。 之前在库文件中,我可以看到 ... w("demoVisitors",[Oa,La,Ma,Na]); ... 并且内容在 HTML 页面中正确可见。在 es6 模块更改后,我可以看到 ... H("demoVisitors$$module$filemane",Oa); ...filename 是定义 demoVisitors 的文件名)用于尝试 1,H("demoVisitors",[Na,Ka,La,Ma]); 用于尝试 2。demoVisitors 在浏览器中未定义相同页面。

【问题讨论】:

  • 我不确定@export 是否在模块中工作。
  • 根据您的评论,我将 @export 替换为 goog.exportSymbol。它在编译后的代码中看起来更好,但在浏览器中仍未定义。我相应地更新了我的帖子。

标签: javascript ecmascript-6 google-closure-compiler es6-modules


【解决方案1】:

经过进一步调查,我找到了解决方案。

虽然在浏览器中加载,控制台没有任何错误(当然undefined whatever 除外),但我的库没有执行。我只是将闭包库移动到要编译的文件堆栈之前,然后浏览器正确地执行了我的库,并正确导出了我的符号。详情见下文。

三种导出符号的方法在编译后的 es6 模块中起作用:/** @export */ whatevergoog.exportSymbol('whatever', whatever)window['whatever'] = whatever。前 2 个是第三个(用于根符号)的便捷方式。

尽管如此,/** @export */ myClass 会生成一个不友好的未混淆名称,例如 myClass$$module$source-filename-with-path。 为了获得未经混淆的名称myClass,避免在我的代码中使用goog 函数并巧妙地启用编译/未编译模式,我删除了/** @export */ 并在class myClass { ... } 之后添加unobfuscateSymbol('myClass', myClass)。这是我的“自己的”函数,直接来自闭包库中定义的exportSymbol 函数。这仅对类等根符号是必需的,您可以为类中定义的所有符号(属性、函数等)保留/** @export */

这里是源代码:

export function unobfuscateSymbol(publicPath, object, objectToExportTo = window) {
    // Same code can be used compiled and not compiled so unobfuscation only in case of obfuscation
    if (unobfuscateSymbol.name !== 'unobfuscateSymbol') {
        const /** Array<string> */ parts = publicPath.split('.');
        let /** Object */ objToExportTo = objectToExportTo;
        let /** string */ part;
        const /** number */ nbOfParts = parts.length;
        for (let /** number */ i = 0; i < nbOfParts; i += 1) {
            part = parts[i];
            if ((i === (nbOfParts - 1)) && object) {
                objToExportTo[part] = object;
            } else if (objectToExportTo[part] && objectToExportTo[part] !== Object.prototype[part]) {
                objToExportTo = objectToExportTo[part];
            } else {
                objToExportTo[part] = {};
                objToExportTo = objToExportTo[part];
            }
        }
    }
}

我如何详细识别问题:

  1. 为了理解导出问题,我尝试在浏览器中加载我的 HTML 测试页面时在闭包库中定义的 exportSymbol 函数中放置一个断点:没有中断...

  2. 我通过添加 console.log("my library is being executed") 仔细检查了这个执行问题:我能够在我的库的 goog.require/goog.provide 版本中看到该消息,但没有在 es6 import/export 版本中。如果不执行,当然不会导出任何符号。

  3. 我用 IIFE 包装了我的图书馆。闭包编译器参数:--output_wrapper "(function(){%output%})()" 并且我的库中的执行错误消息出现在浏览器控制台中。我发现 goog.global 是 Closure 库的基本命名空间,在休息时间是 undefined

  4. 我将闭包库移到要编译的文件堆栈之前。关闭编译器参数:–js closure-lib-path/base.js –js myfile1.js –js myfile2.js … 以确保编译的 goog 内容在第一个 /** @export */ 编译生成的第一个 exportSymbol 调用之前。

  5. 在浏览器中执行和导出都正常。我刚刚添加了如上所述的unobfuscateSymbol 函数,以获得与我的库的goog.require/goog.provide 版本相同的友好导出名称。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多