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

es6模块化如何使用

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

北京

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

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

看不清楚,换张图片

免费获取短信验证码

es6模块化如何使用

本篇内容介绍了“es6模块化如何使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

天下苦 CommonJs 久矣

  • Es Module 的独特之处在于,既可以通过浏览器原生加载,也可以与第三方加载器和构建工具一起加载。

  • 支持 Es module 模块的浏览器可以从顶级模块加载整个依赖图,且是异步完成。浏览器会解析入口模块,确定依赖,并发送对依赖模块的请求。这些文件通过网络返回后,浏览器就会解析它们的依赖,,如果这些二级依赖还没有加载,则会发送更多请求。

  • 这个异步递归加载过程会持续到整个应用程序的依赖图都解析完成。解析完成依赖图,引用程序就可以正式加载模块了。

  • Es Module 不仅借用了 CommonJsAMD 的很多优秀特性,还增加了一些新行为:

  • Es Module 默认在严格模式下执行;

  • Es Module 不共享全局命名空;

  • Es Module 顶级的 this 的值是 undefined(常规脚本是window);

  • 模块中的 var 声明不会添加到 window 对象;

  • Es Module 是异步加载和执行的;

export 和 import

  • 模块功能主要由两个命令构成: exportsimport

  • export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

export的基本使用

  • 导出的基本形式:

export const nickname = "moment";
export const address = "广州";
export const age = 18;
  • 当然了,你也可以写成以下的形式:

const nickname = "moment";
const address = "广州";
const age = 18;

export { nickname, address, age };
  • 对外导出一个对象和函数

export function foo(x, y) {
 return x + y;
}

export const obj = {
 nickname: "moment",
 address: "广州",
 age: 18,
};

// 也可以写成这样的方式
function foo(x, y) {
 return x + y;
}

const obj = {
 nickname: "moment",
 address: "广州",
 age: 18,
};

export { foo, obj };
  • 通常情况下,export输出的变量就是本来的名字,但是可以使用as关键字重命名。

const address = "广州";
const age = 18;

export { nickname as name, address as where, age as old };
  • 默认导出,值得注意的是,一个模块只能有一个默认导出:

export default "foo";

export default { name: 'moment' }

export default function foo(x,y) {
 return x+y
}

export { bar, foo as default };

export 的错误使用

  • 导出语句必须在模块顶级,不能嵌套在某个块中:

if(true){
export {...};
}
  • export 必须提供对外的接口:

// 1只是一个值,不是一个接口export 1// moment只是一个值为1的变量const moment = 1export moment// function和class的输出,也必须遵守这样的写法function foo(x, y) {    return x+y
}export foo复制代码

import的基本使用

  • 使用 export 命令定义了模块的对外接口以后,其他js文件就可以通过 import 命令加载整个模块

import {foo,age,nickname} from '模块标识符'
  • 模块标识符可以是当前模块的相对路径,也可以是绝对路径,也可以是纯字符串,但不能是动态计算的结果,例如凭借的字符串。

  • import 命令后面接受一个花括弧,里面指定要从其他模块导入的变量名,而且变量名必须与被导入模块的对外接口的名称相同。

  • 对于导入的变量不能对其重新赋值,因为它是一个只读接口,如果是一个对象,可以对这个对象的属性重新赋值。导出的模块可以修改值,导入的变量也会跟着改变。

es6模块化如何使用

  • 从上图可以看得出来,对象的属性被重新赋值了,而变量的则报了 Assignment to constant variable 的类型错误。

  • 如果模块同时导出了命名导出和默认导出,则可以在 import 语句中同时取得它们。可以依次列出特定的标识符来取得,也可以使用 * 来取得:

// foo.js
export default function foo(x, y) {
 return x + y;
}

export const bar = 777;

export const baz = "moment";

// main.js
import { default as foo, bar, baz } from "./foo.js";

import foo, { bar, baz } from "./foo.js";

import foo, * as FOO from "./foo.js";

动态 import

  • 标准用法的 import 导入的模块是静态的,会使所有被导入的模块,在加载时就被编译(无法做到按需编译,降低首页加载速度)。有些场景中,你可能希望根据条件导入模块或者按需导入模块,这时你可以使用动态导入代替静态导入。

  • 关键字 import 可以像调用函数一样来动态的导入模块。以这种方式调用,将返回一个 promise

