温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

如何理解JavaScript中的ES模块

发布时间:2021-09-30 15:51:19 来源:亿速云 阅读:105 作者:柒染 栏目:web开发

如何理解JavaScript中的ES模块,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

模块在每个编程语言中都能找到。它是一种能在一个代码块中引入另一个代码的模块功能的方法。这些模块是开发人员开发的具有特定功能的代码,可以在项目其他地方重复使用。模块化为你提供了一些好处,比如代码的可复用性和模块化。

如果你之前一直在使用JavaScript开发代码,你就会知道早期的JavaScript没有这样的模块功能。开发者为了将js文件加载到他们的页面中,不得不使用HTML <script>标签。直到后来,几种模块定义规范才开始出现。

  • CommonJS &mdash; 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 = "<h2>Demo App for ES Modules</h2>"; 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模块。你可以在这里查看上面例子的源代码。你也可以在这里查看实时演示。

关于如何理解JavaScript中的ES模块问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注亿速云行业资讯频道了解更多相关知识。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI