JavaScript 模块化
基本知识
为了让 JavaScript 更模块化、更整洁以及更易于维护,ES6 引入了在多个 JavaScript 文件之间共享代码的机制。 它可以导出文件的一部分供其它文件使用,然后在需要它的地方按需导入
在HTML
中导入模块,需要定义属性 type="module"
,模块总是会在所有HTML
解析后才执行
html
<script type="module" src="filename.js"></script>
<!-- 使用了 module 类型的脚本可以使用 import 和 export 特性 -->
- 模块都有独立的顶级作用域,与普通
script
标签不同,不同模块不能互相访问
html
<script type="module">
let test = "Hello World"
</script>
<script type="module">
alert(test) // Error
</script>
- 模块在导入时只执行一次解析,之后的导入不会再执行模块代码,而使用第一次解析结果,并共享数据
html
<script type="module" src="test.js"></script>
<script type="module" src="test.js"></script>
导入导出
ES6 使用基于文件的模块,即一个文件一个模块
- 使用
export
将开发的接口导出 - 使用
import
导入模块接口 - 使用
*
可以导入全部模块接口 - 导出是以引用方式导出,无论是标量还是对象,即模块内部变量发生变化将影响已经导入的变量
下面是对math.js
模块进行导出
js
// 整体导出
const add = (x, y) => x + y
const subtract = (x, y) => x - y
export { add, subtract }
// 单个导出
export const add = (x, y) => x + y
export const subtract = (x, y) => x - y
导入分为两种:具名导入、批量导入
- 具名导入(建议使用)
js
import { add, subtract } from './math.js'
// 用这种方式导入时,相对路径(./)和文件扩展名(.js)都是必需的
- 批量导入(
*
)
js
// import 语句会创建一个 addModule 的对象(命名自定义)
import * as addModule from "./math.js"
// addModule 对象包含 math.js 文件里的所有导出,可以直接访问其中的函数
myMathModule.add(2,3)
myMathModule.subtract(5,3)
别名
可以为导入和导出的模块起别名,使用as
关键字
- 有些导出的模块命名过长,起别名更简洁
- 本模块与导入模块重名时,可以通过起别名防止冲突
js
// 导入
import { add as addFunc, subtract as subtractFunc } from './math.js'
// 导出
export { add as addFunc, subtract as subtractFunc }
默认导出
在文件中只有一个类或需要导出的时候,通常会使用默认导出语法,它也常常用于给文件或者模块创建返回值
使用default
定义默认导出的接口,导入时不需要使用 {}
- 默认导出可以不命名,也可以为默认导出自定义别名
- 只能有一个默认导出
- 如果将导出命名为
default
也算默认导出
js
// 导出
export default function add(x, y) {
return x + y
}
// 导入
import add from "./math.js"
// add只是变量名,对应 math.js 文件的任何默认导出值。在导入默认导出时,可以任意命名
如有多个函数或变量
js
const baseURL = 'https://www.baidu.com/'
const getArraySum = arr => arr.reduce((sum, item) => sum += item, 0)
export default {
baseURL,
getArraySum
}
混合导出
模块可以同时存在默认导出与命名导出
- 使用
export default
导出默认接口 - 使用
export {}
导出普通接口
js
// 导出(math.js)
const baseURL = 'https://www.baidu.com/'
const getArraySum = arr => arr.reduce((sum, item) => sum += item, 0)
function add(x, y) {
return x + y
}
export { baseURL, getArraySum, add as default } // 将导出命名为default也算默认导出
// 导入
import { baseURL, getArraySum } from './math.js' // 对应export
import addFunc from './math.js' // 对应export default
- 可以使用一条语句导入默认接口与常规接口
js
import { baseURL, getArraySum }, addFunc from './math.js'
- 也可以使用别名导入默认导出
js
import { baseURL, getArraySum, default as addFunc } from './math.js'
- 如果是批量导入,可以使用
default
获得默认导出
js
import * as api from './math.js'
api.default.add()
导出合并
可以将导入的模块重新导出使用,将所有模块合并到一个入口文件中
math.js
js
export const add = (x, y) => x + y
export const subtract = (x, y) => x - y
base.js
js
const baseURL = 'https://www.baidu.com/'
const getArraySum = arr => arr.reduce((sum, item) => sum += item, 0)
export default {
baseURL,
getArraySum
}
统一导出
js
export * as math from "./math.js";
// 默认模块需要单独导出
export { default as base } from "./base.js";
动态加载
使用 import
必须在顶层静态导入模块,而使用import()
函数可以动态导入模块,它返回一个 promise
对象
不在顶层静态导入时是错误的,但使用动态加载却可以
js
if (true) {
import { add, subtract } from "./math.js"; // Error
const res = import(./math.js)
}