Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typescript文件模块知多少 #51

Open
Rashomon511 opened this issue Aug 13, 2019 · 0 comments
Open

Typescript文件模块知多少 #51

Rashomon511 opened this issue Aug 13, 2019 · 0 comments

Comments

@Rashomon511
Copy link
Owner

前言

Typescript将内部模块称为命名空间,外部模块称为模块,我们就来介绍下模块的用法。

命名空间

一般情况下,当你在一个新的Typescript文件中写下代码时,它会处于全局命名空间中,比如你声明一个变量,该变量是在全局可以访问到的。

// foo.ts
const foo = 123;
// bar.ts
const bar = foo;

使用全局变量空间是危险的,因为它会与文件内的代码命名冲突,为了确保变量不会泄漏至全局变量,我们可以使用文件模块来解决这个问题。
如在 TypeScript 文件的根级别位置含有 import 或者 export,它就会在这个文件中创建一个本地的作用域,如果其他文件想要使用变量,必须显示的导入该变量,上述例子可以进行以下修改:

// foo.ts
export const foo = 123;
// bar.ts
import { foo } from './foo';
const bar = foo; // allow

避免全局变量污染的第二种方法是使用 namespace 的外部的模块

namespace Utility {
  export const foo = 123;
}

console.log(Utility.foo) // 123

命名空间是支持嵌套的。因此,你在在一个命名空间下嵌套另外一个命名空间 ,对于快速的演示和移植旧的 JavaScript 代码。推荐使用namespace 的外部的模块。

文件模块

言归正传,既然文件模块有如此强大的功能,我们就来学习他的一些用法。

模块导出

任何声明(比如变量,函数,类,类型别名或接口)都能够通过添加export关键字来导出,当我们需要导出大量的内容时,可以采用这种方式。

export const someVar = 123;
export const numberRegexp = /^[0-9]+$/;
export type someType = {
  foo: string;
};
export interface StringValidator {
    isAcceptable(s: string): boolean;
}
export class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}

除了上面的写法,还可以以另一种方式书写

const someVar = 123;
const numberRegexp = /^[0-9]+$/;
export { someVar, numberRegexp };

或者可以重新命名变量导出

const someVar = 123;
export { someVar as aDifferentName };

重新导出
通过重命名,部分导出从另一个模块导入的项目。想要扩展某个模块的功能可以使用重新导出,因为重新导出可以不要去改变原来的对象,而是导出一个新的实体来提供新的功能。

export { someVar as aDifferentName } from './foo';

从其他模块导出后部分导出

export { someVar } from './foo';

从其他模块导出后整体导出

export * from './foo';

模块导入

使用import关键字导入一个变量或者类型,需要明确的导出模块名称时使用这种方式。

import { someVar, someType } from './foo';

可以对导入对模块进行重命名

import { someVar as aDifferentName } from './foo';

用星号(*)指定一个变量,所有输出值都加载在这个变量上面,进行整体导入

import * as foo from './foo';

具有副作用的导入模块,一些模块会设置一些全局状态供其它模块使用,一般不推荐这么做

import "./my-module.js";

默认导入/导出

默认导出使用 default关键字标记;并且一个模块只能够有一个default导出,如果仅导出单个 class 或 function,使用 export default。
使用方式:

  • 在一个变量之前(不需要使用 let/const/var);
  • 在一个函数之前;
  • 在一个类之前
// some var
export default (someVar = 123);
// some function
export default function someFunction() {}
// some class
export default class someClass {}

导入使用import即可

import foo from './foo';

export = 和 import = require()

CommonJS和AMD的环境里都有一个exports变量,为了支持CommonJS和AMD的exports, TypeScript提供了export =语法.
export =语法定义一个模块的导出对象。 这里的对象一词指的是类,接口,命名空间,函数或枚举

export class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}
export = ZipCodeValidator

若使用export =导出一个模块,则必须使用TypeScript的特定语法import module = require("module")

import ZipCodeValidator = require("./ZipCodeValidator");

重写类型的动态查找

在项目里,也可以通过 declare module 'somePath' 来声明一个全局模块的方式,用来解决查找模块路径的问题

// globals.d.ts
declare module 'foo' {
  // some variable declarations
  export var bar: number;
}
// other.ts
import * as foo from 'foo';

模块懒加载

编译器会检测是否每个模块都会在生成的JavaScript中用到。 如果一个模块只在类型注解部分使用,并且完全没有在表达式中使用时,就不会生成 require这个模块的代码,如下例子:

import foo = require('foo');
var bar: foo;
// 编译的js
let bar

在某些情形下,只想在需要时加载模块,此时需要仅在类型注解中使用导入的模块名称,而不是在变量中使用。那么在编译javascript时,就不会生成 require这个模块的代码.
如下基于commonjs例子中,使用typeof关键字来获取foo的类型

import foo = require('foo');

export function loadFoo() {
  // 这是懒加载 foo,原始的加载仅仅用来做类型注解
  const _foo: typeof foo = require('foo');
  // 现在,你可以使用 `_foo` 替代 `foo` 来做为一个变量使用
}

一个同样简单的 amd 模块(使用 requirejs)

import foo = require('foo');

export function loadFoo() {
  // 这是懒加载 foo,原始的加载仅仅用来做类型注解
  require(['foo'], (_foo: typeof foo) => {
    // 现在,你可以使用 `_foo` 替代 `foo` 来做为一个变量使用
  });
}

使用场景:

  • 在 web app 里, 当你在特定路由上加载 JavaScript 时;
  • 在 node 应用里,当你只想加载特定模块,用来加快启动速度时

总结

我们了解到来文件模块的众多使用方法,以及相应使用方法下的应用场景,我们可以根据业务需要采取合适的模块导入导出方式。

参考

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant