ES Module 的 import 导入和 import () 动态导入

ES Module 的 import 导入和 import () 动态导入介绍

一、ES Module 简介

ES Module 是 JavaScript 官方提供的标准化模块系统,它的出现解决了长期以来 JavaScript 在模块管理方面的混乱局面。通过 ES Module,开发者可以更加方便地组织和复用代码,提高代码的可维护性和可扩展性。

二、import 导入

(一)基本语法

import 默认导出

在一个模块中,可以使用export default来定义默认导出。例如,在myModule.js文件中:

// myModule.jsconst myFunction = () => {console.log("这是一个默认导出的函数");
};export default myFunction;

在另一个模块中导入这个默认导出:

// main.jsimport myFunction from "./myModule.js";myFunction(); // 输出:这是一个默认导出的函数

import 具名导出

模块中也可以使用具名导出,将多个变量、函数或类分别导出。例如:

// mathModule.jsexport const add = (a, b) => a + b;export const subtract = (a, b) => a - b;

在其他模块中导入具名导出:

// main.jsimport { add, subtract } from "./mathModule.js";console.log(add(2, 3)); // 输出:5console.log(subtract(5, 3)); // 输出:2

也可以给 import 的具名导出起别名:

// main.jsimport { add as sum, subtract as difference } from "./mathModule.js";console.log(sum(2, 3)); // 输出:5console.log(difference(5, 3)); // 输出:2

混合导入

一个模块可以同时有默认导出和具名导出,在导入时也可以混合使用:

// myModule.jsconst myDefaultFunction = () => {console.log("这是默认导出的函数");
};export const myVariable = 42;export default myDefaultFunction;
// main.jsimport myDefaultFunction, { myVariable } from "./myModule.js";myDefaultFunction(); // 输出:这是默认导出的函数console.log(myVariable); // 输出:42

(二)import 的特点

静态性import语句在编译阶段就会被解析,这意味着它不能出现在运行时才执行的逻辑中,比如if语句块内。这使得 JavaScript 引擎可以在代码执行前对模块依赖进行分析和优化。

提升import语句会被提升到模块的顶部,即使在代码中它出现在其他语句之后,也会先于其他语句执行。

三、import () 动态导入

(一)基本语法

import()是 ES2020 引入的动态导入语法,它返回一个Promise。这使得我们可以在运行时根据条件动态地加载模块。例如:

// main.jsconst condition = true;if (condition) {import("./myModule.js").then((module) => {module.default(); // 假设 myModule.js 有默认导出}).catch((error) => {console.error("加载模块失败", error);});
}

如果模块是具名导出,可以这样使用:

// main.jsimport("./mathModule.js").then((module) => {console.log(module.add(2, 3)); // 假设 mathModule.js 有具名导出 add 函数}).catch((error) => {console.error("加载模块失败", error);});

(二)支持导入 CommonJS

在 Node.js 环境中,import()语法还支持导入 CommonJS 模块。CommonJS 是一种广泛使用的 JavaScript 模块规范,尤其是在 Node.js 应用中。当使用import()导入 CommonJS 模块时,需要注意以下几点:

转换规则

CommonJS特性ESM转换表现
module.exports成为默认导出default属性
exports.xxx转换为具名导出属性
exports.default不会特殊处理
动态导出可能无法正确识别
const cjsModule = await import('./legacy-module.cjs');
console.log(cjsModule.default); // 默认导出
console.log(cjsModule.namedExport); // 具名导出

默认导出与命名导出:CommonJS 模块只有一个exports对象用于导出内容,通过import()导入时,默认导出的是整个exports对象。例如,有一个 CommonJS 模块commonModule.js

// commonModule.js(CommonJS模块)const myValue = 10;exports.myValue = myValue;exports.anotherFunction = () => {console.log("这是CommonJS模块中的另一个函数");
};

在 ES Module 中使用import()导入该模块:

// main.jsimport("./commonModule.js").then((module) => {console.log(module.myValue); // 输出:10module.anotherFunction(); // 输出:这是CommonJS模块中的另一个函数}).catch((error) => {console.error("加载模块失败", error);});

