持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第17天,点击查看活动详情
前言
由于最初的脚本又小又简单,导致JavaScript在很长一段时间内是没有语言级(language-level)的模块语法的;不过由于应用越来越庞大、使得脚本越来越复杂,大佬们就想办法将其拆分成多个文件,即所谓的“模块(module)”,AMD、CommonJS以及UMD都是一些实现的策略,其中影响最深的,就是Node.js使用的CommonJS规范。
而对JavaScript来说,其语言级的模块系统在 2015 年的时候出现在了标准(ES6)中,此后便成为了浏览器和服务器的通用的模块解决方案,在现今的框架(Vue、React等)和打包工具(如Webpack)中广泛被使用。
什么是模块?
用一句话来说,一个模块(module)就是一个文件,它可以包含用于特定目的的变量、类或者函数库。
模块可以相互加载,并可以使用特殊的导入(import )和导出( export )指令来交换功能,比如在一个模块(main.js)中调用另一个模块(sum.js)的函数:
// sum.js
export const sum = (a, b)=> a + b
// main.js
import { sum } from './sum.js'
console.log(sum(1,2)) // 3
以上只是import和export的一个简单用法,关于具体如何使用,通过下面这张表可以有个初步的了解:
| export语法 | import语法 |
|---|---|
| 命名的导出 export | 带花括号 import { xxx } from |
| 默认的导出 export default | 不带花括号 import xxx from |
| export 和 export default | import * as xxx from |
下面我们具体展开唠唠:
export 和 import
export 关键字标记了可以从当前模块外部访问的内容,包括变量、类和函数。
我们可以在声明之前使用export,也可以在声明后使用,两者是一样的效果
// module.js
// 声明前使用
export let month = 'Oct'; // 导出变量
export class User { // 导出类
constructor(name) {
this.name = name;
}
}
export const sum = (a, b)=> a + b // 导出函数
// 声明后使用
let month = 'Oct';
class User {
constructor(name) {
this.name = name;
}
}
function sayHi(user) {
alert(`Hello, ${user}!`);
}
export { month, User, sayHi }
此时,我们如果需要在另一个模块中使用sayHi()函数,可以使用import {}、import 'as'或者import *导入:
// main.js
import { sayHi } from './module.js';
sayHi('Yiler'); // Hello, Yiler!
// main.js
import { sayHi as hi } from './module.js';
hi('Yiler'); // Hello, Yiler!
// main.js
import * as say from './module.js';
say.sayHi('Yiler'); // Hello, Yiler!
export default 和 import
在实际开发中,我们可能不希望在同一个模块中包含过于复杂的库或函数,更倾向于每个模块中只有它自己的“东西”,比如模块 user.js 仅导出 class User。这就是export default 最初设计出来的目的,它可以使“一个模块只做一件事”。
因此,每个模块应该只有一个 export default,同时比较特殊的是,其导入不需要使用花括号,甚至导出时也不需要名称,也即:
// user.js
export class User { // 默认导出类
constructor(name) {
this.name = name;
}
}
// user.js
export class { // 默认导出类,不需要名称
constructor(name) {
this.name = name;
}
}
// main.js
import User from './user.js'; // 不需要花括号
new User('John');