视频加载失败

ES6模块Module

1615 字
8 分钟
ES6模块Module

Module 是什么#

模块就是把代码拆分成多个文件,每个文件负责一部分功能,然后通过导入和导出实现复用。

一个一个的局部作用域的代码块

什么是模块系统

模块系统需要解决的主要问题:

模块化的问题

消除全局变量

管理加载顺序

Module 的基本用法#

导出:分别导出

export const name = '张三';
export function sayHello() {
console.log('hello');
}

导入:

import { name, sayHello } from './user.js';
console.log(name);
sayHello();

默认导出

math.js
export default function add(a, b) {
return a + b;
}

导入时可以随便起名字

import sum from './math.js';
console.log(sum(1, 2));

区别:

类型导出导入
named export{ add }{ add }
default exportexport default随便命名

混合导出

export const a = 1;
export default function test() {
console.log('default');
}

导入:

import test, { a } from './file.js';

导出方式总结

//直接导出
export const a = 1;
//先写再导出
const b = 2;
export { b };
//重命名导出
const c = 3;
export { c as count };
import { count } from './file.js';

全部导入

import * as utils from './utils.js';
console.log(utils.a);
utils.fn();

export default 和对应的 import#

一个模块可以有多个普通导出(named export),但只能有一个默认导出(default export)。

默认导出:

math.js
export default function add(a, b) {
return a + b;
}
//导入
// app.js
import add from './math.js';
console.log(add(1, 2));//3

为什么导入时没有 {}? 因为导出的是这个模块的默认值,导入时直接拿,名字可以随便起

//import 任意名字 from '模块';
import add from './math.js';
import fn from './math.js';

默认导出对象:

export default {
add,
sub,
mul
};
import math from './math.js';
math.add();

默认导出类:

export default class User {
login() {}
}
import User from './User.js';
const u = new User();

默认导出匿名函数:

export default function () {
console.log('hello');
}
import fn from './a.js';
fn();

默认导出匿名类

export default class {
login() {}
}
import User from './User.js';
const u = new User();

export 和对应的 import#

一个模块可以有多个普通导出,普通 export 是“按名字导出”,导入时必须按名字取。

普通导出:

//导出
export function add() {}
export function sub() {}
//导入 名字必须对应。
import { add, sub } from './math.js';
import { fn } from './math.js';//错误
function add(a, b) {
return a + b;
}
function sub(a, b) {
return a - b;
}
function mul(a, b) {
return a * b;
}
export {
add,
sub,
mul
};

为什么要加 {}?因为导出了多个名字,导入时必须明确

导出后再统一 export

const a = 1;
const b = 2;
export { a, b };
//等价于
export const a = 1;
export const b = 2;

导出时起别名

const a = 1;
export {
a as count
};
//导入肯定就需要别名
import { count } from './a.js';
console.log(count);
  • 导入时起别名

    import { count as c } from './a.js';
    console.log(c);
  • 一次导入导出多个

    export const a = 1;
    export const b = 2;
    export const c = 3;
    //导入
    import { a, b, c } from './a.js';
  • 全部导入:

    import * as utils from './a.js';
  • 导入导出复合写法

    //复合写法无法在当前模板中使用
    export { age } from './module.js';//类似中转站

Module 的注意事项#

import 必须写在顶层

//错误
if (true) {
import { a } from './a.js';
}
function fn() {
import { a } from './a.js';
}
//正确
import { a } from './a.js';
//如果需要条件加载:
if (true) {
const module = await import('./a.js');
}

import 导入的是只读绑定:

a.js
export let count = 1;
//b.js
import { count } from './a.js';
count = 2; // ❌import 得到的是只读引用。不能修改

导出的变量是动态绑定:

a.js
export let count = 1;
setTimeout(() => {
count = 2;
}, 1000);
//b.js
import { count } from './a.js';
setTimeout(() => {
console.log(count);//2
}, 2000);

一个模块只能有一个 default

默认导出导入时名字随便起

普通导出必须按名字导入

