iis服务器助手广告
返回顶部
首页 > 资讯 > 前端开发 > 其他 >一文彻底搞定es6模块化
  • 172
分享到

一文彻底搞定es6模块化

ES6 2023-05-14 22:05:05 172人浏览 泡泡鱼
摘要

Es Module 的解析流程在开始之前,我们先大概了解一下整个流程大概是怎么样的,先有一个大概的了解:阶段一:构建(Construction),根据地址查找 js 文件,通过网络下载,并且解析模块文件为 Module Record;阶段二

Es Module 的解析流程

  • 在开始之前,我们先大概了解一下整个流程大概是怎么样的,先有一个大概的了解:
  1. 阶段一:构建(Construction),根据地址查找 js 文件,通过网络下载,并且解析模块文件为 Module Record;
  2. 阶段二:实例化(Instantiation),对模块进行实例化,并且分配内存空间,解析模块的导入和导出语句,把模块指向对应的内存地址;
  3. 阶段三:运行(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;
  1. ModuleRequest: 一个模块标识符(ModuleSpecifier);
  2. ImportName: 由 ModuleRequest 模块标识符的模块导出所需绑定的名称。值 namespace-object 表示导入请求是针对目标模块的命名空间对象的;
  3. LocalName: 用于从导入模块中从当前模块中访问导入值的变量;
  • 详情可参考下图:imp.png
  • 下面这张表记录了使用 import 导入的 ImportEntry Records 字段的实例:
导入声明 (Import Statement From)模块标识符 (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
  1. 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

  • 好了,这篇文章到这也就结束了。《JavaScript视频教程

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

--结束END--

本文标题: 一文彻底搞定es6模块化

本文链接: https://www.lsjlt.com/news/207035.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

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

下载Word文档
猜你喜欢
  • 一文彻底搞定es6模块化
    Es Module 的解析流程在开始之前,我们先大概了解一下整个流程大概是怎么样的,先有一个大概的了解:阶段一:构建(Construction),根据地址查找 js 文件,通过网络下载,并且解析模块文件为 Module Record;阶段二...
    99+
    2023-05-14
    ES6
  • 一文彻底搞懂IO底层原理
    目录一、混乱的 IO 概念二、用户空间和内核空间三、IO模型3.1、BIO(Blocking IO)3.2、“C10K”问题3.3、NIO非阻塞模型3.4、IO多路复用模型3.4.1...
    99+
    2024-04-02
  • 一文带你彻底搞懂Vuex
    大家可以思考一下,组件之间的传值有哪些?有父子通讯,兄弟组件通讯......但是传参对于多层嵌套就显得非常繁琐,代码维护也会非常麻烦。因此vuex就是把组件共享状态抽取出来以一个全局单例模式管理,把共享的数据函数放进vuex中,任何组件都可...
    99+
    2022-11-22
    Vue vue3 vue.js VueX
  • 一文彻底搞清golang占位符
    本篇文章给大家带来了关于golang的相关知识,其中主要给大家聊聊golang中的占位符,感兴趣的朋友一起来看一下吧,希望对大家有帮助。xdm ,写 C/C++ 语言的时候有格式控制符,例如 %s , %d , %c , %p 等等在写 g...
    99+
    2023-05-14
    Golang
  • 一文彻底搞定Java哈希表和哈希冲突
    目录一、什么是哈希表?二、什么是哈希函数?三、什么是哈希冲突?一、什么是哈希表? 哈希表也叫散列表,它是基于数组的。这间接带来了一个优点:查找的时间复杂度为 O(1)、当然,它的插入...
    99+
    2024-04-02
  • 一文彻底搞懂Kotlin中的协程
    产生背景 为了解决异步线程产生的回调地狱 //传统回调方式 api.login(phone,psd).enquene(new Callback<User>(){ ...
    99+
    2024-04-02
  • 一文彻底搞懂Python中__str__和__repr__
    __str__和__repr__有什么异同字符串的表示形式我们都知道,Python的内置函数 repr()​ 能够把对象用字符串的形式表达出来,方便我们辨认。这就是“字符串表示形式”。repr()​ 就是通过 __repr__​ 这个特殊方...
    99+
    2023-05-14
    Python 函数 内置
  • 一文详解es6中的模块化
    Es Module 的解析流程在开始之前,我们先大概了解一下整个流程大概是怎么样的,先有一个大概的了解:阶段一:构建(Construction),根据地址查找 js 文件,通过网络下载,并且解析模块文件为 Module Record;阶段二...
    99+
    2022-11-22
    ES6 前端 JavaScript 面试
  • 一篇文章彻底搞懂jdk8线程池
    这可能是最简短的线程池分析文章了。 顶层设计,定义执行接口 Interface Executor(){ void execute(Runnable command); ...
    99+
    2024-04-02
  • 一篇文章彻底搞懂C++常见容器
    目录1.概述2.容器详解2.1vector(向量)2.2deque(双端队列)2.3list(列表)2.4 array(数组)2.5 string(字符串)2.6 map(映射)2....
    99+
    2023-02-13
    C++常用容器 c++容器用法 c++容器的使用
  • 一篇文章彻底搞懂Python切片操作
    目录引言一、Python可切片对象的索引方式二、Python切片操作的一般方式三、Python切片操作详细例子1.切取单个值2.切取完整对象3.start_index和end_ind...
    99+
    2024-04-02
  • 一篇文章带你彻底搞懂Redis 事务
    目录Redis 事务简介Redis 事务基本指令实例分析Redis 事务与 ACID总结Redis 事务简介 Redis 只是提供了简单的事务功能。其本质是一组命令的集合,事务支持一...
    99+
    2022-11-13
    redis有几种部署方式 redis事务三大特性 redis怎么做到事务回滚
  • 一文带你彻底搞懂Nginx反向代理
    一文带你彻底搞懂Nginx反向代理 一、什么是反向代理1.1 正向代理1.2 反向代理1.3 总结 二、配置反向代理2.1 准备 Tomcat2.2 配置 Nginx 一、什么是反...
    99+
    2023-09-20
    nginx 服务器 运维 shell 反向代理
  • 一文彻底搞懂PHP进程信号处理
    本篇文章给大家带来了关于PHP的相关知识,其中主要详细介绍了PHP 进程信号处理,感兴趣的朋友下面一起来看一下吧,希望对大家有帮助。背景前两周老大给我安排了一个任务,写一个监听信号的包。因为我司的项目是运行在容器里边的,每次上线,需要重新打...
    99+
    2023-05-14
    进程 PHP
  • 一篇文章带你彻底搞懂Redis 事务
    目录Redis 事务简介Redis 事务基本指令实例分析Redis 事务与 ACID总结Redis 事务简介 Redis 只是提供了简单的事务功能。其本质是一组命令的集合,事务支持一次执行多个命令,在事务执行过程中,会顺...
    99+
    2024-04-02
  • 一文彻底搞懂java多线程和线程池
    目录 什么是线程 一. Java实现线程的三种方式1.1、继承Thread类1.2、实现Runnable接口,并覆写run方法二. Callable接口...
    99+
    2024-04-02
  • 一篇文章彻底搞懂python正则表达式
    目录前言1. 正则表达式的基本概念2. python的正则表达式re模块3. 正则表达式语法(1)匹配单个字符(2)匹配多个字符(3)边界匹配(4)分组匹配4. re模块相关方法使用...
    99+
    2024-04-02
  • 一文带你彻底搞懂JavaScript正则表达式
    目录正则表达式的概述什么是正则表达式正则表达式的作用正则表达式的特点正则表达式在js中的使用创建正则表达式测试正则表达式 test正则表达式中的特殊字符正则表达式的组成边界符字符类量...
    99+
    2024-04-02
  • 一文彻底搞懂Vue的MVVM响应式原理
    目录前言Vue的MVVM原理创建一个html示例在MVue.js中创建MVue入口创建Compile1.处理元素节点compileElement(child)2.处理文本节点comp...
    99+
    2024-04-02
  • js深拷贝与浅拷贝一文彻底搞懂
    深拷贝浅拷贝只是针对引用数据类型 JavaScript数据类型包括基本数据类型(Number,String,boolean,Null,Undefined,Symbol)、引用数据类型...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作