欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 创投人物 > ES 模块的用法

ES 模块的用法

2024/10/23 7:04:11 来源:https://blog.csdn.net/qq_24956515/article/details/143142558  浏览:    关键词:ES 模块的用法

fileOf7174.png

一. 概念

通过前面对模块化的理解,你可能会发现,使用 UMD、AMD、CMD 的代码会变得难以编写和理解。于是在 2015 年,负责 ECMAScript 规范的 TC39 委员会将模块添加为 JavaScript 的内置功能,这些模块称为 ECMAScript 模块,简称 ES 模块。

模块和经典 JavaScript 脚本略有不同:

  • 模块默认启用严格模式, 比如分配给未声明的变量会报错:

<script type="module">a = 5; 
</script>
  • 模块有一个词法顶级作用域。这意味着,例如,运行 var foo = 42; 在模块内不会创建名为 foo 的全局变量,可通过浏览器中的 window.foo 访问,尽管在经典 JavaScript 脚本中会出现这种情况;

<script type="module">let person = "Alok";
</script><script type="module">alert(person);{/* Error: person is not defined */}
</script>
  • 模块中的 this 并不引用全局 this,而是 undefined。(如果需要访问全局 this,可以使用 globalThis);

<script>alert(this); {/* 全局对象 */}
</script><script type="module">alert(this); {/* undefined */}
</script>
  • 新的静态导入和导出语法仅在模块中可用,并不适用于经典脚本。

  • 顶层 await 在模块中可用,但在经典 JavaScript 脚本中不可用;

  • await 不能在模块中的任何地方用作变量名,经典脚本中的变量可以在异步函数之外命名为 await;

  • JavaScript 会提升 import 语句。因此,可以在模块中的任何位置定义它们。

CommonJS 和 AMD 都是在运行时确定依赖关系,即运行时加载,CommonJS 加载的是拷贝。而 ES 模块是在编译时就确定依赖关系,所有加载的其实都是引用,这样做的好处是可以执行静态分析和类型检查。

二. 语法

1. 导出

当导出模块代码时,需要在其前面添加 export 关键词。导出内容可以是变量、函数或类。任何未导出的代码都是模块私有的,无法在该模块之被外访问。ES 模块支持两种类型的导出:

  • 命名导出:

export const first = 'JavaScript';
export function func() {return true;
}

当然,我们也可以先定义需要导出的变量/函数,最后统一导出这些变量/函数:

const first = 'JavaScript';
const second = 'TypeScript';
function func() {return true;
}
export {first, second, func};
  • 默认导出:

function func() {return true;
}export default func;

当然,也可以直接默认导出:

export default function func() {return true;
}

默认导出可以省略变量/函数/类名,在导入时可以为其指定任意名称:

// 导出
export default function () {console.log('foo');
}
// 导入
import customName from './module';

注意:   导入默认模块时不需要大括号,导出默认的变量或方法可以有名字,但是对外是无效的。export default  在一个模块文件中只能使用一次。

可以使用 as 关键字来重命名需要暴露出的变量或方法,经过重命名后同一变量可以多次暴露出去:

const first = 'test';
export {first as second};

2. 导入

使用命名导出的模块,可以通过以下方式来导入:

import {first, second, func} from './module';

使用默认导出的模块,可以通过以下方式来引入,导入名称可以自定义,无论导出的名称是什么:

import customName from './module.js';

导入模块位置可以是相对路径也可以是绝对路径,.js扩展名是可以省略的,如果不带路径而只是模块名,则需要通过配置文件告诉引擎查找的位置:

import {firstName, lastName} from './module';

可以使用 as 关键字来将导入的变量/函数重命名:

import { fn as fn1 } from './profile';

在 ES 模块中,默认导入和命名导入是可以同时使用的,比如在 React 组件中:

import React, {usestate, useEffect} from 'react';const Comp = () => {return <React.Fragment>...</React.Fragment> 
}export default Comp;

可以使用 as 关键字来加载整个模块,用于从另一个模块中导入所有命名导出,会忽略默认导出:

import * as circle from './circle';
console.log('圆面积:' + circle.area(4));
console.log('圆周长:' + circle.circumference(14));

3. 动态导入

上面我们介绍的都是静态导入,使用静态 import 时,整个模块需要先下载并执行,然后主代码才能执行。有时我们不想预先加载模块,而是按需加载,仅在需要时才加载。这可以提高初始加载时的性能,动态 import 使这成为可能:

<script type="module">(async () => {const moduleSpecifier = './lib.mjs';const {repeat, shout} = await import(moduleSpecifier);repeat('hello');// → 'hello hello'shout('Dynamic import in action');// → 'DYNAMIC IMPORT IN ACTION!'})();
</script>

与静态导入不同,动态导入可以在常规脚本中使用。

4. 其他用法

可以使用以下方式来先导入后导出模块内容:

export { foo, bar } from './module';

上面的代码就等同于:

import { foo, bar } from './module';
export { foo, boo};

另一个与模块相关的新功能是import.meta,它是一个给 JavaScript 模块暴露特定上下文的元数据属性的对象。它包含了这个模块的信息,比如说这个模块的 URL。

默认情况下,图像是相对于 HTML 文档中的当前 URL 加载的。import.meta.url可以改为加载相对于当前模块的图像:

function loadThumbnail(relativePath) {const url = new URL(relativePath, import.meta.url);const image = new Image();image.src = url;return image;
}const thumbnail = loadThumbnail('../img/thumbnail.png');
container.append(thumbnail);

三. 在浏览器使用

目前主流浏览器都支持 ES 模块:

fileOf7174.png

如果想在浏览器中使用原生 ES 模块方案,只需要在 script 标签上添加  type="module"  属性。通过该属性,浏览器知道这个文件是以模块化的方式运行的。而对于不支持的浏览器,需要通过  nomodule  属性来指定某脚本为 fallback 方案:

<script type="module">import module1 from './module1'
</script>
<script nomodule src="fallback.js"></script>

支持  type="module"  的浏览器会忽略带有  nomodule  属性的脚本。使用  type="module"  的另一个作用就是进行 ES Next 兼容性的嗅探。因为支持 ES 模块化的浏览器,都支持 ES Promise 等特性。

由于默认情况下模块是延迟的,因此可能还希望以延迟方式加载 nomodule 脚本:

<script nomodule defer src="fallback.js"></script>

四. 在 Node.js 使用

上面提到,Node.js 使用的是 CommonJS 模块规范,它也是支持 ES 模块的。在 Node.js 13 之前,ES 模块是一项实验性技术,因此,可以通过使用  .mjs  扩展名保存模块并通过标志访问它来使用模块。

从 Node.js 13 开始,可以通过以下两种方式使用模块:

  • 使用  .mjs  扩展名保存模块;

  • 在最近的文件夹中创建一个  type="module"  的  package.json  文件。

那如何在小于等于 12 版本的 Node.js 中使用 ES 模块呢?可以在执行脚本启动时加上  --experimental-modules,不过这一用法要求相应的文件后缀名必须为  .mjs

node --experimental-modules module1.mjs
import module1 from './module1.mjs'
module1

五. 总结

模块化是一种将系统分离成独立功能部分的方法,可以将系统分割成独立的功能部分,严格定义模块接口,模块间具有透明性。通过将代码进行模块化分隔,每个文件彼此独立,开发者更容易开发和维护代码,模块之间又能够互相调用和通信,这就是现代化开发的基本模式。

模块化的贯彻执行离不开相应的约定,即规范。这是能够进行模块化工作的重中之重。实现模块化的规范有很多,比如:AMD、RequireJS、CMD、SeaJS、UMD、CommonJS、ES6 Module。除此之外,IIFE(立即执行函数)也是实现模块化的一种方案。

本文将介绍其中的六个:

  • IIFE:   立即调用函数表达式

  • AMD:   异步模块加载机制

  • CMD:   通用模块定义

  • UMD:   统一模块定义

  • CommonJS:  Node.js 采用该规范

  • ES 模块:  JavaScript 内置模块系统

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com