Es Module规范
1.Es Module出现
- 之前我们说过JavaScript 没有他自己的模块化一直是他的痛点,所以才会产生一些社会规范
- 比如用在node的CommonJS规范 用在浏览器的规范 AMD和CMD等
- ESM模块采用export 和 import 关键字来实现模块化
- export 负责将模块内的内容导出
- import 负责从其他模块导入内容
- ESM自动采用严格模式:
use strict
2.在浏览器使用Es Module
- 在浏览器中使用ESM模块 type设置为
module

-
此时你会发现你浏览器报错:
-
这个错误MDN上面有解释: MDN
-
你需要注意本地测试,如果你通过本地加载HTML文件比如(file://路径的文件)你将遇到
cors错误因为JavaScript模块安全性需要
-
你需要通过一个服务器来测试
-
我使用的 Vscode, Vscode中有个插件 Live Server
-

3.在node使用ESM模块化
生成配置文件
-
使用
npm init --y这个命令不能包含中文名 生成package.json文件{ "name": "useES6", "version": "1.0.0", "description": "", "main": "index.js", "type":"module", // module 是es6模块 commonjs 代表CommonJS规范 "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }
基本使用
-
导出有按需导出
export和默认导出export default- 在
bar.js
const name = 'zs'; const age = 18 export { name }; export default { name, age }; - 在
-
导入有
import- 在
index.js
import info, { name } from './modules/bar.js'; console.log(info);// {name:'zs',age:18} console.log(name); // zs - 在
其它用法
-
import导入方式有很多种:
-
方式一: import {导出的标识} from '模块'
import { name } from './modules/bar.js'; -
方式二:import 命名变量 from '模块'
import info from './modules/bar.js'; -
方式三:import * from '模块'
import * from './modules/bar.js'; -
方式四:导入时给标识符起别名
asimport info , { name as name1 } from './modules/bar.js'; import * as info3 from './modules/bar.js'; // [Module: null prototype]{ default:{name:'zs',age:18 }, name:'zs'} console.log(info3)注意:使用
*导入的变量 会个export default 默认导出的对象起一个名字是default
-
-
export 和 import结合使用
-
在
math.js文件中const name = 'zs'; const age = 18 export { name, age } -
在
bar.js文件中export {name,age} from './math.js' // 导出方式一: export和import结合使用 按需导出 or export * from './math.js' // // 导出方式二: export和import结合使用 默认导出全部 -
在
index.js文件中import {name,age} from './bar.js' // 按需引入 or import * as bar from './bar.js' // 全部引入
-
-
为什么要这么做
- 在开发和封装功能库时,通常我们希望将暴露的所有接口放到一个文件中
- 这样方便统一管理,这个时候我可以使用import和export 结合
-
注意: 默认导出
export default一个文件只能有一个, 可以和按需导出export搭配使用
import函数
-
引入的
import必须写在头部 -
如果要引入json文件需要特殊处理 需要增加断言并且指定类型json node低版本不支持
import data from './data.json' assert { type: "json" }; console.log(data); -
动态导入模块
-
import静态加载不支持掺杂在逻辑中如果想动态加载请使用import函数模式
-
动态加载 返回的Promise函数
if(true){ import('./test.js').then() }
-
-
ES11新增的特性
import meta-
它包含了这个模块的信息,比如这个模块的URL
// 假如你的项目当前路径 file://home/user/my-module.mjs console.log(import.meta) // { url: "file:///home/user/my-module.mjs" } -
在
vite环境中 你可以在.env文件设置环境变量,然后使用代码import meta.env读取// .env 文件 VITE_APP_TITLE = Hello Vite ! // main.js 文件 console.log(import.meta.env.VITE_APP_TITLE); // 输出:Hello Vite!
4.ESM的原理解析
有以下代码
// 在 main.js中 import aaa from 'aaa.js' // 在 aaa.js中 import bbb from 'bbb.js' // 在 bbb.js中 export const name = 'zs'- 构建阶段
- 在这个阶段浏览器首先会查找js模块文件,并去下载,将其解析成模块记录
Module Record
- 在这个阶段浏览器首先会查找js模块文件,并去下载,将其解析成模块记录
- 实例化阶段
- 在这个阶段进行实例化,解析导出语句并分配内存空间即(模块环境记录)此时模块环境记录里面值是没有的
- 解析导入语句也生成一个模块环境记录
- 此时模块指向对应的内存地址
- 求值阶段(运行阶段)
- 运行代码,计算值,并且将值填入到内存地址中
- 运行代码,计算值,并且将值填入到内存地址中
-
Module Record 模块记录
Module Environment Record: 模块环境记录
exporting modulecan update variable value... : 导出模块可以更新变量值...
but importing modulecannot update variable value: 导入模块
无法更新变量值(不能在导入的时候去修改值)
5.ESM和CommonJS的区别
- 相同点:
- 同一个文件引用多次,会缓存,只会加载一次
- 不同点
- ES6模块是基于编译的异步加载,CommonJS是基于运行时同步加载
- ES6顶层this是undefind(因为默认严格模式), CommonJS中的this是模块本身
- ES6模块支持three shaking CommonJS是不支持的
- ES6导出的值是不可以修改的(可读的,类似于
const),CommonJS是支持修改的 - 查找规则commjs会直接根据查找规则进行查找,ESM则必须显示引入
- 比如有以下文件
index.js
import index from 'index'// 这样写报错,因为原生ESM不支持 没有在vite或webpack环境下 // 必须显示写出 import index from 'index.js' - 比如有以下文件