模块与命名空间 – 导入vs需要打字稿
我对module/namespace/export
和import, require, reference
用法感到困惑。 从Java的背景,有人可以解释我什么时候使用什么和什么是正确的devise? 当我写样本项目时,我觉得自己很乱
到目前为止,这是我的理解1. module
是外部的包2. namespace
是内部的包
- 我没有得到我们如何分类他们?
- 何时导出类或名称空间或包?
- 如果我们导出包/名称空间,那么所有的类都被导出或需要被显式导出
- 他们每个人如何import/需要?
根据文档 ,如果我为每个经理/模型创build每个“ts”文件,Typescript不build议使用“命名空间”? 直接使用参考path?
Please explain in detail as I am coming from different background and not sure about ES6/ES5 etc
。
我看到有几个人正在提出/混淆同样的问题。 我希望有人能够详细解释真实世界的情况
我没有得到我们如何分类他们?
命名空间用于组织/封装您的代码。 外部模块用于组织/封装您的代码,并在运行时查找您的代码。 实际上,在运行时有两种select:1)将所有转译的代码合并到一个文件中,或2)使用外部模块,并有多个文件,并需要其他一些机制来获取这些文件。
何时导出类或名称空间或包?
要使文件外部的types或值可见,必须将其导出到名称空间中。 无论是在顶层还是在命名空间中导出,都将决定它是否在外部模块中。
如果我们导出包/名称空间,那么所有的类都被导出或需要被显式导出
命名空间中的类将始终需要显式导出,以使该类在编译时在定义该文件的文件之外可见。
他们每个人如何import/需要?
这取决于你是否使用外部模块。 总是需要导入一个外部模块来“使用”它。 导入不在外部模块中的命名空间实际上只是为命名空间提供了一个别名 – 您仍然必须在别名前面加上types的前缀(这就是为什么您通常不希望将命名空间与外部模块一起使用;这样做意味着在引用外部模块提供的任何东西时,总是必须使用前缀。)不在外部模块中的命名空间可以跨越文件,因此如果您位于同一个命名空间中,则可以引用命名空间,而不需要任何导入。
要真正了解上述,你需要一些背景知识。 使用引用/名称空间/外部模块来理解的关键是这些构造在编译时所做的事情以及它们在运行时所做的事情。
编译时使用引用指令来定位types信息。 你的源代码中有一个特定的符号。 TypeScript编译器如何定位该符号的定义? 参考指令大部分被tsconfig.json机制包含 – 使用tsconfig.json,告诉编译器你所有的源是哪里。
命名空间可以包含types定义和/或实现。 如果一个名字空间只包含types信息,那么它根本就没有运行时performance – 你可以通过查看JS输出并find一个空的JS文件来检查它。 如果一个名字空间有实现代码,那么这个代码被封装在一个名为空间的全局variables中。 使用嵌套的命名空间,将会有一个全局variables用于根名称空间。 再一次,检查JS输出。 命名空间历史上是如何JS客户端库试图避免命名冲突的问题。 我们的想法是将整个库封装到一个闭包中,然后尽可能小地展示全局空间 – 只有一个引用闭包的全局variables。 那么,问题仍然是你在全球空间声称了一个名字。 如果你想要两个版本的图书馆怎么办? TypeScript命名空间仍然存在如何定位命名空间的来源的问题。 也就是说,引用AB的源代码仍然存在告诉编译器如何定位AB的问题 – 无论是使用引用指令还是使用tsconfig.json。 或者将名称空间放入外部模块,然后导入外部模块。
外部模块起源于服务器端JS。 在外部模块和文件系统上的文件之间存在一对一的对应关系。 您可以使用文件系统目录结构将外部模块组织为嵌套结构。 导入外部模块通常会引入对该外部模块的运行时依赖(例外情况是,当您导入外部模块但不使用任何导出的值时 – 也就是说,只导入外部模块获取其types信息)。 一个外部模块隐含在闭包中,这是关键:模块的用户可以将闭包分配给任何他们想要的局部variables。 TypeScript / ES6增加了将外部模块的导出映射到本地名称的附加语法,但这只是一个很好的解释。 在服务器端,定位一个外部模块是比较简单的:只需在本地文件系统中find代表外部模块的文件。 如果您想在客户端使用外部模块,则在浏览器中使用外部模块会变得更加复杂,因为与具有可加载模块的文件系统不同。 所以现在在客户端,你需要一种方法来将所有这些文件打包成一个可以在浏览器中远程使用的forms – 这就是Webpack这样的模块打包器(Webpack做的比捆绑模块要多得多)和Browserify发挥作用。 模块打包器允许运行时parsing浏览器中的外部模块。
真实的世界场景:AngularJS。 假装外部模块不存在,使用单个命名空间来限制对全局空间的污染(在下面的示例中,单个variablesMyApp全部在全局空间中),仅导出接口,并使用AngularJSdependency injection来实现可供使用。 把所有的类都放到一个目录根目录下,把tsconfig.json添加到根目录下,在同一个目录根目录下安装angularjs typings,这样tsconfig.json也可以把它选中,把所有的输出整合到一个JS文件中。 如果代码重用不是很重要的话,这对大多数项目来说都可以正常工作。
MyService.ts:
namespace MyApp { // without an export the interface is not visible outside of MyService.ts export interface MyService { .... } // class is not exported; AngularJS DI will wire up the implementation class MyServiceImpl implements MyService { } angular.module("MyApp").service("myService", MyServiceImpl); }
MyController.ts:
namespace MyApp { class MyController { // No import of MyService is needed as we are spanning // one namespace with multiple files. // MyService is only used at compile time for type checking. // AngularJS DI is done on the name of the variable. constructor(private myService: MyService) { } } angular.module("MyApp").controller("myController", MyController); }
使用IIFE来避免污染全局运行时范围。 在这个例子中,根本没有创build全局variables。 (假定有tsconfig.json。)
Foo.ts:
namespace Foo { // without an export IFoo is not visible. No JS is generated here // as we are only defining a type. export interface IFoo { x: string; } } interface ITopLevel { z: string; } (function(){ // export required above to make IFoo visible as we are not in the Foo namespace class Foo1 implements Foo.IFoo { x: string = "abc"; } // do something with Foo1 like register it with a DI system })();
Bar.ts:
// alias import; no external module created import IFoo = Foo.IFoo; (function() { // Namespace Foo is always visible as it was defined at // top level (outside of any other namespace). class Bar1 implements Foo.IFoo { x: string; } // equivalent to above class Bar2 implements IFoo { x: string; } // IToplevel is visible here for the same reason namespace Foo is visible class MyToplevel implements ITopLevel { z: string; } })();
使用IIFE你可以在第一个例子中消除把MyApp作为一个全局variables。
MyService.ts:
interface MyService { .... } (function() { class MyServiceImpl implements MyService { } angular.module("MyApp").service("myService", MyServiceImpl); })();
MyController.ts:
(function() { class MyController { constructor(private myService: MyService) { } } angular.module("MyApp").controller("myController", MyController); })();
有两件事情:
- TypeScript中的模块是一个标准的ES6概念,它在代码的顶层使用
import
/export
关键字; - 命名空间是特定于TypeScript的概念,以帮助以过时的方式组织代码。
命名空间
这几乎是一个过时的概念。 在使用ES6模块之前,在浏览器中分离JavaScript代码的常见方法是创build全局variables。 例如, 下划线之类的API的所有function都位于名为_
的全局variables中。
这种继续进行的方式就像Java包或PHP命名空间。 它不适合于networking。 新的ECMAScript标准解决了如下问题:如何使用两个同名的库? 如何使用同一个库的两个不同版本?
注意:在“模块”(2014年夏季)的ECMAScript定义之前的TypeScript版本中,名称空间被称为“内部模块”,模块被称为“外部模块”。
注意2: namespace
内的关键字export
是关键字的非标准TypeScript用法。 这是宣布从名称空间外公开访问的一种方法。
ES6模块
模块是一个包含关键字import
或export
的代码顶级文件。
TypeScript遵循ECMAScript的标准。 我build议在Mozilla的文章中阅读一个关于ES6模块的好介绍 。
如果你想在前端应用程序(在浏览器)中使用模块,那么你将不得不使用一个捆绑器( Webpack [ 这里是文档 ],Browserify)或一个加载器( SystemJS [ 这里的教程 ],RequireJS)和用这个环境configurationTypeScript。
如果你的代码是在Node.js中执行的,只需要configurationTypeScript编译器来生成CommonJS格式。
注意:名称空间可以在模块中声明。 在这种情况下,它将不能作为模块外部的全局variables访问。 但是,它可以从模块中导出。
- 模块用于外部包2.名称空间用于内部包
实际上, module
关键字已经被namespace
关键字所取代。
一个更好的语句是这样的: 模块是以前称为外部模块的模块, 命名空间是以前称为内部模块的模块。
更多
希望这有助于进一步: https : //basarat.gitbooks.io/typescript/content/docs/project/modules.html
“ 要求 ”和“ 导入 ”在function上是等效的。 我们可以互换使用它们,因为我们拥有的转译器并不关心浏览器是否支持本机。 但是,“ 要求 ”源于CommonJS的旧编码风格,而“导入”则是从广泛接受的ES6(ES2015)语法中衍生出来的。 所以你应该使用“ import ”的新项目,而不是“ 要求 ”。