【问题标题】:How do I import an IIFE-based JavaScript module into an Angular TypeScript app?如何将基于 IIFE 的 JavaScript 模块导入 Angular TypeScript 应用程序?
【发布时间】:2019-06-11 01:03:03
【问题描述】:

所以我有一个第三方 SDK,它是一个基于老式 IIFE 的模块。换句话说,它看起来像这样:

var ThirdPartySDK = (function() {
  var export = {};

  // Add some methods to export

  return export;
})();

然后您应该通过在全局范围内引用它来使用它,如下所示:

<html>
  <body>
    <script src="lib/ThirdPartySDK.js">
    <script>
      ThirdPartySDK.foo();
    <\script>
  <\body>
<\html>

当然,我仍然可以以这种方式使用它,但这真的是 Angular 和 TypeScript 的最佳实践吗?有什么方法可以使用 angular/TypeScript/webpack 进行设置,以便我可以使用正确的导入语句?像这样的:

import { ThirdPartySDK } from '../lib/ThirdPartySDK.js';
ThirdPartySDK.foo();

【问题讨论】:

    标签: javascript angular typescript webpack iife


    【解决方案1】:

    ThirdPartySDK 的实际 设置正确的导入语句的最佳方法是将脚本重构为导出该值的模块。以下 sn-p 允许您使用 import 语句,如下所示:

    export const ThirdPartySDK = {
        foo() { console.log('Doing foo'); }
    };
    

    对于大型库,重构并不总是那么容易,所以我看到了两种不需要太多重构的方法:


    1.导出ThirdPartySDK 变量

    您可以简单地通过导出当前的IThirdPartySDK 变量(由 IIFE 返回)从 IIFE 文件中创建一个模块,然后按照您的说明导入它:

    export const ThirdPartySDK = (function() {
        var _export = {};
    
        // Add some methods to export
    
        return _export;
    })();
    

    请注意,如果您想获得有关ThirdPartySDK 形状的一些有用信息,则必须在const 声明中添加类型注释,如果SomeType(见下文)尚不存在,则您'必须自己写:

    export const ThirdPartySDK: SomeType = (function() {
    // ...
    

    此时,Typescript 将开始抱怨 IIFE 表达式不能分配给 SomeType;使用 as 关键字告诉 typescript 假装表达式计算为 SomeType 类型的值的快速“解决方案”:

    export const ThirdPartySDK: SomeType = (function() {
        // ...
    })() as SomeType;
    

    2。保留&lt;script&gt;标签并声明变量

    另一个选项是保留脚本标签,不导入任何内容,以及declare the variable 及其在打字稿中的预期类型:

    (但在这种情况下,您可能必须自己提供类型定义)

    interface SomeType {
        // SDK type shape goes here...
    }
    
    declare const ThirdPartySDK: SomeType;
    

    【讨论】:

    • 谢谢!我有点不愿意用导出语句修改源文件,因为我希望能够在新版本出现时插入它。声明可能是唯一的方法,但我希望我可以做一些聪明的事情,比如将 IIFE 的全局范围隔离/导出到单独的模块或其他东西中。
    【解决方案2】:

    您可以使用eval 的hack 将第三方SDK 包装在TypeScript 模块中。

    假设 ThirdPartySDK.js 如下所示:

    var ThirdPartySDK = (function () {
        var exports = {
            foo: function () { console.log("Hello, world!"); }
        };
        return exports;
    })();
    

    然后,您将创建一个 ThirdPartySDK-wrapper.ts 模块,如下所示:

    import * as fs from 'fs';
    const script = fs.readFileSync('../lib/ThirdPartySDK.js').toString();
    
    global.eval(script);
    
    //@ts-ignore
    export default ThirdPartySDK;
    

    @ts-ignore 指令用于防止 TypeScript 编译器抱怨找不到 ThirdPartySDK 变量的声明(它在通过 eval 执行的脚本中声明)。

    然后您可以通过包装模块导入第三方SDK:

    import ThirdPartySDK from './wrapper';
    ThirdPartySDK.foo();  // Console output: "Hello, world!" 
    

    请注意,此包装器仅适用于在 Node.js 中运行的应用程序,因为它使用 fs.fileReadSync 来获取脚本的内容。

    如果您要在浏览器中使用它,您将需要一些其他方法来检索脚本。您可能可以使用 WebPack 等框架将 ThirdPartySDK 脚本捆绑为字符串值,您可以在包装模块中使用 require

    【讨论】:

    • 不幸的是在浏览器中。考虑到过去有多少 IIFE 模块正在运行,我有点惊讶没有标准的解决方案。
    • @ZacDelventhal:好的,那么您使用的是哪个模块加载器?调整这种方法在浏览器中也可以工作应该不会太难。
    • 这是一个使用 webpack 的 Angular 6/TypeScript 前端。
    • @ZacDelventhal:我已经很久没有玩过 Angular 或 Webpack(当时是 Angular 2.0),但我可以尝试一下以了解如何制作的详细信息我的方法在那里工作,如果这就是您接受我的回答所需要的:-) 同时,您可能希望编辑问题以包含此要求并添加webpack 标签。
    • 哈哈。我喜欢你的回答,但我认为这并不理想,至少考虑到我的工具链。我现在正在使用 angular/webpack 解决方案,如果我能正常工作,我会发布它。听取了您的建议并更新了标签/标题/描述以反映整个工具链。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-14
    • 2020-06-01
    • 2021-01-14
    • 2017-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多