JavaScript 模块入门
大家好👋,
这是我关于 JavaScript ES6 概念的第二篇文章。如果你还没有读过第一篇,可以在这里阅读。在本文中,我将讨论 JavaScript 模块入门所需的一切。
目录 -
- 历史
- 什么是模块?为什么我们需要它们?
- CommonJS 模块简介
- ES6 模块
- 进出口
- ES6 模块的一些重要特性
历史
以前,应用程序通常比较简单小巧,代码也同样如此。当 JavaScript 代码比较短小的时候,将它们放在一个文件中并不是什么难事。但随着应用程序的改进和规模的扩大,代码量也随之增长,管理这些代码并将其放在一个文件中变得非常困难。
于是,模块的概念应运而生。为了实现这一概念,人们发明了各种各样的方法,例如 AMD 和 UMD。
但这些现在已成为历史的一部分,一般不再使用,但您可以在一些较旧的应用程序中找到它们。
另一个被发明的模块系统是CommonJS,它是为 Node.js 服务器创建的。我们将在本文后面讨论 CommonJS 模块系统。
语言级模块系统于 2015 年问世,通常称为ES6 模块。此外,现在所有主流浏览器和 Node.js 都支持它,因此我们将在本文中更多地讨论 ES6 模块。
但首先,让我们讨论一下什么是模块。
什么是模块?为什么我们需要它们?
模块只是数据的构建块,我们可以使用它来构建大型应用程序。
模块背后的基本思想是我们可以导出一部分代码,然后将其导入到其他文件中使用。
使用模块,我们可以将大脚本分解为更小的模块,这些模块也可以在其他文件中使用。
让我们借助一张图片来理解这一点——
在第一张图片中,您可以看到所有函数或代码都放在一个文件中,因此文件很大。现在想象一下,如果文件里还有20-30个函数和其他代码,这个文件会变得多大?这么大的文件将很难理解和管理。
这样,我们将代码拆分成多个模块,如第二张图所示。我们将函数分别写在不同的模块中并导出。现在,它们可以被 index.js 和其他模块导入。如我们所见,模块 A、B 和 C 被 index.js 导入;模块 A 也被模块 B 导入。现在,由于函数位于不同的模块中,并且 index.js 很小,我们的代码更容易理解和管理。
注意: JS模块可以导出变量、函数、对象等。
让我们来看看 JS 模块的一些好处 -
可维护性:
正如我们之前提到的,如果我们的代码被拆分并组织良好,维护起来就会更容易。此外,模块的目标始终是尽可能地独立,以便它能够独立地发展和改进。如果我们对模块进行更改,代码库也无需进行太多改动。
可重用性:
借助模块,我们可以反复重用代码。我们所要做的就是将其从文件中导出,然后项目中的所有其他文件都可以导入并使用它。
可共享性:
由于模块旨在独立运行,因此我们也可以与其他开发人员共享它们,他们可以将它们导入到他们的项目中并使用它们。npm 就是一个典型的例子。借助 npm,我们可以导入其他开发人员共享的包(包含模块所需的所有文件),并在我们的项目中使用它们。
CommonJS 模块简介
CommonJS 模块系统是Node.js中用于处理模块的标准。
CommonJS 模块以同步加载的方式进行处理,并按照 JavaScript 运行时找到它们的顺序进行处理。
该系统的发明考虑到了服务器端 JavaScript,并不适用于客户端。
此外,npm 生态系统基于 CommonJS 模块系统。
让我们看一个如何在 CommonJS 中导入和导出的小例子 -
我们可以通过简单地使用exports关键字来导出任何函数、类、变量等:
// 📂 func.js
exports.add = (a, b) => a + b;
然后任何 JavaScript 文件都可以导入它。导入语法如下:
const package = require('module-name')
使用这种语法,我们可以使用require关键字导入模块:
// 📂 main.js
const addModule = require('./func.js')
addModule.add(2,4)
在讨论 ES6 模块之前,让我们先看看 CommonJS 模块系统和 ES6 模块之间的主要区别 -
1)服务器端 Javascript 使用 CommonJS 模块系统,而客户端 Javascript 使用 ES6 模块。
2)CommonJS 模块系统使用exports关键字进行导出,使用require关键字进行导入,而 ES6 模块使用export关键字进行导出,使用import关键字进行导入。
ES6 模块
我们已经讨论了什么是模块,现在让我们讨论一下 ES6 模块。
ES6 模块使用:
export:导出函数、类等。import
:允许模块导入导出的模块。
让我们看一个例子 -
这里我们有三个文件:index.html,func.js和main.js
<!-- 📂 index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<title>modules</title>
<script type="module" src="./main.js"></script>
</head>
<body></body>
</html>
// 📂 func.js
export const sayHi = (user) => {
console.log(`Hi!!! ${user}`);
};
// 📂 main.js
import { sayHi } from "./func.js";
sayHi("Alok"); // Hi!!! Alok
您可以看到func.js文件正在导出一个名为sayHi()的函数(使用 export 关键字) ,该函数只是 console.log out Hi !!! ${user}
。
main.js从func.js文件导入了相同的sayHi()函数(使用 import 关键字)。之后,我们用输入“Alok”运行该函数,并在 console.log 中输出“ Hi !!! Alok”。
我们可以看到,我们没有在 main.js 文件中定义sayHi()函数,但是,我们仍然可以访问和使用它,因为它现在已从 func.js 文件导入到我们的 main.js 文件中。
注意:要使用模块,我们必须使用属性指定我们的脚本是一个模块正如我们在上面的 index.html 中所做的那样:
<script type="module" src="main.js"></script>
进出口
导入和导出 ES6 模块有多种方法。您可以根据需要使用它们。
让我们逐一讨论一下:
先出口后报关-
正如我们在前面的例子中所看到的,您所要做的就是在类、数组、函数等之前放置一个export关键字,无论您想导出什么。
然后,您可以使用import关键字加上花括号中要导入的内容列表来导入它们,例如import {...}。
例子 -
// 📂 func.js
//exporting a function
export const sayHi = (user) => {
console.log(`Hi!!! ${user}`);
};
//exporting a variable
export let person = "Alok";
//exporting an array
export let personArray = ["Alok", "Aman", "Rajan"];
// All are valid
// 📂 main.js
//importing using a list of what to import
import { sayHi,person,personArray } from "./func.js";
单独导出-
我们还可以单独使用export关键字,并使用要导出的内容列表进行导出。
然后按照我们之前所做的方式导入它们。
例子 -
// 📂 func.js
const sayHi = (user) => {
console.log(`Hi!!! ${user}`);
};
let person = "Alok";
let personArray = ["Alok", "Aman", "Rajan"];
// exporting all using a list
export { sayHi, person, personArray };
// 📂 main.js
//importing using a list of what to import
import { sayHi,person,personArray } from "./func.js";
进口 *
到目前为止,我们已经使用要导入的内容列表进行了导入,但如果要导入的内容很多,我们可以使用以下方法将所有内容作为对象导入 -
导入 * 作为
例子 -
// 📂 func.js
const sayHi = (user) => {
console.log(`Hi!!! ${user}`);
};
let person = "Alok";
let personArray = ["Alok", "Aman"];
// exporting all using a list
export { sayHi, person, personArray };
// 📂 main.js
//importing using import * as <obj>
import * as func from "./func.js";
//usage
func.sayHi("Alok");// Hi!!! Alok
console.log(func.person);// Alok
console.log(func.personArray);// ["Alok", "Aman”]
导入“as” -
我们还可以使用不同的名称导入类、变量等。例如,我们可以使用as关键字导入 person 变量,并使用另一个名称(user)。
例子 -
// 📂 func.js
const sayHi = (user) => {
console.log(`Hi!!! ${user}`);
};
let person = "Alok";
let personArray = ["Alok", "Aman"];
// exporting all using a list
export { sayHi, person, personArray };
// 📂 main.js
//importing using "as"
import { sayHi as Hi, person as user, personArray } from "./func.js";
//usage
Hi("Alok"); //Hi!!! Alok
console.log(user); //Alok
console.log(personArray); //["Alok", "Aman"]
导出“为” -
类似地,您可以使用as关键字以不同的名称导出。
例子 -
// 📂 func.js
const sayHi = (user) => {
console.log(`Hi!!! ${user}`);
};
let person = "Alok";
let personArray = ["Alok", "Aman"];
//exporting using "as"
export { sayHi as Hi, person as user, personArray };
// 📂 main.js
//importing using a list
import { Hi, user, personArray } from "./func.js";
//usage
Hi("Alok"); //Hi!!! Alok
console.log(user); //Alok
console.log(personArray); //["Alok", "Aman"]
导出默认值-
我们可以使用default关键字将任何导出设为默认导出。
通常,开发人员在模块中保留单个导出以保持代码清洁,在这种情况下,当我们有单个导出时,我们可以使用默认导出。
如果我们有默认导出,那么我们可以直接导入它,而无需使用花括号{ }。
例子 -
// 📂 func.js
const sayHi = (user) => {
console.log(`Hi!!! ${user}`);
};
//exporting using default
export default sayHi;
// 📂 main.js
//importing without { }
import sayHi from "./func.js";
sayHi("Alok"); // Hi!!! Alok
请注意,在导入 sayHi() 时,我们直接使用sayHi,而不是{ sayHi }。
注意:我们还可以将默认导出与命名导出混合使用,但一个模块只能有一个默认导出,如下所示:
// 📂 func.js
const sayHi = (user) => {
console.log(`Hi!!! ${user}`);
};
let person = "Alok";
let personArray = ["Alok", "Aman"];
//exporting using default
export default sayHi;
//exporting using list
export { person, personArray };
// 📂 main.js
//importing without { }
import sayHi from "./func.js";
//importing using a list
import { person, personArray } from "./func.js";
//usage
sayHi("Alok"); //Hi!!! Alok
console.log(person); //Alok
console.log(personArray); //["Alok", "Aman"]
但正如我们所讨论的,开发人员通常只在模块中保留一个导出,并且不会混合它们以保持代码清洁。
ES6 模块的一些重要特性
始终“使用严格”
默认情况下,模块始终使用严格。
分配给未声明的变量将会产生错误。
例子 -
<script type="module">
a = 5; {/* error */}
</script>
模块级范围
一个模块不能访问另一个模块的顶级变量和函数。
例子 -
<script type="module">
{/* scope of person is only this module script */}
let person = "Alok";
</script>
<script type="module">
alert(person);{/* Error: person is not defined */}
</script>
模块代码仅在第一次导入时执行
如果多个模块导入一个模块(例如,func.js),那么只有在第一次导入时它才会被执行并提供给所有导入者。
“this”未定义
在模块中,顶层这是未定义的。
例子 -
<script>
alert(this); {/* global object */}
</script>
<script type="module">
alert(this); {/* undefined */}
</script>
注意:感谢 Seijinx 提供的信息 - “IE11 不支持 ES 模块。在这种情况下,您仍然需要依赖捆绑器。”
我尽量保持其简单和精确,感谢您阅读到最后,如果您发现任何拼写错误/错误,请向我报告,以便我可以纠正它🙂
如果您发现这有用,那么您可以与其他人分享:)
欢迎留言,我们聊聊吧👋👋👋
阅读本系列的其他博客
文章来源:https://dev.to/thecoollearner/getting-started-with-javascript-modules-2mkg