兼容性:虽然import()支持导入 CommonJS 模块,但在不同的运行环境中,其兼容性可能有所不同。在 Node.js 中,从 Node.js 13.2.0 版本开始原生支持通过import()导入 CommonJS 模块。在浏览器环境中,情况相对复杂,一些现代浏览器可能对导入 CommonJS 模块的支持并不完善,这时候可能需要借助工具如 Babel 和 Webpack 来进行处理,将 CommonJS 模块转换为 ES Module 格式,以确保代码在各种环境中都能正常运行。

(三)import () 的优势

代码拆分:在大型应用中,通过import()可以实现代码的按需加载,将应用的代码拆分成多个小块,只有在需要的时候才加载相应的模块,从而提高应用的初始加载性能。

条件加载:可以根据运行时的条件来决定加载哪个模块,增加了代码的灵活性。例如,根据用户的语言偏好加载不同语言的翻译模块。

(四)import() 导入如何清除缓存

在使用import()动态导入模块时,缓存机制可能会导致加载旧版本的模块内容。根据模块类型不同,清除缓存的方式也有所差异。

导入 ES Module :ES Module 的缓存机制相对严格,要确保每次都加载最新文件,可在每次调用import()时,对文件地址添加不同的查询参数,以此强制浏览器或 Node.js 重新请求该模块。例如:

