我的编程空间,编程开发者的网络收藏夹
学习永远不晚

一文详解es6中的模块化

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

一文详解es6中的模块化

Es Module 的解析流程


在开始之前,我们先大概了解一下整个流程大概是怎么样的,先有一个大概的了解:

  • 阶段一:构建(Construction),根据地址查找 js 文件,通过网络下载,并且解析模块文件为 Module Record;

  • 阶段二:实例化(Instantiation),对模块进行实例化,并且分配内存空间,解析模块的导入和导出语句,把模块指向对应的内存地址;

  • 阶段三:运行(Evaluation),运行代码,计算值,并且将值填充到内存地址中;

Construction 构建阶段

  • loader 负责对模块进行寻址及下载。首先我们修改一个入口文件,这在 HTML 中通常是一个 <script type="module"></script> 的标签来表示一个模块文件。

entry.png

  • 模块继续通过 import语句声明,在 import声明语句中有一个 模块声明标识符(ModuleSpecifier),这告诉 loader 怎么查找下一个模块的地址。

09_module_specifier.png

  • 每一个模块标识号对应一个 模块记录(Module Record),而每一个 模块记录 包含了 JavaScript代码执行上下文ImportEntriesLocalExportEntriesIndirectExportEntriesStarExportEntries。其中 ImportEntries 值是一个 ImportEntry Records 类型,而 LocalExportEntriesIndirectExportEntriesStarExportEntries 是一个 ExportEntry Records 类型。

ImportEntry Records

  • 一个 ImportEntry Records 包含三个字段 ModuleRequestImportNameLocalName;

ModuleRequest: 一个模块标识符(ModuleSpecifier);

ImportName: 由 ModuleRequest 模块标识符的模块导出所需绑定的名称。值 namespace-object 表示导入请求是针对目标模块的命名空间对象的;

LocalName: 用于从导入模块中从当前模块中访问导入值的变量;

  • 详情可参考下图:imp.png
  • 下面这张表记录了使用 import 导入的 ImportEntry Records 字段的实例:
导入声明 (Import Statement Form)模块标识符 (ModuleRequest)导入名 (ImportName)本地名 (LocalName)
import React from "react";"react""default""React"
import * as Moment from "react";"react"namespace-obj"Moment"
import {useEffect} from "react";"react""useEffect""useEffect"
import {useEffect as effect } from "react";"react""useEffect""effect"

ExportEntry Records

  • 一个 ExportEntry Records 包含四个字段 ExportNameModuleRequestImportNameLocalName,和 ImportEntry Records不同的是多了一个 ExportName

ExportName: 此模块用于导出时绑定的名称。

  • 下面这张表记录了使用 export 导出的 ExportEntry Records 字段的实例:

    导出声明导出名模块标识符导入名本地名
    export var v;"v"nullnull"v"
    export default function f() {}"default"nullnull"f"
    export default function () {}"default"nullnull"default"
    export default 42;"default"nullnull"default"
    export {x};"x"nullnull"x"
    export {v as x};"x"nullnull"v"
    export {x} from "mod";"x""mod""x"null
    export {v as x} from "mod";"x""mod""v"null
    export * from "mod";null"mod"all-but-defaultnull
    export * as ns from "mod";"ns"mod"allnull
  • 回到主题

  • 只有当解析完当前的 Module Record 之后,才能知道当前模块依赖的是那些子模块,然后你需要 resolve 子模块,获取子模块,再解析子模块,不断的循环这个流程 resolving -> fetching -> parsing,结果如下图所示:

10_construction.png

  • 这个过程也称为 静态分析,不会运行JavaScript代码,只会识别 exportimport 关键字,所以说不能在非全局作用域下使用 import,动态导入除外。
  • 如果多个文件同时依赖一个文件呢,这会不会引起死循环,答案是不会的。
  • loader 使用 Module Map 对全局的 MOdule Record 进行追踪、缓存这样就可以保证模块只被 fetch 一次,每个全局作用域中会有一个独立的 Module Map。

MOdule Map 是由一个 URL 记录和一个字符串组成的key/value的映射对象。URL记录是获取模块的请求URL,字符串指示模块的类型(例如。“javascript”)。模块映射的值要么是模块脚本,null(用于表示失败的获取),要么是占位符值“fetching(获取中)”。

25_module_map.png

linking 链接阶段

  • 在所有 Module Record 被解析完后,接下来 JS 引擎需要把所有模块进行链接。JS 引擎以入口文件的 Module Record 作为起点,以深度优先的顺序去递归链接模块,为每个 Module Record 创建一个 Module Environment Record,用于管理 Module Record 中的变量。

30_live_bindings_01.png

  • Module Environment Record 中有一个 Binding,这个是用来存放 Module Record 导出的变量,如上图所示,在该模块 main.js 处导出了一个 count 的变量,在 Module Environment Record 中的 Binding 就会有一个 count,在这个时候,就相当于 V8 的编译阶段,创建一个模块实例对象,添加相对应的属性和方法,此时值为 undefined 或者 null,为其分配内存空间。
  • 而在子模块 count.js 中使用了 import 关键字对 main.js 进行导入,而 count.jsimportmain.jsexport 的变量指向的内存位置是一致的,这样就把父子模块之间的关系链接起来了。如下图所示:

