文章目录
- 一、模块
- 1. 导出
- 基础导出
- 重新导出
- 导出重命名
- 2. 导入
- 基础导入
- 导入重命名
- 3. 默认导出
- 4. 模块化兼容
- exports =
- import = require('')
- 编译结果
- 二、命名空间
- 1. 例子
- 2. 命名空间
- 3. 引入命名空间
- 三、模块和命名空间
一、模块
- JavaScript 在 ES2015 中引入了模块的概念,我们的 JS 也开始进行了标准的模块化开发阶段,在 TS 内沿用了这个概念
- 在 TS 中,从语法到概念,其实和 JavaScript 中基本一致的
1. 导出
- 在一个文件中,所有的声明都可以利用 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'
- 在这个 moduleB 文件中导入了 moduleA 文件的所有内容
- 并且在 moduleB 中也进行了一次导出
- 可能很多小伙伴就要问了,这个有什么意义吗 ?
- 其实是可以做一些模块合并的
- 举个例子
- moduleA.ts
export const num = 100
- moduleB.ts
export const str = 'hello world'
- moduleC.ts
export interface Users {}
- moduleIndex.ts
export * from './moduleA'
export * from './moduleB'
export * from './moduleC'
- 这样一来,就相当于用 moduleIndex 文件对前面三个模块进行可一个整合导出
导出重命名
- 有的时候,我们在重新导出的时候,有可能会遇到一些多个模块的重名问题
- 或者说会遇到多个模块因为不是一个人书写的,导致模块命名风格不统一
- 我们在书写的时候,就可以在导出的时候做一个重命名
- 语法
export { num as current } from './moduleA'
- 相当于在这个文件中导入了 moduleA 模块中的 num 数据
- 并且进行了再次导出
- 只不过进行再次导出的时候,用的名字是 current 而已
- 举个例子
- moduleA.ts
export const n = 100
- moduleB.ts
export const s = 'hello world'
- moduleC.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
2. 导入
- 和上面导出是配套出现的
基础导入
- 直接导出某一个模块的某个或者某些内容
- 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 方法
- 但是在 index 文件内使用的时候,使用 s 这个函数名
3. 默认导出
- 每个模块都会有一个默认导出,使用 default 来进行
- 需要注意 : 一个模块只能有一个默认导出
- moduleA.ts
const utils = {
study () {},
play () {},
sleep () {}
}
export default utils
- 这是在 moduleA 内以默认导出形式导出了 utils 这个对象
- index.ts
import XhlUtils from './moduleA'
- 这里在 index 内的 XhlUtils 得到的就是 moduleA 文件内导出的 utils 对象
4. 模块化兼容
- 之前的都是 JS 的模块化语法,我们相当于复习或者回顾
- 现在这一块是 TS 对模块化的增强了,各位小伙伴要注意学习了哦
前言 :
我们刚才说的其实都是 ES6 Module 的语法规范
但是我们直到,在 JS 的发展进程中,我们并不是只有这一个模块化语法规范
我们还有 AMD / CommonJS / UMD 等模块化语法规范
各自有各自的导入导出语法规范
但是在这几个语法内有一个共同的规范,就是 exports 变量的出现
exports 变量
在 CommonJS 和 AMD 内都有 exports 这个对象
就是这两个模块化规范的默认导出,相当于 ES6 模块化规范内的 export default
虽然大家的语法是相似,并且意义也差不多
但是 ES6 模块化语法,并不能兼容 CommonJS 和 AMD 语法规范
所以在 TS 内对 ES6 的模块化语法进行了一些扩展
可以在编译的时候,更好的支持 CommonJS 的语法
exports =
- 如果你只是使用 TS 进行前端开发,不需要考虑模块化语法的兼容,那么你不需要了解这个
- 只有当你需要兼容 CommonJS 和 AMD 语法的时候
- 那么我们的 export default 就不能用了
- index.ts => 不考虑兼容性
class Person {}
export default Person
- index.ts => 考虑兼容
class Person {}
export = Person
import = require(‘’)
- 为了支持兼容,我们的导出语法变了,那么我们的导入语法也要变化
- index.ts => 不考虑兼容
import Person from './xxx'
- index.ts => 考虑兼容
import Person = require('./xxx')
编译结果
- 如果你按照兼容方式书写的导出内容,会被编译成一下几个情况(看看得了)
- SimpleModule
import Person = require('./xxx')
- AMD( requireJS )
define(["require", "exports", "./xxx"], function (require, exports, mod_1) {
console.log(mod_1)
});
- CommonJS( node )
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 SimpleModule
System.register(["./xxx"], function(exports) {
let Person
return {
setters: [
function (param) {
Person = param
}],
execute: function() {
console.log(Person)
}
}
})
- Native ES6 Module
import Person from './xxx'
二、命名空间
前言 :
最早的 TS 里面,是没有命名空间的概念的,就是模块化的概念
分成了两种,内部模块 和 外部模块
从 TS1.5 开始,内部模块 改名为 命名空间,所以其实 命名空间 就是 内部模块
那么我们如何他们呢,其实主要还是从内部封装的内容上来区分
命名空间(内部模块) : 主要用于组织代码,避免命名冲突
外部模块(模块) : 侧重代码的封装复用,一个模块内可能包含多个命名空间
1. 例子
- 我们在开发的过程中,如果一个 ts 文件没有书写模块化语法
- 那么会默认这个文件内所有定义的变量都是挂载在全局上的
- 那么两个文件内就不能出现重复变量名,不然会出现问题
- a.ts
const num = 100
const str = 'hello world'
- b.ts
const num = 200
- 看似没有关系的两个文件,只是都写了 num 变量
- 但是因为没有模块化语法,这两个就都是挂载在全局的变量
- 这个时候,我们就可以把这些内容放在命名空间( 内部模块 )内来解决这个问题
2. 命名空间
- 语法 :
namespace 命名 {
/* ... */
}
- 在命名空间内可以定义变量使用,也可以向外暴露内容
- 在命名空间内向外暴露内容也是使用 export 关键字
namespace GoodsHandler {
const current = 1
const pagenum = 10
const info = []
export const list = info.slice((current - 1) * pagenum, current * pagenum)
}
- 这样一来,我们所有的内容就放在了这个叫做 GoodsHandler 的命名空间内
- 其中三个变量别的地方用不了,只能在当前命名空间内使用
- 向外暴露了一个叫做 list 的内容
3. 引入命名空间
- 当我们需要使用一个命名空间的时候,我们可以使用三斜线(///) 指令
- 注意 : 三斜线(///) 指令必须要要用在文件的最开始
// <reference path="./goodsHandler.ts" />
console.log(GoodsHandler.list)
三、模块和命名空间
- 当我们使用 模块 和 命名空间 组织我们的代码以后
- 我们的向外暴露的内容越来越多,也会考虑到文件太多的问题
- 所以有的时候我们可以把命名空间和模块放在一起使用
- 也就是在一个模块内,可以导出多个 命名空间
- 分清作用和概念
- 命名空间: 主要为了解决变量命名冲突
- 模块: 组织代码,复用
export namespace GoodsHandler { /* ... */ }
export namespace ListHandler { /* ... */ }