了解JavaScript中的ES模块

发表于:2020-7-09 09:10

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:方章和    来源:新钛云服

#
Java
#
java
分享:
  模块在每个编程语言中都能找到。它是一种能在一个代码块中引入另一个代码的模块功能的方法。这些模块是开发人员开发的具有特定功能的代码,可以在项目其他地方重复使用。模块化为你提供了一些好处,比如代码的可复用性和模块化。
  如果你之前一直在使用JavaScript开发代码,你就会知道早期的JavaScript没有这样的模块功能。开发者为了将js文件加载到他们的页面中,不得不使用HTML <script>标签。直到后来,几种模块定义规范才开始出现。
  · CommonJS — Node.js中使用的module.export和require语法
  · Asynchronous Module Definition (AMD)
  · Universal Module Definition (UMD)
  · ES Modules
  首先,我们先来看下为什么需要这些模块化定义的规范
  为什么我们需要模块化
  每当你思考程序是如何工作的,其实它们所做的就是变量管理。它为变量赋值,修改变量,将两个变量组合在一起等等。但是当变量的数量随着你的应用程序的规模而增加时,你会发现你的代码管理起来会非常麻烦。
  解决这个问题的方法是只需要考虑几个变量。JavaScript实现这一目标的方式被称为作用域。由于JavaScript中的作用域定义方式导致函数不能访问其他函数中定义的变量。虽然这使得你的变量不能被其他函数访问,但这又引起了另一个问题--你很难在不同函数之间共享变量。解决这个问题就是将它们定义在全局作用域上。
  虽然这种方法可以解决问题,但并不推荐。你的脚本标签应该按照正确的顺序,你必须确保没有人改变这个顺序。如果顺序确实发生了变化,你的应用程序将抛出一个错误。这使得代码管理变得很棘手。你永远不知道什么会破坏什么。任何函数都可以访问,所以你不知道哪些函数依赖于哪些脚本。
  另一个问题是,在该全局范围内的每一部分代码都可以被其它代码改变。这将允许恶意的和非恶意的代码访问甚至修改你的全局变量,无论是否有恶意。
  所以需要引入模块来帮助克服这些问题。
  模块化是如何优雅的解决这个问题的
  模块可以让你更好地组织和管理变量和函数。通常情况下,属于同一功能的函数和变量会被放在一个模块中。这就把这些变量放到了模块作用域中。模块作用域可以用来在模块中的函数之间共享变量。
  这也使得变量也可以为其他模块所用。他们可以明确的说哪些变量、类或函数应该对外部模块可用。这就是所谓的导出。一旦你有了一个导出,其他模块就可以明确地说它们依赖于该变量、类或函数。由于这种明确的关系,你将知道如果你删除一个模块,哪些模块将被破坏。
  一旦你能够导入和导出变量和函数,你就可以更容易地将你的代码分割和分解成可以独立工作的代码块。你以后可以通过使用这些模块来构建你的应用程序,类似于用乐高积木来构建。
  为了实现这个超级有用的功能,已经多次尝试用JavaScript添加模块功能。
  现有的模块化系统
  CommonJS
  CommonJS是NodeJS一直在使用的。使用Node,你会得到CommonJS的module.exports和require并可以直接使用。但是,与Node不同的是,浏览器并不支持CommonJS。此外,CommonJS会同步加载模块,因此对于浏览器来说,它不是一个最佳的解决方案。你可以使用Webpack或Browserify等打包程序来解决这个问题。
//   filename: bar.js
//   dependencies
var $ = require('jquery');
//   methods
function myFunction(){};
//   exposed public method (single)
module.exports = myFunction;
Asynchronous Module Definition (AMD)
  AMD诞生于一群不喜欢CommonJS发展方向的开发者。事实上,AMD在发展初期就从CommonJS中分裂出来了。AMD和CommonJS的主要区别在于AMD是异步加载模块的。这在浏览器中非常受欢迎,因为启动时间对于良好的用户体验至关重要。
//   filename: bar.js
define(['jquery'], function ($) {
//   methods
function myFunction(){};
//   exposed public methods
return myFunction;
});
  由于CommonJS和AMD在各自的领域相当流行,所以需要一个 "通用 "的模式来支持两种风格。但事实证明,UMD又乱又丑。虽然它确实同时支持AMD和CommonJS,也支持老式的 "全局 "变量定义。
  Universal Module Definition (UMD)
  什么是ES模块
  众所周知,JavaScript缺乏一个标准模块定义规范。因此,在ES6中提出了一个单一的、原生的模块标准。import和export指令允许程序在不运行代码的情况下导入导出,从而建立一个完整的模块依赖关系。
  其语法格式还是简单好用的,并且兼容浏览器中的同步和异步操作模式。ES模块在浏览器中很快就可以使用,但在Node.js中,要想出一个向后兼容并能实现增量升级的解决方案就有点难了。在Node.js中,原生的ES模块在实验性模块标志后面长期可用。
  下面以ES6模块为例。