const modulePath = './myModule.js';
const uniqueModulePath = `${modulePath}?v=${Date.now()}`;
import(uniqueModulePath).then((module) => {// 使用模块}).catch((error) => {console.error('加载模块失败', error);});

上述代码通过Date.now()生成一个随时间变化的唯一值作为查询参数,确保每次请求的模块路径不同,从而避免缓存。

导入 Common JS 模块:在 Node.js 环境中,CommonJS 模块的缓存管理与 ES Module 不同。若要手动清除缓存,需借助require方法。在 ES Module 中,本身没有直接的require方法,但可通过以下方式获取:

import { createRequire } from "module";
const require = createRequire(import.meta.url);

获取require方法后,就能像在 Common JS 模块中一样清除缓存。假设要动态加载并确保每次获取最新的 Common JS 模块legacyModule.cjs:

const modulePath = './legacyModule.cjs';
delete require.cache[require.resolve(modulePath)];const module = require(modulePath);
//  也可以使用import()
import(modulePath).then((module)=>{console.log(module)
})

这段代码先通过require.resolve(modulePath)获取模块在缓存中的路径,再从require.cache中删除该路径对应的缓存,确保下次require / import 时重新加载模块。

通用加载方案:若不确定要加载的模块是 ES Module 还是 Common JS 模块,可构建一个通用的加载函数,综合上述两种方式来处理缓存问题。以下是一个示例:

import path from "path";
import { pathToFileURL } from "url";
import { createRequire } from "module";
const require = createRequire(import.meta.url);function loadFile(filePath) {delete require.cache[require.resolve(filePath)];const fileUrl = pathToFileURL(path.resolve(filePath)).href;const urlWithCacheBuster = `${fileUrl}?v=${Date.now()}`;return import(urlWithCacheBuster).then((module) => {return module.default || module;}).catch((err) => {console.error(`文件加载失败 ${modulePath}:`, err);});
}export { loadFile };

此函数loadFile 首先手动清除 Common JS 模块的缓存,再通过添加查询参数避免ES Module缓存;最后通过import 加载文件。这样无论模块是何种类型,都能尽可能确保加载到最新内容。

四、import 和 import () 的区别

静态与动态import是静态导入,在编译阶段确定依赖关系;import()是动态导入,在运行时确定依赖关系。

使用场景import适用于那些在模块初始化时就需要加载的依赖;import()更适合用于代码拆分和条件加载的场景。

语法形式import是声明式语法,而import()是函数调用语法,返回一个Promise

五、总结

ES Module 的import导入和import()动态导入为开发者提供了强大的模块管理能力。import的静态特性使得代码的依赖关系更加清晰,便于优化;import()的动态特性则为代码的灵活性和性能优化提供了更多可能。在实际开发中,我们应根据具体的需求选择合适的导入方式,以构建高效、可维护的 JavaScript 应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/897221.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用Node.js从零搭建DeepSeek本地部署(Express框架、Ollama)

目录 1.安装Node.js和npm2.初始化项目3.安装Ollama4.下载DeepSeek模型5.创建Node.js服务器6.运行服务器7.Web UI对话-Chrome插件-Page Assist 1.安装Node.js和npm 首先确保我们机器上已经安装了Node.js和npm。如果未安装,可以通过以下链接下载并安装适合我们操作系…

BUUCTF——[GYCTF2020]FlaskApp1 SSTI模板注入/PIN学习

目录 一、网页功能探索 二、SSTI注入 三、方法一 四、方法二 使用PIN码 (1)服务器运行flask登录所需的用户名 (2)modename (3)flask库下app.py的绝对路径 (4)当前网络的mac地…

Java基础关键_018_集合(二)

目 录 一、泛型 ※ 1.说明 2.实例 3.擦除与补偿 4.泛型的定义 (1)类定义 (2)静态方法定义 (3)接口定义 5.通配符 (1)无限定 (2)上限 &#xff…

FPGA学习篇——Verilog学习3(关键字+注释方法+程序基本框架)

1 Verilog常用关键字 大概知道以下哪些是关键字就好,如何使用还是得在编写代码中来学习。 2 Verilog注释方法 Verilog有两种注释方式: 2.1 “ // ” 单行。 2.2 “ /* ... */ ” 可扩展多行。 3 Verilog程序基本框架 Verilog 的基本设计单元是“…

FPGA之USB通信实战:基于FX2芯片的Slave FIFO回环测试详解

FPGA之Usb数据传输 Usb 通信 你也许会有疑问,明明有这么多通信方式和数据传输(SPI、I2C、UART、以太网)为什么偏偏使用USB呢? 原因有很多,如下: 1. 高速数据传输能力 高带宽:USB接口提供了较高的数据传…

深入理解与配置 Nginx TCP 日志输出

一、背景介绍 在现代网络架构中,Nginx 作为一款高性能的 Web 服务器和反向代理服务器,广泛应用于各种场景。除了对 HTTP/HTTPS 协议的出色支持,Nginx 从 1.9.0 版本开始引入了对 TCP 和 UDP 协议的代理功能,这使得它在处理数据库…

【大模型安全】安全解决方案

【大模型安全】安全解决方案 1.技术层面2.数据层面数据收集阶段训练阶段模型推理阶段 1.技术层面 在使用大语言模型时,通常有几种选择:一种是采用封装好的大语言模型SaaS云服务;另一种是在公有云上部署自有的大语言模型,并通过权…

python中httpx库的详细使用及案例

文章目录 1. 安装 httpx2. 同步请求3. 异步请求4. 高级功能5. 错误处理6. 配置客户端7. 结合 Beautiful Soup 使用8. 示例:抓取并解析网页9. 注意事项httpx 是一个现代化的 Python HTTP 客户端库,支持同步和异步请求,功能强大且易于使用。它比 requests 更高效,支持 HTTP/2…

OpenCV计算摄影学(19)非真实感渲染(Non-Photorealistic Rendering, NPR)

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 非真实感渲染(Non-Photorealistic Rendering, NPR)是一种计算机图形学技术,旨在生成具有艺术风格或其他非现实…

微信小程序点击按钮,将图片下载到本地

前言&#xff1a; 最近在公司完成一个小程序的时候需要实现一个功能&#xff1a;点击按钮获取用户相册权限&#xff0c;将图片下载到用户本地相册&#xff0c;经过了好几次的尝试最终算是实现了。将总结的经验在这里分享给小伙伴们。 实现方式&#xff1a; //.wxml文件 <…

数据仓库为什么要分层

数据仓库分层架构是数据仓库设计中的一个重要概念&#xff0c;其主要目的是为了更好地组织和管理数据&#xff0c;提高数据仓库的可维护性、可扩展性和性能。分层架构将数据仓库划分为多个层次&#xff0c;每个层次都有其特定的职责和功能。以下是数据仓库分层的主要原因和好处…

selenium库

一、什么是selenium库&#xff1f; selenim是一个用于Web应用程序自动化测试工具&#xff0c;selenium测试直接运行在浏览器中 像真正的用户在操作一样&#xff0c;驱动浏览器执行特定的动作&#xff0c;如点击&#xff0c;下拉等操作 二、selenium在爬虫中的应用 获取动态…

python从入门到精通(二十四):python爬虫实现登录功能

这里写目录标题 requests实现登录功能selenium实现登录功能 requests实现登录功能 使用 requests 库结合会话&#xff08;Session&#xff09;来尝试登录。不过豆瓣有反爬虫机制&#xff0c;这种方式可能会受到验证码等因素的限制 import requests import re# 豆瓣登录页面 l…

十七、从0开始卷出一个新项目之瑞萨RZN2L定时器(GPT)+DMA生成PWM的运动控制

一、概述 嵌入式科普(34)通过对比看透DMA的本质 分享瑞萨RZN2L使用DMA生成PWM的运动控制的例程源码 rzn2l必要的外设资源&#xff1a; rzn2l拥有32-bit timer General PWM Timer (GPT) with 18 channels CPU、GPT最高频率400Mhz DMAC0 and DMAC1 8 channels 8 channels 还…

MR的环形缓冲区(底层)

MapReduce的大致流程&#xff1a; 1、HDFS读取数据&#xff1b; 2、按照规则进行分片&#xff0c;形成若干个spilt&#xff1b; 3、进行Map 4、打上分区标签&#xff08;patition&#xff09; 5、数据入环形缓冲区&#xff08;KVbuffer&#xff09; 6、原地排序&#xff…

解锁STM32外设:开启嵌入式开发新世界

✨✨✨这里是小韩学长yyds的BLOG(喜欢作者的点个关注吧) ✨✨✨想要了解更多内容可以访问我的主页 小韩学长yyds-CSDN博客 目录 探索 STM32 强大的外设家族 初窥门径&#xff1a;STM32 外设开发基础 开发方式与工具 外设配置基础步骤 深入剖析&#xff1a;常见外设应用实例…

大模型AI平台DeepSeek 眼中的SQL2API平台:QuickAPI、dbapi 和 Magic API 介绍与对比

目录 1 QuickAPI 介绍 2 dbapi 介绍 3 Magic API 介绍 4 简单对比 5 总结 统一数据服务平台是一种低代码的方式&#xff0c;实现一般是通过SQL能直接生成数据API&#xff0c;同时能对产生的数据API进行全生命周期的管理&#xff0c;典型的SQL2API的实现模式。 以下是针对…

人工智能之数学基础:对线性代数中逆矩阵的思考?

本文重点 逆矩阵是线性代数中的一个重要概念,它在线性方程组、矩阵方程、动态系统、密码学、经济学和金融学以及计算机图形学等领域都有广泛的应用。通过了解逆矩阵的定义、性质、计算方法和应用,我们可以更好地理解和应用线性代数知识,解决各种实际问题。 关于逆矩阵的思…

[傻瓜式教学]如何将MathType公式编辑器内嵌到WPS工具栏中

[傻瓜式教学]如何将MathType公式编辑器内嵌到WPS工具栏中 将MathType公式编辑器内嵌到WPS工具栏中 下载好所需文件 我用夸克网盘分享了「mathtype安装教程超简单易上手.zip」&#xff0c;点击链接即可保存。打开「夸克APP」 链接&#xff1a;https://pan.quark.cn/s/4726c684…

WPF框架---MvvmLight介绍

目录 1. MvvmLight 框架准备 2. MvvmLight 中的相关基类 3. MvvmLight 中的数据绑定与通知 a. 核心功能 b. 关键方法与属性 c. 完整示例 d. 高级用法 4. MvvmLight 中的命令对象 a. 命令对象的作用 b. 核心接口&#xff1a;ICommand c. MvvmLight 中的 RelayCommand…