模块化规范之ES Modules && CommonJS 规范

1,811 阅读4分钟

学徒之心,有错误欢迎指出哈~

一、模块化发展的过程

下面是一篇关于前端模块化发展的文章

模块化在不同环境的使用: 浏览器环境: ES Modules 模块化规范 Node环境: CommonJS 模块化规范

二、ES Modules 特性

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <!-- script标签执行时立即执行加载脚本, defer 属性会在页面渲染完之后执行脚本 -->
  <!-- 给script标签 设置type = module 来告知当前script标签中的代码采用ESM的规范来执行 -->
  <!-- <script type="module"></script> -->
  <!-- ESM的基本特性 -->
  <!-- 1. 自动采用严格模式 -->
  <script type="module">
    console.log(this) // undefined
  </script>

  <!-- 2. 每个ESM模块都是单独的私有作用域 -->
  <script type="module">
    var a = 111
    console.log(a)
  </script>

  <script type="module">
    console.log(a) // ref Error
  </script>

  <!-- 3. ESM 的 script 标签会延迟执行脚本,默认加上了defer属性 -->

  <!-- 4. ESM 是通过 CORS这种跨域请求的方式 去请求外部 JS 模块的 -->
  <!-- 下面地址去请求js库会报跨域,是因为这个地址不支持 CORS, 如果要请求外部地址服务端必须支持 CORS -->
  <script type="module" src="https://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
  <!-- 下面这个地址就没有问题,因为服务端支持 CORS -->
  <script type="module" src="https://unpkg.com/jquery@3.4.1/dist/jquery.min.js"></script>
</body>

</html>

总结

  • 自动采用严格模式
  • 每个ESM模块都是单独的私有作用域
  • ESM 的 script 标签会延迟执行脚本,默认加上了defer属性
  • ESM 是通过 CORS这种跨域请求的方式 去请求外部 JS 模块的

三、ES Modules 导入导出

1. export import 后面的 {}

export import 后面的 {} 并不是字面量对象而是固定语法,与ES6的对象的简写和解构没有任何关系。

不是简写 在这里插入图片描述 在这里插入图片描述 不是解构 在这里插入图片描述 在这里插入图片描述

在这里插入图片描述

2. export {} 与 export default 的区别

  • 通过export { m1, m2,m3 .... }可以为我们导出多个成员
  • export default 只可以为我们导出一个默认成员,一个模块仅允许导出一个默认成员 在这里插入图片描述 在这里插入图片描述
  • export {} 和 export default 可以在一起使用 在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3. export 导出是栈内存中的变量(原始数据类型存储的是值,对象数据类型存储的是堆内存的引用地址)

在这里插入图片描述

在这里插入图片描述 在这里插入图片描述

首先我们在module.js中导出了变量name,feature ,并且在index.js中打印了他们。1秒后在module模块中修改了原始数据类型name所指向的内存空间为新的空间david(原始数据类型值为不可变的)和feature 在堆内存中hair的值为black,2秒后在index模块中打印了他们,发现index模块中的值都发生了变化。如果是值的拷贝,此时按理说是不会发生任何变化的。

4. import 导入的成员是一个只读的成员,不能够修改值

在这里插入图片描述

在这里插入图片描述 在这里插入图片描述 从报错信息我们得知,export 导出的成员是以常量进行声明的。所以const声明的原始数据类型不能够进行修改,引用数据类型可以修改(因为只要是栈内存存储的引用地址不发生任何的改变都不会报错)

5. import 导入路径相关

(1)必须跟上完整的导入文件路径,不能够省略文件类型。

CommonJS require() 模块的时候是可以省略文件的扩展名的 在这里插入图片描述

(2). import 也可以导入第三方模块需添加完整的路径,例如 https://localhost:3000/........

在这里插入图片描述 除了完整的路径,也可以写绝对路径 绝对路径会默认从网站的根目录下面去找,我这里起的服务是 在这里插入图片描述

(3)import 在浏览器环境下 不能够导入 CommonJS 提供的模块

在这里插入图片描述

在这里插入图片描述

6. 如果不需要导入成员,只想执行所导入的模块可以省略{}, 例如可以直接写:import './module.js'

7. import * as 别名 from './module.js', 可以导出 module 模块 下的所有成员

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

8. 如何动态的加载模块,通过 调用全局的 import(文件路径) ,该import方法返回一个promise对象。通过then方法可以取到导出的值。

在这里插入图片描述 在这里插入图片描述

此时页面会报错误,请看下面代码如何动态导出成员

const age = 19

if (age > 18) {
  // 如果age 》 18 我们需要导入modules.js中的所有成员

  import('./modules.js').then(res => {
    console.log(res, '动态导入成员')
  })
} else {
  // 在这里我们就可以导入另外一个模块
  // import ('./modulesB.js')
}

在这里插入图片描述

9. 同时导入默认成员与命名成员写法可以有两种

import {name, age,  default as hhh) from './module.js'
import hhh, {name,age} from './modules.js'

10. 可以导入导出一起使用。

export {name, age} from './modules.js'

四、ES Modules 浏览器兼容性问题

如ie浏览器目前还是没有支持这个语法 可以通过引用 browser-es-module-loader Pollyfill 来让 ie支 持es module语法

五、ESModules 在 Node 环境中的支持

目前 node.js 从 8.5版本过后已经作为实验特性支持 ES Modules的使用

1. 通过修改文件后缀名为 .mjs 的 形式,运行node 命令的时候指定命令行参数为如图

在这里插入图片描述

2. 在package.json 文件中指定 type = module

在这里插入图片描述 如果在package.json 指定了type=module 也就说当前项目模块化规范是按照ESM ,此时若想要使用 CommonJS 规范需要使用.cjs后缀才能够使用require 在这里插入图片描述

六、ES Modules 在 Node 环境中与 CommonJS 的交互

  • ES Modueles 中可以导入 CommonJS 模块导出的成员 在这里插入图片描述

  • CommonJS 中不能导入 ES Modules 所定义的导出成员 这里发生了语法错误异常的问题 在这里插入图片描述

  • CommonJS 始终只会导出一个默认成员也就是exports对象,多个成员是往这个对象里面添加key ,value

// 第一种
module.exports = 导出成员

// 第二种 expors 是 module.exports 的别名
// 采用此方法需要往 exports 对象中添加key value的形式来添加导出成员,注意不能够给exports重新赋值一个新对象
exports.key1 = value1
exports.key2 = value2

七、ES Modules 在 Node 环境中与 CommonJS 的差异

在这里插入图片描述

八、通过babel也可以在node环境中使用ESM

yarn add @babel/node @babel/core @babel/preset-env -D

配置babelrc文件,使用代码将按照哪个转换库进行转换。 babel 本身不负责任何代码的转换,他仅提供一个平台,真正进行代码转换的是@babel/preset-env, preset-env 是一个转换库的集合,里面包含了ES6+特性的语法,esmodules转换等等。 在这里插入图片描述

在这里插入图片描述