JavaScript
//------ library.js ------
export const sqrt = Math.sqrt;
export function square(x) {
return x * x;
}
export function diagonal(x, y) {
return sqrt(square(x) + square(y));
}
//------ main.js ------
import { square, diagonal } from 'library';
console.log(square(13)); // 169
console.log(diagonal(12, 5)); // 13
const app = document.getElementById("app");
app.innerHTML = "<h1>Demo App for ES Modules</h1>";
const input = document.getElementById("num");
input.addEventListener("change",displaySquare);
function displaySquare(){
var sqrOutput = document.getElementById("sqr");
sqrOutput.value = square(input.value);
}
HTML
<HTML>
<head>
<title>ES Modules Demo</title>
</head>
<body>
<script type="module" src="./main.js" ></script>
<div id="app"></div>
<label>Input</label>
<input id="num" type="number" placeholder="Enter number here"/>
<br>
<label>Output</label>
<input id="sqr" type="number" disabled style="margin- top:20px"/>
</body>
</HTML>
  如上图所示,在HTML文件中,你需要在脚本标签中指定type="模块",浏览器才会将其视为ECMAScript模块。
  兼容性
  为了向后兼容,你可以在脚本标签中包含nomodule(其中加载的JS文件是单个打包文件)。支持ES模块的浏览器会知道忽略这一点。这个解决方案即使在最老的浏览器中也能使用。Willem的回答已经很好的解释了这个问题。
  在上面的方案中,我们会在HTML中加入这样的内容。
<script type="module" src="./main.js" >
</script>
<script nomodule src="./fallback.js" >
</script>
  如果你是在本地测试,你将需要在服务器上运行这个,因为你会遇到CORS问题。请在这里阅读更多信息。模块以绝对或相对引用导入,必须以"/"、"./"或"./"开头。
  注意:
  动态导入
  最新的ES2020版本确实带有动态导入功能。要动态导入模块,导入关键字可以作为函数调用。当以这种方式使用时,它会返回一个promise。
import('/modules/library.js')
.then((module) => {
// Do something with the module.
});
//or using await
let module = await import('/modules/library.js');
  关于es模块的详细兼容性可以参考这里 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules#import以及https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules#export
  您是否应该选择使用ES模块?
  对于浏览器来说,ES模块是新的标准。可以开箱即用的异步模块加载功能,你可以获得更快的启动时间以更好的性能。虽然您可以在浏览器中使用CommonJS与一些额外的插件,但强烈建议您切换到ES模块,因为它们是浏览器中的原生模块。
  ES原生模块允许您获得单个模块的加载,而不是单个打包文件。这是相当有用的,它减少了加载数据的大小。浏览器对原生模块的兼容性也很重要,因为它决定了原生的ES模块是否会被实现,或者我们是否会回退到我们的模式,它将加载一个单一的文件。当你得到一个单一的bundle文件时,其中一个问题是,当你的应用程序变得更大时,bundle js文件的大小也会增加,从而影响启动时间和性能。你可以通过使用代码拆分来避免这个问题,这是现代打包器(如webpack)中的一个功能。但在某些情况下,我们可能会选择模块打包器,如webpack而不是ES模块。如果你有CSS、图像、字体等资产,甚至是XML、CSV等数据文件,你可能会选择webpack解决方案,因为webpack提供了文件打包功能。
  你还应该考虑到浏览器对HTTP2的支持。当你使用本地模块时,你的浏览器会单独加载这些模块。但在HTTP2的帮助下,我们可以用一个连接同时服务多个请求,而不是发送多个HTTP请求。根据CanIUse的数据,96.49%的浏览器使用HTTP2。
  但是当你开发一个应用程序时,即使是剩下的3.51%也应该满足,那么你可能会想改用webpack。这是因为如果你坚持使用原生的ES模块,你的应用程序将需要发送几个HTTP请求来加载每个单独的模块。
  在Node中,情况就完全不同了。由于该功能仍被标记为实验性的,所以你最好坚持使用CommonJS。不过你还是可以尝试一下ES模块。你可以在这里查看上面例子的源代码。你也可以在这里查看实时演示。我希望你明白什么是ES模块,以及为什么需要它们。

本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理。
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号