这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战
为何需要 ES Module
历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。
无模块概念
一开始,将每个功能及其相关数据,各自单独放到不同的 JS 文件,作为不同的可执行脚本,然后通过 <script>
元素引用
<script src="./a.js"></script>
<script src="./b.js"></script>
复制代码
但一旦脚本增加,具有越来越多变量、函数等,不但容易产生命名冲突,而且作用于全局作用域,无法确定依赖关系,维护难度增加
模块化规范
Javascript 程序本来很小——在早期,它们大多被用来执行独立的脚本任务,在你的 web 页面需要的地方提供一定交互,所以一般不需要多大的脚本。过了几年,我们现在有了运行大量 Javascript 脚本的复杂程序
模块化可理解为:将 JavaScript 程序拆分为可按需导入的单独模块的机制
为了更好地组织脚本代码,偏向以一个 JS 文件作为入口文件,根据需要来针对性引用功能模块,发展了一些模块化标准规范:
规范 | 特点 | 简要说明 |
---|---|---|
CommonJS 规范 | 同步加载 | - 避免引起大量的同步请求,不适用于浏览器 - 通过 module.exports 导出成员,通过 require 函数载入模块 |
AMD 规范 | 异步加载 | - 适用于浏览器 - 通过 define 函数定义模块,通过 require 函数自动加载模块 (加载模块时,自动创建 script 标签去请求并执行模块代码) - 使用起来相对复杂 - 若模块划分越来越多,可能引起页面具有大量的脚本资源请求 |
UMD规范 | 规范集合 | - CommonJS 规范 + AMD 规范 - 浏览器端、服务器端都适用 |
(PS:# JS模块化规范,浅谈一下模块化规范的差异)
ES6 模块功能
ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案
ES6 模块功能的主要特性:export
和import
,一个输出另一个输入,利于确定模块依赖关系
ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量
-
静态加载,好处:可进行静态分析(不执行代码,从字面量上对代码进行分析),即利于 # Tree-shaking,在编译时消除无用代码
-
异步加载:不会造成堵塞浏览器,页面渲染完毕,再执行脚本。具体加载原理,详见 # 传送门
export先输出
ES6 模块不是对象,而是通过
export
命令显式指定输出的代码,再通过import
命令输入。
export
命令用于规定模块的对外接口,主要用法如下:
命名输出
此方式可输出多个变量、函数或类(class),即一个模块可以有任意个命名输出
export let num = 1; // √
export let name = 'alison'; // √
export 2; // X,直接输出,未提供对外的接口
复制代码
或指定输出的一组变量:
let num = 1;
let name = 'alison';
export {num, name}; // √,使用{}指定输出的变量
export num; // X,还是直接输出,未提供对外的接口
复制代码
支持重命名
支持使用 as
关键字重命名
let num = 1;
let name = 'alison';
export {num as number, name}; // √,num 重命名为 number
复制代码
默认输出
一个模块中只能有一个默认导出export default, 即只能使用一次
let num = 1;
let name = 'alison';
export default {num, name}; // √,默认输出
复制代码
import再输入
import
命令用于输入指定模块提供的功能
-
输入的变量、函数或类 只读(对象较特殊),均不建议改写
-
from
后指定模块文件的位置,可相对路径,可绝对路径,可模块名(需具有配置文件) -
具有提升效果,类似变量提升效果,import的执行先于对应的调用
-
不能使用表达式和变量
主要用法
对应 export
命令输出方式:
输出 | 输入 | 代码 | 备注 |
---|---|---|---|
命名输出(任意个) | 需{} | import {num, name} from './demo.js' | - 输入模块命名不可随意 - 支持重命名输入 import {num as number, name} from './demo.js' |
默认输出(只有一个) | 无需{} | import demo from './demo.js' | - 输入模块命名可随意 import MyDemo from './demo.js'也可 |
整体输入
用*
与as
指定一个对象,将所有输出都加载到该对象
import * as demo from './demo.js';
console.log(demo.num); // √
console.log(demo.name); // √
复制代码
链接传送门
Last but not least
如有不妥,请多指教~