模块
●前言:
JavaScript 在 ES2015 中引入了模块的概念,我们的 JS 也开始经历标准的模块化开发阶段,而这个概念在 TS 中也得到了继承。
TS中,从句型到概念,与JavaScript中基本相同
●因为我们主要学习TS,所以这里对模块的句型和概念进行简单的准备。
出口
●在一个文件中,可以使用export关键字导入所有声明
●实际上,内容是暴露给外界并被其他模块使用的。
基本导入
●moduleA.ts
// 导出一个变量
export const num = 100
export const str = 'hello world'
export const reg = /^千锋教育$/
// 导出一个函数
export function fn() {}
// 导出一个类
export class Student {}
export class Person extends People {}
// 导出一个接口
export interface Users {}
重新进口
●有时我们可能会导出文件中的一段内容,然后再次导入
●modeuB.ts
export * from './moduleA'
○将moduleA文件的所有内容导出到此moduleB文件中
○ 并在 moduleB 中进行了导入
●很多朋友可能会问,这有什么意义呢?
○ 其实有些模块是可以合并的
●举个例子
○模块A.ts
export const num = 100
○模块B.ts
export const str = 'hello world'
○模块C.ts
export interface Users {}
○moduleIndex.ts
export * from './moduleA'
export * from './moduleB'
export * from './moduleC'
○这样就相当于使用moduleIndex文件将以下三个模块集成导入为一个
导出重命名
●有时,我们重新导入时,可能会遇到多个模块重名的问题
●也就是说,你可能会遇到多个模块不是一个人写的,导致模块命名风格不一致。
●我们写入的时候导入的时候可以重命名
●语法
export { num as current } from './moduleA'
○相当于导出该文件中的moduleA模块中的num数据
○ 并重新进口
○只是再次导入时,使用的名称是当前的。
● 举一个反例
○模块A.ts
export const n = 100
○模块B.ts
export const s = 'hello world'
○模块C.ts
export interface Aaa {}
○moduleIndex.ts
export { n as num } from './moduleA'
export { s as str } from './moduleB'
export { Aaa as Users } from './moduleC'
○这样就相当于使用moduleIndex文件将以下三个模块集成导入为一个
○并且上述三个模块导入的内容已更名
○ 相当于
// 伪代码, 仅仅为了直观看一下上面代码的效果
export const num = 100 // moduleA 中导出的 n
export const str = 'hello world' // moduleB 中导出的 s
export interface Users {} // moduleC 中导出的 Aaa
进口
●与以往进口配套
基本导出
●直接导入模块的某些或个别内容
● moduleA.ts => 导入
export function study () {}
export function play () {}
export function sleep () {}
●index.ts => 用于导出
import { study, play } from './moduleA'
导入重命名
●导入时,有时我们可能不想在原始文件中使用导入的名称。
●无论是因为命名冲突还是因为原来的名称太长,都有可能。
●然后我们就可以进行重命名操作了
● moduleA.ts => 导入
export function study () {}
export function play () {}
export function sleep () {}
●index.ts => 用于导出
import { study as s } from './moduleA'
○相当于在moduleA中导入一个study方法
○但是在索引文件中使用时,使用函数名s
默认导入
●每个模块都会有一个默认导入,使用default即可
●注意:一个模块只能有一次默认导入
●moduleA.ts
const utils = {
study () {},
play () {},
sleep () {}
}
export default utils
○这是默认导入方式下moduleA导入的utils对象。
● 索引.ts
import XhlUtils from './moduleA'
○这里索引中的XhlUtils获取的是moduleA文件中导入的utils对象。
模块化兼容
●前面的都是JS的模块句型。 我们相当于准备考试或者复习
●现在这块是TS对模块化的改进,大家要注意学习
前言:
虽然我们刚才说的是ES6 Module的句型规范
但我们知道,在JS的开发过程中typescript 内部模块,我们并不是唯一的模块化语句规范
我们还有 AMD/CommonJS/UMD 等模块化句子规范
每个都有自己的导出和导入句型规范
但这些句型中有一个共同的规范,那就是exports变量的出现
导出变量
CommonJS 和 AMD 中都有一个导出对象
是这两个模块化规范的默认导入,相当于ES6模块化规范中的默认导出
虽然你们的句型相似,意思也相似
但ES6模块化句型不兼容CommonJS和AMD语法规范
因此,TS中对ES6的模块句型进行了一些扩展
编译时可以更好的支持CommonJS句型
出口=
●如果你只是用TS做后端开发,不需要考虑模块化句型的兼容性typescript 内部模块,那么你就不需要理解这个
● 仅当需要兼容CommonJS和AMD语法时
●那么我们的导出默认就不能用了
●index.ts => 不考虑兼容性
class Person {}
export default Person
●index.ts => 考虑兼容性
class Person {}
export = Person
导入 = 要求('')
●为了支持兼容性,我们的导入句型发生了变化,所以我们的导出句型也会发生变化
●index.ts => 不考虑兼容性
import Person from './xxx'
●index.ts => 考虑兼容性
import Person = require('./xxx')
编译结果
●如果按照兼容的形式写导入内容,会编译成以下情况(看一下)
○简单模块
import Person = require('./xxx')
○AMD(需要JS)
define(["require", "exports", "./xxx"], function (require, exports, mod_1) {
console.log(mod_1)
});
○CommonJS(节点)
const Person = require('./xxx')
○UMD
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
let v = factory(require, exports); if (v !== undefined) module.exports = v
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "./xxx"], factory)
}
})(function (require, exports) {
let Person = require("./xxx")
console.log(Person)
});
○系统简单模块
System.register(["./xxx"], function(exports) {
let Person
return {
setters: [
function (param) {
Person = param
}],
execute: function() {
console.log(Person)
}
}
})
○原生ES6模块
import Person from './xxx'
命名空间
前言:
最早的TS中,没有命名空间的概念,而是模块化的概念
分为内部模块和外部模块两种
从TS1.5开始,内部模块被重命名为命名空间,所以虽然命名空间是内部模块
那么我们如何区分它们呢? 其实我们主要是从内部包装的内容来区分。
命名空间(模块内部):主要用于组织代码,避免命名冲突
外部模块(module):注重代码封装和复用,一个模块可能包含多个命名空间
例子
●我们开发过程中,如果一个ts文件没有写模块化句型
●则默认该文件中定义的所有变量都被全局挂载。
●那么两个文件中不能有重复的变量名,否则会出现问题
●a.ts
const num = 100
const str = 'hello world'
●b.ts
const num = 200
●这两个文件看似无关,但它们都包含num变量。
●但是由于没有模块句型,所以这两个是全局挂载的变量。
●这时候我们可以把这个内容放到命名空间(内部模块)中来解决这个问题
命名空间
●语法:
namespace 命名 {
/* ... */
}
●变量可以在命名空间内定义和使用,内容也可以暴露给外界。
●暴露命名空间内的内容也使用export关键字
namespace GoodsHandler {
const current = 1
const pagenum = 10
const info = []
export const list = info.slice((current - 1) * pagenum, current * pagenum)
}
○这样我们所有的内容就都放在这个名为GoodsHandler的命名空间中了
○其中三个变量不能在其他地方使用,只能在当前命名空间内使用。
○向外界曝光了一个名为list的内容
引入命名空间
●当我们需要使用命名空间时,可以使用三斜杠(///)命令
●注意:文件开头必须使用三斜杠(///)命令
/// <reference path="./goodsHandler.ts" />
console.log(GoodsHandler.list)
模块和命名空间
●当我们使用模块和命名空间来组织我们的代码之后
●我们向外界暴露的内容越来越多,我们也会考虑文件过多的问题。
●所以有时我们可以将命名空间和模块一起使用
●即一个模块内,可以导入多个命名空间
●功能与概念的区分
○命名空间:主要解决变量命名冲突
○模块:组织代码、复用
export namespace GoodsHandler { /* ... */ }
export namespace ListHandler { /* ... */ }