import("./foo.js").then((module) => {  const { default: foo, bar, baz } = module;  console.log(foo); // [Function: foo]
 console.log(bar); // 777
 console.log(baz); // moment});复制代码

使用顶层 await

  • 在经典脚本中使用 await 必须在带有 async 的异步函数中使用,否则会报错:

import("./foo.js").then((module) => {
 const { default: foo, bar, baz } = module;
 console.log(foo); // [Function: foo]
 console.log(bar); // 777
 console.log(baz); // moment
});
  • 而在模块中,你可以直接使用 Top-level await:

const p = new Promise((resolve, reject) => {  resolve(777);
});const result = await p;console.log(result);
// 777正常输出

import 的错误使用

  • 由于import是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。

// 错误
import { 'b' + 'ar' } from './foo.js';

// 错误
let module = './foo.js';
import { bar } from module;

// 错误
if (x === 1) {
 import { bar } from './foo.js';
} else {
 import { foo } from './foo.js';
}

在浏览器中使用 Es Module

  • 在浏览器上,你可以通过将 type 属性设置为 module 用来告知浏览器将 script 标签视为模块。

<script type="module" class="lazy" data-src="./main.mjs"></script><script type="module"></script>
  • 模块默认情况下是延迟的,因此你还可以使用 defer 的方式延迟你的 nomodule 脚本:

  <script type="module">      
 console.log("模块情况下的");
   </script>    
   <script class="lazy" data-src="./main.js" type="module" defer></script>
   <script>
     console.log("正常 script标签");    
     </script>

es6模块化如何使用

  • 在浏览器中,引入相同的 nomodule 脚本会被执行多次,而模块只会被执行一次:

    <script class="lazy" data-src="./foo.js"></script>    <script class="lazy" data-src="./foo.js"></script>

   <script type="module" class="lazy" data-src="./main.js"></script>
   <script type="module" class="lazy" data-src="./main.js"></script>
   <script type="module" class="lazy" data-src="./main.js"></script>

es6模块化如何使用

模块的默认延迟

  • 默认情况下,nomodule 脚本会阻塞 HTML 解析。你可以通过添加 defer 属性来解决此问题,该属性是等到 HTML 解析完成之后才执行。

es6模块化如何使用

  • deferasync 是一个可选属性,他们只可以选择其中一个,在 nomodule 脚本下,defer 等到 HTML 解析完才会解析当前脚本,而 async 会和 HTML 并行解析,不会阻塞 HTML 的解析,模块脚本可以指定 async 属性,但对于 defer 无效,因为模块默认就是延迟的。

  • 对于模块脚本,如果存在 async 属性,模块脚本及其所有依赖项将于解析并行获取,并且模块脚本将在它可用时进行立即执行。

Es Module 和 Commonjs 的区别

  • 讨论 Es Module 模块之前,必须先了解 Es ModuleCommonjs 完全不同,它们有三个完全不同:

  1. CommonJS 模块输出的是一个值的拷贝,Es Module 输出的是值的引用;

  2. CommonJS 模块是运行时加载,Es Module 是编译时输出接口。

  3. CommonJS 模块的 require() 是同步加载模块,ES6 模块的import命令是异步加载,有一个独立的模块依赖的解析阶段。

  • 第二个差异是因为 CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 Es Module 不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。

  • Commonjs 输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。具体可以看上一篇写的文章。

  • Es Module 的运行机制与 CommonJS 不一样。JS引擎 对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,import就是一个连接管道,原始值变了,import 加载的值也会跟着变。因此,Es Module 是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。

Es Module 工作原理的相关概念

  • 在学习工作原理之前,我们不妨来认识一下相关的概念。

Module Record

  • 模块记录(Module Record) 封装了关于单个模块(当前模块)的导入和导出的结构信息。此信息用于链接连接模块集的导入和导出。一个模块记录包括四个字段,它们只在执行模块时使用。其中这四个字段分别是:

  1. Realm: 创建当前模块的作用域;

  2. Environment:模块的顶层绑定的环境记录,该字段在模块被链接时设置;

  3. Namespace:模块命名空间对象是模块命名空间外来对象,它提供对模块导出绑定的基于运行时属性的访问。模块命名空间对象没有构造函数;

  4. HostDefined:字段保留,以按 host environments 使用,需要将附加信息与模块关联。

Module Environment Record

  • 模块环境记录是一种声明性环境记录,用于表示ECMAScript模块的外部作用域。除了普通的可变和不可变绑定之外,模块环境记录还提供了不可变的 import 绑定,这些绑定提供了对存在于另一个环境记录中的目标绑定的间接访问。

不可变绑定就是当前的模块引入其他的模块,引入的变量不能修改,这就是模块独特的不可变绑定。

Es Module 的解析流程

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

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

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

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

Construction 构建阶段

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

es6模块化如何使用

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

es6模块化如何使用

  • 每一个模块标识号对应一个 模块记录(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: 用于从导入模块中从当前模块中访问导入值的变量;

  • 详情可参考下图:es6模块化如何使用

  • 下面这张表记录了使用 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,结果如下图所示:

es6模块化如何使用

  • 这个过程也称为 静态分析,不会运行JavaScript代码,只会识别 exportimport 关键字,所以说不能在非全局作用域下使用 import,动态导入除外。

  • 如果多个文件同时依赖一个文件呢,这会不会引起死循环,答案是不会的。

  • loader 使用 Module Map 对全局的 MOdule Record 进行追踪、缓存这样就可以保证模块只被 fetch 一次,每个全局作用域中会有一个独立的 Module Map。

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

es6模块化如何使用

linking 链接阶段

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

es6模块化如何使用

  • 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 的变量指向的内存位置是一致的,这样就把父子模块之间的关系链接起来了。如下图所示:

es6模块化如何使用

  • 需要注意的是,我们称 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的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。

es6模块化如何使用

  • 看下面的例子,所有的模块只会运行一次:

// 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 ,得出以下结果:

es6模块化如何使用

“es6模块化如何使用”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

免责声明:

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

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

es6模块化如何使用

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

下载Word文档

猜你喜欢

es6模块化如何使用

本篇内容介绍了“es6模块化如何使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!天下苦 CommonJs 久矣Es Module 的独特之
2023-07-05

ES6模块怎么使用

本篇内容主要讲解“ES6模块怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“ES6模块怎么使用”吧!ES6 引入了模块化,其设计思想是在编译时就能确定模块的依赖关系,以及输入和输出的变量。
2023-06-03

如何解析es6模块

这篇文章主要为大家分析了如何解析es6模块的相关知识点,内容详细易懂,操作细节合理,具有一定参考价值。如果感兴趣的话,不妨跟着跟随小编一起来看看,下面跟着小编一起深入学习“如何解析es6模块”的知识吧。ES6就是ECMAScript的第6个
2023-06-28

Node.js中ES6模块化及Promise对象怎么使用

本文小编为大家详细介绍“Node.js中ES6模块化及Promise对象怎么使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“Node.js中ES6模块化及Promise对象怎么使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一
2023-07-02

ES6 模块的实战运用:构建模块化的 JavaScript 应用

ES6 模块提供了一种将 JavaScript 代码组织成模块化单元的方式,从而提高了应用程序的可维护性和可重用性。本文将深入探讨 ES6 模块的实战运用,并通过示例代码展示如何构建模块化的 JavaScript 应用。
ES6 模块的实战运用:构建模块化的 JavaScript 应用
2024-03-02

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

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

一文彻底搞定es6模块化

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

一文详解es6中的模块化

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

揭开 ES6 模块面纱:深入解析模块化概念

ES6 模块化是一种革命性的 JavaScript 特性,它通过将代码组织成模块来提升开发效率和可维护性。本文深入探讨 ES6 模块的核心概念,揭开其幕后运作原理,为开发人员提供全面指南。
揭开 ES6 模块面纱:深入解析模块化概念
2024-03-02

揭秘 ES6 模块:深入探索模块化设计的魅力

ES6 模块是一种强大的工具,使开发者能够创建和管理模块化代码,实现代码的可重用性、可维护性和可扩展性。
揭秘 ES6 模块:深入探索模块化设计的魅力
2024-03-02

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

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

如何使用ES6简化代码

小编给大家分享一下如何使用ES6简化代码,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!使用 ES6 简化代码ES6 已经发展很多年了,兼容性也已经很好了。恰当地使
2023-06-27

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录