44

我花了一些时间阅读 Typescript 语言规范,对内部模块和外部模块之间的区别有些困惑。以下是直接取自规范的描述:

内部模块(第 9.2.2 节)是其他模块(包括全局模块和外部模块)的本地或导出成员。内部模块使用 ModuleDeclarations 声明,指定它们的名称和主体。具有多个标识符的名称路径等效于一系列嵌套的内部模块声明。

外部模块(第 9.4 节)是使用外部模块名称引用的单独加载的代码体。外部模块被编写为单独的源文件,其中至少包含一个导入或导出声明。此外,可以在全局模块中使用 AmbientModuleDeclarations 声明外部模块,直接将外部模块名称指定为字符串文字。这将在第 0 节中进一步描述。

据我了解,我认为外部模块对应于打字稿文件,而没有包含仅导出一组类型和/或变量的模块定义。从另一个打字稿文件,我可以简单地在foo.ts中导入一个外部模块import foo = module("foo");

有人可以向我解释外部模块和内部模块之间的关系吗?

4

4 回答 4

26

规范的第 9.3 和 9.4 节更清楚地解释了这一点。我将在此处重现这些部分中给出的一些示例。

外部模块

假设下面的代码在main.ts.

import log = module("log");
log.message("hello");

该文件引用了一个外部模块log,由任何log.ts导出定义。

export function message(s: string) { 
  console.log(s); 
}

请注意,log.ts不要在module任何地方使用关键字。它只是用export.

内部模块

该文件有两个内部模块,X.Y.Z.

module A.B.C { 
  import XYZ = X.Y.Z; 
  export function ping(x: number) { 
    if (x > 0) XYZ.pong(x – 1); 
  }
} 
module X.Y.Z { 
  import ABC = A.B.C; 
  export function pong(x: number) { 
    if (x > 0) ABC.ping(x – 1); 
  } 
}

它们的行为(大部分)类似于外部模块,但它们包含在一个文件中,您无需引用任何外部文件即可使用它们。定义它们时,它们必须包含在module块内。

于 2012-10-11T14:33:04.660 回答
7

根据 Anders 的介绍:http ://channel9.msdn.com/posts/Anders-Hejlsberg-Introducing-TypeScript (34:40) 和 Typescript 文档,外部模块是基于顶级 AMD(异步模型定义)或通用JS。

外部模块在某种意义上很有用,它们隐藏了模块定义的内部语句,只显示与声明的变量相关的方法和参数。

假设您有一个Main具有定义log方法的类放置在transfer.js文件中。该类的内部方法仅在您将源 js 文件顶部的文件Main导入时可见: . 这样编译器就消除了运行时对所有js文件的遍历。transfer.js///<reference path="transfer.js"/>

这是使用外部模块的巨大好处。另一种是当您尝试引用外部方法或类时,在正常的自上而下的 javascript 流中定义的时间晚于方法调用。使用外部模块,引用的类仅在方法调用时实例化。

于 2012-10-11T14:49:17.750 回答
2

内部模块:

  1. 您可以在 typescriptp 文件中定义模块。
  2. 模块中定义的所有变量都限定在模块范围内并从全局范围中删除。
  3. 当你编译你的 typescript 文件时,你的模块会被转换为变量,这些变量会根据需要嵌套以形成类似命名空间的对象。请注意,模块中定义的类使用 IIFE(立即调用函数表达式)巧妙地隔离。
  4. 下面的代码显示了 MyClass 变量的作用域为 MyInternalModule 模块。它们无法在模块外部访问,这就是为什么代码的最后一行显示错误找不到名称 MyClass。
  5. 您可以使用 export 关键字访问模块外部的变量。
  6. 您还可以扩展内部模块,跨文件共享它们,并使用三斜杠语法引用它们。( /// )

示例

module MyInternalModule{  
    class MyClass{               //if We write export keyword before the MyClass then last line works fine
        constructor (
            public height: number, 
            public width: number) {
    }
    }                   
    //working properly
    var obj1 = new MyClass(10, 4);
}

// it wont work //Because the out of the scope
var obj2 = new MyInternalModule.MyClass(10,4) //shows error: can not find name MyClass

打字稿的编译版本:

var MyInternalModule;
(function (MyInternalModule) {
    var MyClass = (function () {
        function MyClass(height, width) {
            this.height = height;
            this.width = width;
        }
        return MyClass;
    })();
    //working properly
    var obj1 = new MyClass(10, 4);
})(MyInternalModule || (MyInternalModule = {}));

外部模块:

示例

// bootstrapper.ts file

// imports the greeter.ts file as the greeter module
import gt = module('greeter');  
export function run() {  
    var el = document.getElementById('content');
    var greeter = new gt.Greeter(el);
    greeter.start(); 
}

// greeter.ts file

// exports the entire module
export class Greeter {  
    start() {
         this.timerToken = setInterval(() => 
             this.span.innerText = 
             new Date().toUTCString(), 500);
    }
}
于 2016-09-29T07:35:15.520 回答
0

内部模块:namespacemodule关键字

宣言

内部模块可以使用namespacemodule关键字声明。然后我们可以决定使用export关键字公开我们内部模块的哪一部分。

// LivingThings.ts
export namespace Animals {
    export class Dog { }
    export class Cat { }
}
export namespace Plants {
    export class Orchid { }
    export class Bamboo { }
}

// LivingThingsUser.ts
import { Animals, Plants } from "./LivingThings"

逻辑分组

在 ES6 之前,Typescript 使用内部模块来封装接口、类、函数和变量,以支持一组相关功能并隐藏实现细节。这样我们可以防止变量泄漏到全局空间中。这有助于更好地组织代码并防止名称冲突。现在推荐使用外部模块(ES6 模块)来实现这一点。

内部模块现在用于环境命名空间声明。

单个文件使用

我们可以跨多个文件声明内部模块,并且可以使用--outFile标志将它们连接起来。<script>然后,我们可以在 HTML 页面的标签内使用该连接文件。这允许我们在包含所有依赖项的客户端 Web 应用程序中以良好的方式构建我们的代码。


外部模块:justexportimport关键字

宣言

外部模块也称为 ES6 模块。我们使用多个文件对相关功能进行分组,并且只使用export关键字使所需对象公开可见。

// Animals.ts
export class Dog { }
export class Cat { }

// Plants.ts
export class Orchid { }
export class Bamboo { }

// LivingThingsUser.ts
import { Dog, Cat } from "./Animals"
import { Orchid, Bamboo } from "./Plants"

逻辑分组

逻辑分组是通过使用单独的文件对相关功能进行分组来实现的。因此,外部模块也称为文件模块

单个文件使用

我们不使用<script>标签加载客户端 Web 应用程序的外部模块,因为浏览器可能会在下载这么多文件并同时呈现页面时变得迟缓。为此,我们使用 CommonJS、AMD、SystemJS 等模块加载器,它们使我们能够异步加载文件或将外部模块文件连接到单个优化文件中。

对于服务器端,尤其是在 Node.js 中,强烈建议使用外部模块。


命名法

为了声明内部模块,打字稿团队建议使用namespace { }代替module { }语法以避免与外部模块混淆。因为外部模块现在只是“模块”,而内部模块是“命名空间”。

就是这样!

于 2020-08-12T16:05:00.277 回答