微信图片_20211226160317.jpg
javascript用共享一切的方法加载代码,这是这个语言中最容易出错且容易令人感到困惑的地方。在ES6之前,应用程序的每一个Javascript中定义的一切都共享一个全局的作用域,容易引发命名冲突和安全问题。

为了解决作用域问题,使JS应用程序变得有序,便引进了模块。

在ES6模块规范出现前,浏览器原生并不支持模块的行为,ECMAScript同样不支持模块,因此希望使用模块模式的库或代码库必须基于JavaScript的语法和词法特性’伪造’出类似模块的行为

模块思想

把逻辑分块,各自封装,相互独立,每个块自行决定对外暴露的内容,同时自行决定引入执行哪些外部代码

模块标识符

模块系统本质上是键值实体,其中每个模块都有个可用于引用它的标识符。

原生浏览器模块标识符必须提供实际JS文件的路径。除了文件路径,Node.js还会搜索node_modules目录,用标识符去匹配包含index.js的目录

模块依赖

模块系统的核心是管理依赖。指定依赖的模块与周围的环境会达成一种契约。本地模块向模块系统声明一组外部模块,这些外部模块对于当前模块正常运行是必需的。模块系统检视这些依赖,确保这些外部模块能够被加载并在本地模块运行时初始化所有依赖。

模块加载

加载模块的概念派生自依赖契约,当一个外部模块被指定为依赖时,本地模块期望在执行它时,依赖已准备好并已初始化。

浏览器会递归地评估并加载所有依赖,知道所有依赖模块都加载完成。

只有整个依赖图都加载完成,才可以执行入口文件

模块加载是阻塞的,意味着前置操作必须完成才能执行后续操作。每个模块在自己的代码到达浏览器之后完成加载,此时其依赖已经加载并初始化。

动态依赖

有的模块系统要求开发者在模块开始列出所有依赖,有些允许开发者在程序结构中动态添加依赖。

动态添加的依赖必须在模块执行前加载完毕。虽然支持更复杂的依赖关系,但增加了对模块进行静态分析的难度

静态分析

模块中包含的要发送到浏览器的JavaScript代码经常会被静态分析,分析工具会检查代码结构并在不实际执行代码 的情况下推断其行为。

循环依赖

所有的模块系统都支持循环依赖,在包含循环依赖的程序中,模块的加载顺序可能会出人意料,都是执行深度优先的依赖加载

ES6模块

ES6最大的一个改进是引入了模块规范

模块是自动运行在严格模式下并且没有办法退出运行的JS代码。

在模块顶部创建的变量不会自动添加到全局共享作用域,仅在该模块的顶级作用域中存在。

在模块顶部this值为undefined

模块不支持HTML风格的代码注释

ES6模块是异步加载执行的

脚本,也就是任何不是模块的JS代码,缺少这些特性

导出和导入

使用export关键字暴露相关代码给其他模块

export let name='yqx'
function sum(num1,num2){
    return num1 + num2
}
export { sum as add } // 可重命名
export default sum; // 可导出默认值

// 导入
import sum,{ name , add } from "导出的文件相对路径" // 默认值必须排在非默认值前面 

可以为每个模块设置一个默认的导出值

import语句为变量、函数和类创建的是只读绑定,因此不能给导入的绑定重新赋值

export和import必须在其他语句和函数之外使用,因此不能动态的导入导出

导入导出都可重命名

export {sum} from '相对路径' // 重新导出一个绑定

模块可以不导出任何东西,通过无绑定导入执行模块中的代码。一般用于创建Polyfill和Shim

加载模块

ES6只是定义了模块的语法,但它并没有定义如何加载模块,这正是规范复杂性的一个体现,应由不同的实现环境来决定

在Web浏览器中使用模块

Web浏览器有多种方式将JS包含在Web应用程序中

  • 在script元素中通过src属性指定加载代码的地址
  • 将JS代码内嵌到没有src属性的srcript元素中
  • 通过Web Worker 或Service Worker的方法加载并执行JS文件

在script元素中使用模块

当type属性的值为module时支持加载模块,当无法识别type的值时,浏览器会忽略script元素

模块加载顺序

当type=”module”时自动应用defer属性。一旦HTML解析器遇到模块类型便开始下载模块文件,知道文档被完全解析模块才会执行。并且模块按照他们在文档中出现的顺序执行

async属性也可以被应用在模块上,需要确保当模块执行前,其所需的所有资源都下载完成,但不能保证的时模块的执行时机。

将模块作为worker加载

通过使用Web Workers,Web应用程序可以在独立于主线程的后台线程中,运行一个脚本操作。这样做的好处是可以在独立线程中执行费时的处理任务,从而允许主线程(通常是UI线程)不会因此被阻塞/放慢。

let worker1=new Worker("script.js")
let worker2=new Worker("module.js",{type:"module"})

与CommonJS的区别

CommonJS es模块
指定依赖 require import
导出 module.exports export
动态加载 可以 不可以