no.png

  • 需要注意的是,我们称 export 导出的为父模块,import 引入的为子模块,父模块可以对变量进行修改,具有读写权限,而子模块只有读权限。

Evaluation 求值阶段

  • 在模块彼此链接完之后,执行对应模块文件中顶层作用域的代码,确定链接阶段中定义变量的值,放入内存中。

Es module 是如何解决循环引用的


  • Es Module 中有5种状态,分别为 unlinkedlinkinglinkedevaluatingevaluated,用循环模块记录(Cyclic Module Records)的 Status 字段来表示,正是通过这个字段来判断模块是否被执行过,每个模块只执行一次。这也是为什么会使用 Module Map 来进行全局缓存 Module Record 的原因了,如果一个模块的状态为 evaluated,那么下次执行则会自动跳过,从而包装一个模块只会执行一次。 Es Module 采用 深度优先 的方法对模块图进行遍历,每个模块只执行一次,这也就避免了死循环的情况了。

深度优先搜索算法(英语:Depth-First-Search,DFS)是一种用于遍历或搜索树或图的算法。这个算法会尽可能深地搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。

41_cyclic_graph.png

  • 看下面的例子,所有的模块只会运行一次:
// main.js
import { bar } from "./bar.js";
export const main = "main";
console.log("main");

// foo.js
import { main } from "./main.js";
export const foo = "foo";
console.log("foo");

// bar.js
import { foo } from "./foo.js";
export const bar = "bar";
console.log("bar");
  • 通过 node 运行 main.js ,得出以下结果:

results.png

  • 好了,这篇文章到这也就结束了。

原文地址:https://juejin.cn/post/7166046272300777508

【推荐学习:javascript高级教程】

以上就是一文详解es6中的模块化的详细内容,更多请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

一文详解es6中的模块化

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

一文详解es6中的模块化

es6模块化是浏览器端与服务器端通用的模块化开发规范,其设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,及输入和输出的变量。
2022-11-22

一文彻底搞定es6模块化

es6模块化是浏览器端与服务器端通用的模块化开发规范,其设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,及输入和输出的变量。
2023-05-14

剖析 ES6 模块:拆解模块化编程的精髓

ES6 模块化编程是一种将 JavaScript 代码组织成独立模块的方法,使得代码更容易维护、重用和协作。本文将深入剖析 ES6 模块,探讨其概念、优点和使用方式。
剖析 ES6 模块:拆解模块化编程的精髓
2024-03-02

一文详解Python中logging模块的用法

logging是Python标准库中记录常用的记录日志库,主要用于输出运行日志,可以设置输出日志的等级、日志保存路径、日志文件回滚等。本文主要来和大家聊聊它的具体用法,希望对大家有所帮助
2023-02-28

一文详解Node中的模块化、文件系统与环境变量

本篇文章带大家深入了解Node中的模块化、文件系统与环境变量,有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。
2023-05-14

详解nodejs 文本操作模块-fs模块(一)

JS的安全性问题,就决定了JS想要取操作数据库操作文件是不可实现的,而Nodejs作为服务端的JS,如果依然不能操作文件,那么又如何称之为服务端语言呢,所以在Nodejs中,提供了一个fs(File System)模块,以实现文件及目录的读
2022-06-04

一文详解Node中的Express和路由模块

本篇文章带大家一起学习Node,深入介绍一下Express和路由模块的使用方法,希望对大家有所帮助!
2023-05-14

ES6 模块的进阶之路:解锁高级模块化技巧

ES6 模块:高级模块化技术的探索之旅
ES6 模块的进阶之路:解锁高级模块化技巧
2024-03-02

一文详解nodejs的path模块使用

这篇文章主要为大家介绍了nodejs的path模块使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-16

一文详解Python中itertools模块的使用方法

itertools是python内置的模块,使用简单且功能强大。这篇文章主要为大家详细介绍了itertools模块的使用方法,感兴趣的小伙伴可以了解一下
2023-03-22

Node.js中的CommonJS模块化规范详解

这篇文章主要介绍了Node.js中的CommonJS模块化规范,本文主要介绍了 CommonJS 规范在 Node 中的简单应用,主要就是导入和导出模块,需要的朋友可以参考下
2023-02-06

详解python时间模块中的datetime模块

Python提供了多个内置模块用于操作日期时间,像calendar,time,datetime。time模块我在之前的文章已经有所介绍,它提供的接口与C标准库time.h基本一致。相比于time模块,datetime模块的接口则更直观、更容
2022-06-04

一文带你了解Node.js中的http模块

本篇文章给大家了解一下Node.js http模块,介绍一下使用http模块创建服务器的方法,希望对大家有所帮助!
2023-05-14

一文带你了解Node.js中的path模块

Node.js和Python技术类似, 都致力于能够实现跨平台的通用代码。 为此,针对路径的拼接, Node.js提供了path模块,本文就来讲讲path模块的使用
2023-03-21

编程热搜

目录