导入的名字不能重复声明

import { a } from './a.js';
let a = 2;//error Identifier 'a' has already been declared
//解决,起一个别名
import { a as importedA } from './a.js';
let a = 2;

模块默认是严格模式

模块有自己的作用域,模块之间,变量默认不共享

a.js
let a = 1;
//b.js
console.log(a);//a is not defined

模块只执行一次

import './a.js';
import './a.js';
console.log('执行');

浏览器必须加 type="module"

<script type="module" src="./app.js"></script>

Node ESM 必须开启,或者,文件拓展名字.mjs,注意node12之前存在兼容问题

{
"type": "module"
}

Node ESM 必须写扩展名

import { add } from './math';//错误
import { add } from './math.js';

import 有提升:import 的“提升”指的是模块初始化阶段提前建立绑定,不是代码文本位置上的变量提升。

console.log(a);
import { a } from './a.js';
//看起来:先使用后导入;实际上:import 在模块初始化阶段就完成了。

循环引用要小心:如a导入b文件,b文件导入a文件

a.js
import { b } from './b.js';
export const a = 1;
//b.js
import { a } from './a.js';
export const b = 2;
//Cannot access before initialization

Module的应用#

最常见的应用场景

场景使用方式
工具函数export
常量export
APIexport
配置export default
类export default
组件export default
数据库export
路由export

API 请求模块:

api/request.js
export function request(url) {
return fetch(url).then(res => res.json());
}
//api/user.js
import { request } from './request.js';
export function getUser() {
return request('/user');
}

配置文件模块:

config.js
export default {
baseURL: 'http://localhost:3000',
timeout: 5000
};
//app.js
import config from './config.js';
console.log(config.baseURL);

拓展-ES Module 和 CommonJS 的区别#

  • ES Module(ESM)是 JavaScript 官方标准模块规范;CommonJS(CJS)是 Node.js 最早使用的模块规范。

语法区别#

CommonJS:

module.exports = {
add,
sub
};
const math = require('./math');
const { add } = require('./math');

ES Module:

export function add() {}
export function sub() {}
import { add, sub } from './math.js';

加载时机不同#

CommonJS:运行时加载

const math = require('./math');//代码运行到这里才去加载模块
//合法
if (true) {
const a = require('./a');
}

ES Module:编译时加载(静态加载)

import { add } from './math.js';//代码执行之前就已经确定依赖关系
//报错
if (true) {
import { a } from './a.js';
}
//动态导入
const module = await import('./a.js');

值拷贝 VS 引用绑定#

CommonJS:值拷贝

a.js
let count = 1;
module.exports = {
count
};
count = 2;
//b.js
const obj = require('./a');
console.log(obj.count);//1 导出时已经复制了一份值。

ES Module:动态绑定

a.js
export let count = 1;
setTimeout(() => {
count = 2;
}, 1000);
//b.js
import { count } from './a.js';
setTimeout(() => {
console.log(count);//2 import 得到的是引用绑定,不是值拷贝。
}, 2000);

this 指向不同#

CommonJS

console.log(this);//module.exports

ES Module

console.log(this);//undefined ES Module 默认严格模式。

总结:

区别CommonJSES Module
提出者Node.jsECMAScript 官方
导出module.exportsexport
导入requireimport
加载时机运行时编译时
值传递值拷贝动态绑定
thismodule.exportsundefined
顶层 await❌✅
Tree Shaking❌✅
浏览器支持❌✅
静态分析❌✅
文件名.js .cjs.mjs

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

ES6模块Module
https://f.youzi177.cn/posts/2026102201es6-mo-kuai-module/
作者
小封鸭
发布于
2026-10-21
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
小封鸭
小楼一夜听春雨,深巷明朝卖杏花
公告
欢迎来到我的博客。这里主要记录前端全栈和日常内容
分类
标签
最新动态
站点统计
文章
60
分类
3
标签
12
总字数
107,734
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
EdgeOne Pages
博客版本
Firefly v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录