我却看到很多与困惑module/namespace/export
和import, require, reference
使用.来自Java背景,有人可以简单地解释我何时使用什么和什么是正确的设计?在编写示例项目时,我觉得自己搞砸了
到目前为止,这是我的理解1. module
适用于外部包2. namespace
适用于内部包
根据doc,如果我为每个管理器/模型创建每个"ts"文件,Typescript不建议使用"命名空间"?直接使用参考路径?
请详细解释,因为我来自不同的背景,不确定ES6/ES5等.
我看到有几个人对同样的问题感到困惑.我希望有人可以用现实世界的场景详细解释
kay*_*tea 72
我没有得到我们如何分类他们?
命名空间用于组织/封装您的代码.外部模块用于组织/封装代码并在运行时定位代码.在实践中,您在运行时有两个选择:1)将所有已转换的代码组合到一个文件中,或2)使用外部模块并拥有多个文件,并需要一些其他机制来获取这些文件.
何时导出类或命名空间或包?
要使类型或值在其所在文件之外可见,如果它在命名空间内,则必须将其导出.无论是在顶层还是在命名空间中导出它,都将决定它现在是否在外部模块中.
如果我们导出包/命名空间,则导出其中的所有类或者需要显式导出它们
始终需要显式地导出命名空间中的类,以便在编译时在编译它的文件之外可见.
如何导入/需要每个人?
这取决于您是否使用外部模块.始终需要导入外部模块以"使用"它.导入不在外部模块中的命名空间实际上只是为命名空间提供别名 - 您仍然必须使用别名为类型/任何内容添加前缀(这就是为什么您通常不希望将命名空间与外部模块一起使用;这样做意味着在引用外部模块提供的任何内容时总是必须使用前缀.)不在外部模块中的命名空间可以跨文件,因此如果您在同一名称空间中,则可以引用由命名空间,无需任何导入.
要真正理解上述内容,您需要一些背景知识.使用references/namespaces/external modules理解的关键是这些构造在编译时的作用以及它们在运行时的作用.
编译时使用引用指令来定位类型信息.您的来源中有一个特定的符号.TypeScript编译器如何定位该符号的定义?参考指令主要由tsconfig.json机制包含 - 使用tsconfig.json,您告诉编译器所有源都在哪里.
命名空间可以包含类型定义和/或实现.如果命名空间仅包含类型信息,那么它根本没有运行时表现形式 - 您可以通过查看JS输出并查找空JS文件来检查它.如果命名空间具有实现代码,则代码将封装在一个闭包内,该闭包分配给与名称空间同名的全局变量.使用嵌套的命名空间,根名称空间将有一个全局变量.再次,检查JS输出.历史上,命名空间是JS客户端库如何尝试避免命名冲突的问题.我们的想法是将整个库封装到一个闭包中,然后尽可能小地展示全局 - 只需一个引用闭包的全局变量.嗯,问题仍然是你在全球空间声称了一个名字.如果您想要两个版本的库,该怎么办?TypeScript命名空间仍然存在如何定位命名空间源的问题.也就是说,引用AB的源代码仍然存在告诉编译器如何定位AB的问题 - 通过使用引用指令或使用tsconfig.json.或者将命名空间放入外部模块,然后导入外部模块.
外部模块源自服务器端JS.外部模块与文件系统上的文件之间存在一对一的对应关系.您可以使用文件系统目录结构将外部模块组织为嵌套结构.导入外部模块通常会在外部模块上引入运行时依赖性(例外情况是导入外部模块但不在值位置使用任何导出时 - 也就是说,只导入外部模块获取其类型信息).外部模块隐式位于闭包中,这是关键:模块的用户可以将闭包分配给他们想要的任何局部变量.TypeScript/ES6增加了将外部模块的导出映射到本地名称的附加语法,但这只是一个非常好的方法.在服务器端,定位外部模块相对简单:只需在本地文件系统上找到代表外部模块的文件即可.如果要在客户端使用外部模块,在浏览器中,它会变得更复杂,因为没有与可用于加载的模块的文件系统等效.所以现在在客户端你需要一种方法将所有这些文件捆绑到一个可以在浏览器中远程使用的表单 - 这就是像Webpack这样的模块捆绑器(Webpack比捆绑模块更多)和Browserify发挥作用.模块捆绑器允许在浏览器中运行时解析外部模块.
真实场景:AngularJS.假设外部模块不存在,使用单个命名空间来限制全局空间的污染(在下面的示例中,单个变量MyApp就在全局空间中),仅导出接口,并使用AngularJS依赖注入来实现可供使用.将所有类放在目录根目录中,将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);
}
Run Code Online (Sandbox Code Playgroud)
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);
}
Run Code Online (Sandbox Code Playgroud)
使用IIFE避免污染全局运行时范围.在此示例中,根本不会创建全局变量.(假设是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
})();
Run Code Online (Sandbox Code Playgroud)
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;
}
})();
Run Code Online (Sandbox Code Playgroud)
使用IIFE可以消除在第一个示例中将MyApp作为全局变量的引入.
MyService.ts:
interface MyService {
....
}
(function() {
class MyServiceImpl implements MyService {
}
angular.module("MyApp").service("myService", MyServiceImpl);
})();
Run Code Online (Sandbox Code Playgroud)
MyController.ts:
(function() {
class MyController {
constructor(private myService: MyService) {
}
}
angular.module("MyApp").controller("myController", MyController);
})();
Run Code Online (Sandbox Code Playgroud)
Pal*_*leo 25
有两件事:
import
/ export
关键字在顶层的代码;这几乎是一个过时的概念.在ES6模块之前,在浏览器中分离JavaScript代码的常用方法是创建全局变量.例如,像下划线这样的API的所有函数都位于一个名为的全局变量中_
.
这种旧的方法就像Java包或PHP命名空间一样.它不适应Web.新的ECMAScript标准解决了以下问题:如何使用两个具有相同名称的库?如何使用同一个库的两个不同版本?
注意:在ECMAScript定义"modules"(2014年夏季)之前的TypeScript版本中,名称空间称为"内部模块",模块称为"外部模块".
通知2:关键字export
内的namespace
是该关键字的非标准打字稿用法.它是声明可以从命名空间外部公开访问的东西的方法.
模块是包含关键字import
或export
位于代码顶层的文件.
TypeScript遵循ECMAScript的标准.我建议在Mozilla的一篇文章中阅读对ES6模块的一个很好的介绍.
如果你想在前端应用程序中使用模块(在浏览器中),那么你将不得不使用一个bundle(Webpack [ 这里的文档 ],Browserify)或一个加载器(SystemJS [ 这里的教程 ],RequireJS)和使用此环境配置TypeScript.
如果您的代码在Node.js中执行,只需配置TypeScript编译器以生成CommonJS格式.
注意:可以在模块中声明命名空间.在这种情况下,它不能作为模块外部的全局变量访问.但是,它可以从模块导出.
bas*_*rat 13
- module用于外部包2. namespace用于内部包
实际上module
关键字已被关键字替换namespace
.
因此,一个更好的声明是模块,它曾经被称为外部模块,命名空间是以前被称为内部模块的.
希望这有助于进一步:https://basarat.gitbooks.io/typescript/content/docs/project/modules.html
在 Typescript 的早期,命名空间被称为内部模块,ES6 模块被称为外部模块。
现在,为了声明命名空间,Typescript 团队建议使用namespace { }
代替module { }
语法,以避免与外部模块的命名混淆。因为外部模块现在只是“模块”,而内部模块是“命名空间”。
宣言
Typescript 中的命名空间可以使用namespace
或module
关键字来声明。这两个关键字都做同样的事情。然后我们可以使用关键字决定命名空间的哪一部分公开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"
Run Code Online (Sandbox Code Playgroud)
逻辑分组
在 ES6 之前,Typescript 中使用命名空间来封装接口、类、函数和变量,以支持一组相关功能并隐藏实现细节。这样我们就可以防止变量泄漏到全局空间中。这有助于更好地组织代码并防止名称冲突。现在推荐使用ES6模块来实现这一点。
命名空间现在用于环境命名空间声明。
单个文件使用
我们可以跨多个文件声明命名空间,并且可以使用标志将它们连接起来--outFile
。<script>
然后我们可以在 HTML 页面的标记内使用该串联文件。这使我们能够在包含所有依赖项的客户端 Web 应用程序中以良好的方式构建代码。
宣言
模块也称为 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"
Run Code Online (Sandbox Code Playgroud)
逻辑分组
模块中的逻辑分组是通过使用单独的文件对相关功能进行分组来实现的。因此,外部模块也称为文件模块。
单个文件使用
我们不使用标签加载客户端Web应用程序的模块<script>
,因为在下载如此多的文件并同时渲染页面时,浏览器可能会变得缓慢。为此,我们使用 CommonJS、AMD、SystemJS 等模块加载器,使我们能够异步加载文件或将外部模块文件连接到单个优化文件中。
对于服务器端,尤其是 Node.js,强烈推荐使用这些模块。
就是这样!
“ require ”和“ import ”在功能上是等效的。我们可以互换使用它们,因为我们的编译器并不在乎浏览器是否本身支持它们。但是,虽然“ require ”起源于CommonJS到2009年的旧编码风格,但“ import”却从广泛接受的ES6(ES2015)语法派生其语法。因此,对于新项目,您应该使用“ import ”,而不是“ require ”。
归档时间: |
|
查看次数: |
28334 次 |
最近记录: |