JavaScript模块化-CommonJS规范和ESM规范
1 ES6模块化
1.1 ES6基本介绍
ES6 模块是 ECMAScript 2015(ES6)引入的标准模块系统,广泛应用于浏览器环境下的前端开发。Node.js环境主要使用CommonJS规范。ESM使用import和export来实现模块化开发从而解决了以下问题:
- 全局作用域下的变量命名冲突问题,模块化变量不会暴露在全局。
- 解决了依赖管理混乱的问题。
- 模块化提高了代码的可读性和维护性
1.2 导出方法
首先创建Index.html文件,再分别创建user.js和article.js以及index.js,index.js将导入user和article内部的变量和方法,user和articel分别导出需要暴露的函数和变量,在index.html中引入index.js。
user.js文件代码如下所示:
const name = "User 1"const getData = () => {const res = "Data from User 1"return res
}const getAge = () => {return 30
}
article.js初始代码如下所示:
const name = "Article 1"const getData = () => {const res = "Data from Article 1"return res
}const getColunmn = () => {return 3
}
index.html如下所示:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body></body>
</html>
1.2.1 分别导出
在需要导出的变量前加上export即可导出该变量,user.js如下所示:
export const name = "User 1"export const getData = () => {const res = "Data from User 1"return res
}
1.2.2 按需命名导出
将需要导出的变量放入一个对象中,统一使用export {}导出,article文件代码如下所示。
export {name, getData}
1.2.3 默认导出
使用export default 变量 实现默认导出,注意,一个模块只能有一个默认导出,默认导出的变量将会视为一个整体,在使用imprt * 导出时存放在default属性下,如下所示。
const getAge = () => {return 30
}export default getAgeexport default getAge = () => {
// return 30
// }
1.3 导入方法
在index.js进行导入前,需要将index.js引入index.html的body中,加入如下语句:
<body><script src="index.js" type="module"></script>
</body>
注意:在js文件中使用了import后需要将type="javascript/text"变为type="module",表明以内的是一个js模块,否则报错,此时模块内的变量和方程在浏览器控制台内就无法访问,隔离了变量。
1.3.1 全局导入
使用import * as 别名 from "./user.js"来引入全部导出的变量,如下所示:
import * as user from './user.js';
import * as article from './article.js';console.log(user)
console.log(article)
在控制台打印结果如下所示:
可以看到分别导出和按需命名导出变量都在该对象上,按需导出的内容存储在对象的default属性上。
1.3.2 命名导入
命名导入使用形如import {name, getData} from "./user.js"来导入,类似解构赋值,如果变量重名可以使用as 别名来解决命名冲突问题,如下所示:
import {name as username, getData as getUserdata} from './user.js';
import {name, getData} from './article.js';console.log(username)
console.log(getUserdata())
console.log(name)
console.log(getData())
可以看到控台输出如下所示:
1.3.3 默认导入
默认导入不需要解构赋值(形如 import user<-任意名称 from "./user.js"),可以以任意不冲突变量名来导入,如下所示:
import getAge from './user.js';
import anyName from './article.js';console.log(getAge()) // output: 3
console.log(anyName.getData()) // output: 30
1.3.4 混合导入
混合导入可以同时导入默认导出和命名导出的内容,如下所示:
import user, {name, getData} from './user.js';console.log(user)
console.log(name)
console.log(getData())
1.4 浏览器环境使用CommonJS规范-使用Browserify
将CommonJS模块文件转换为浏览器可识别的格式有许多方法,常用的工具有:ESbuild、Babel、Browserify等。本文将介绍Browserify,可以将 CommonJS 模块转换为浏览器可用的格式,只需要转换后在浏览器中引入即可,使用流程如下所示:
1. 安装Browserify
使用下面的命令安装Browserify:
npm install --save-dev browserify
2. 准备CommonJS规范的导入和导出文件
修改user.js文件内容如下所示:
const name = "User 1"
const getData = () => {const res = "Data from User 1"return res
}const getAge = () => {return 30
}module.exports = {name,getData,getAge
}
修改index.js文件内容如下所示:
const user = require('./user.js');
console.log(user)
3. 使用工具打包编写好的CommonJS模块文件
运行如下命令将index.js打包未可以识别的bundle.js:
npx browserify index.js -o bundle.js
4. 引入打包后的文件bundle.js到html文件当中
<script src="bundle.js" type="module"></script>
即可在控制台中查看输出。
1.5 注意事项
1.5.1 默认导出和命名导出的差异
命名导出:
- 可以在一个模块中导出多个内容。
- 导入时需要使用大括号 {} 并匹配导出的名称。
- 更适合导出多个功能或工具。
默认导出:
- 每个模块只能有一个默认导出。
- 导入时可以使用任意名称,不需要大括号。
- 适合导出一个主要的功能或对象。
1.5.2 模块的静态结构与运行时动态加载运行
在ESM中模块的依赖关系在编译时就已经决定了,模块之间的依赖关系和加载顺序是静态的,带来一下好处:
- 性能优化:构建阶段进行代码分析和优化,预加载、并行加载;
- 提前的错误检测:问题可以在编译阶段就提前发现
- 静态分析:准确的类型检查和自动补全
同时模块也可以通过事件进行动态的添加并执行(一次),在dynamic_code.js文件中添加console.log("content loaded"),然后再script中添加如下代码:
<body><button id="btn">Click</button><script>const btn = document.getElementById('btn');btn.addEventListener('click', async () => {await import('./dynamic_code.js')});</script>
</body>
同时需要注意到import返回的是一个Promise对象,需要使用then或者await来等待加载完成状态。
1.5.3 导出变量的修改变化
我们先来看一个例子,来猜一猜它的输出:
function test(){let sum = 0function increment(){sum ++}return {sum, increment}
}let {sum, increment} = test()console.log(sum) // 0
increment()
console.log(sum) // 0
解构赋值得到的只是一个拷贝,并不是原始的引用,所以increment增加的是闭包环境内的sum对象,和赋值得到的sum没有关系。但是如果是返回的是引用对象(数组和对象),那么就不同了:
function test(){const obj = {sum:0}function increment(){obj.sum ++}return {obj, increment}
}let {obj, increment} = test()console.log(obj.sum) // 0
increment()
console.log(obj.sum) // 1
像obj.sum一样在ESM中导出的变量进行修改是会修改原始模块中的变量的值,如果意外修改了模块内的变量,且模块变量被多个其它模块所共用可能造成意外的错误。如下所示是一个ESM中的修改影响原始模块变量的例子:
首先编写count.js文件,导出let声明的sum和方程increment:
let sum = 0
function increment(){sum ++
}
export {sum, increment}
在index.js中导入sum和increment函数,在网页控制台中查看输出:
import { sum, increment } from "./count";
console.log(sum); // 0
increment();
increment();
increment();
console.log(sum); // 3
可以看到输出0和3,对导出变量的修改会影响模块内的原始值,所以为了让模块内的变量不被修改,需要将导出的变量使用const 声明,如下所示。:
const sum = 0
const increment = () =>{sum ++
}
export {sum, increment}
2 CommonJS模块
2.1 CommonJS基本介绍
CommonJS 是 Node.js 最初采用的模块系统,基于 require 和 module.exports以及exports 实现动态模块加载。尽管ESM才是官方的模块化标准,但是CommonJS是现在官方认可且Node.js环境中广泛使用的模块化规范。
2.2 导出导出方法
在CommonJS中导出的内容相当于挂载在一个对象上,exports.属性名相当于在对象上添加属性,module.exports相当于修改整个导出的对象。
2.2.1 exports简化导出-require导入
分别编写index.js和user.js及运行的输出如下所示,使用exports.变量名挂载到导出对象上,require( 'filepath')获得导入对象:
// index.js
const user = require('./user');
console.log(user); // output: { name: 'User 1', age: 30, getData: [Function (anonymous)] }// user.js
exports.name = "User 1"
exports.age = 30
exports.getData = () => {const res = "Data from User 1"return res
}
导入的时候可以使用{ }进行赋值的解构,如果有重名使用ES6的重命名语法,如下所示:
const {name:username} = require('./user');
console.log(username);
2.2.2 module.exports整体导出
使用module.exports整体导出对象,导入方式不变,如下所示:
// user.js
const name = "User 1"
const age = 30
const getData = () => {const res = "Data from User 1"return res
}module.exports = {username: name,age,getData
}
2.2.3 混合导出存在的问题
首先不能使用exports = {}来进行导出,如果混合使用exports.变量名和module.exports = {}来进行导出,会以最后的module.exports值进行确认,如下所示:
const name = "User 1"
const age = 30
const getData = () => {const res = "Data from User 1"return res
}exports.a = 1module.exports = {username: name,age,getData
}exports.b = 4module.exports = {username: "Usernawm",age,getData,b:32
}exports.c = 3
导出的对象如下所示:
{ username: 'Usernawm', age: 30, getData: [Function: getData], b: 32 }
2.3 实现原理
CommonJS的原理实际是将每一个模块封装在一个函数中,module、exports和require都是函数传递的参数,我们使用console.log(arguments.callee.toString())来查看,输出内容如下。
function (exports, require, module, __filename, __dirname) {
exports.name = "User 1"
exports.age = 30
exports.getData = () => {const res = "Data from User 1"return res
}
console.log(arguments.callee.toString())
}
2.4 Node.js环境中的ESM和CommonJS中的兼容问题
1. 包配置修改-type字段
通过 package.json 中的 "type" 字段也可以指定整个包的模块类型,"module" 指的就是ESM。
// package.json
{"type": "module"
}
2. 文件扩展名设置
Node.js 使用文件扩展名(.mjs 表示 ESM,.cjs 表示 CommonJS)来区分模块类型。
3. 默认导出和命名导出的处理
在 CommonJS 中,module.exports 可以是任何类型(对象、函数、类等),而 ESM 的默认导出是一个单一的值。 当从 CommonJS 模块导入到 ESM 时,整个 module.exports 对象被视为默认导出。
// CommonJS 模块
module.exports = {add: (a, b) => a + b
};// ESM 导入
import math from './math.js';
console.log(math.add(2, 3)); // 正常工作
反之,从 ESM 导入到 CommonJS 时,需要通过 default 属性访问默认导出。
// ESM 模块
export default function log(message) {console.log(message);
}// CommonJS 导入
const log = require('./logger.mjs').default;
log('Hello, Mixed Modules!');
相关文章:
JavaScript模块化-CommonJS规范和ESM规范
1 ES6模块化 1.1 ES6基本介绍 ES6 模块是 ECMAScript 2015(ES6)引入的标准模块系统,广泛应用于浏览器环境下的前端开发。Node.js环境主要使用CommonJS规范。ESM使用import和export来实现模块化开发从而解决了以下问题: 全局作用…...
解决银河麒麟V10中的apt Lock异常
解决银河麒麟V10中的apt Lock异常 一、查找并杀掉apt进程二、删除锁文件三、重新尝试apt命令 💖The Begin💖点点关注,收藏不迷路💖 在使用银河麒麟V10的apt命令时,如果遇到lock异常,可以按以下步骤解决&…...
windows11环境安装lua及luarocks(踩坑篇)
一、lua安装及下载 官方地址: Lua Binaries Download 从这里就有坑了,下载后先解压win64_bin.zip,之后解压lib,用lib中的文件替换win64的,并把include文件夹复制过去,之后复制并重命名lua54,方…...
Glide基本用法及With方法源码解析
文章目录 引入优点 使用步骤导入依赖权限使用 其他用法占位符错误图片后备回调符圆角过渡动画大小调整gif缩略图 使用RequestOptions缓存机制设置缓存策略清理缓存 使用集成库OkHttpVolley with源码解析getRetrieverGlide.getinitializeGlide getRequestManagerRetriever Reque…...
html中的文本标签(含标签的实现案例)
目录 1.标题标签 2.标题标签的align属性 3.段落标签 4.水平线标签hr 5.换行标签br 6.文本样式标签font 编辑7.文本格式化标签 8.文本语义标签 1)时间time标签 2)文本高亮Mark标签 3)cite标签 9.特殊字符标签 10.图像标签img 附录ÿ…...
通信协议感悟
本文结合个人所学,简要讲述SPI,I2C,UART通信的特点,限制。 1.同步通信 UART,SPI,I2C三种串行通讯方式,SPI功能引脚为CS,CLK,MOSI,MISO;I2C功能引…...
IDEA几大常用AI插件
文章目录 前言列表GPT中文版TalkXBito AIIDEA自带的AI 前言 最近AI、GPT特别火,IDEA里面又有一堆插件支持GPT,所以做个专题比较一下各个GPT插件 列表 先看idea的plugins里支持哪些,搜索“GPT”之后得到的,我用下来感觉第一第二和…...
51单片机学习第六课---B站UP主江协科技
DS18B20 1、基本知识讲解 2、DS18B20读取温度值 main.c #include<regx52.h> #include"delay.h" #include"LCD1602.h" #include"key.h" #include"DS18B20.h"float T; void main () {LCD_Init();LCD_ShowString(1,1,"temp…...
sadTalker本地编译
SadTalker一款开源的可生成逼真的人像动画的工具。它利用深度学习技术,根据输入的图像和音频,生成具有生动表情和动作的视频。用户可以通过上传照片或使用预设的模型,轻松创建个性化的动画内容. 以上是官网的图, 下边是本地部署生成的,效果差…...
强化学习核心概念与公式总结
强化学习核心概念与公式总结 1. 核心概念 1.1 智能体(Agent)和环境(Environment) 智能体:学习和做决策的实体环境:智能体交互的外部系统1.2 状态(State) 描述环境在特定时刻的情况1.3 动作(Action) 智能体可以执行的操作1.4 奖励(Reward) 环境对智能体动作的即时反馈1.5 策…...
基础算法--双指针【概念+图解+题解+解释】
更多精彩内容..... 🎉❤️播主の主页✨😘 Stark、-CSDN博客 本文所在专栏: 数据结构与算法_Stark、的博客-CSDN博客 其它专栏: 学习专栏C语言_Stark、的博客-CSDN博客 项目实战C系列_Stark、的博客-CSDN博客 座右铭&a…...
国产化系统/鸿蒙开发足浴店收银源码-收缩左侧———未来之窗行业应用跨平台架构
一、左侧展开后 二、代码 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head><title></title><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"><style t…...
如何从硬盘恢复丢失/删除的视频
您是否想知道是否可以恢复已删除的视频? 幸运的是,您可以使用奇客数据恢复从硬盘驱动器、SD 卡和 USB 闪存驱动器恢复已删除的视频文件。 你有没有遇到过这样的情况:当你随机删除文件以释放空间时,你不小心按下了一些重要视频的…...
《Effective C++》第三版——设计与声明(1)
参考资料: 《Effective C》第三版 注意:《Effective C》不涉及任何 C11 的内容,因此其中的部分准则可能在 C11 出现后有更好的实现方式。 条款 18:让接口容易被正确使用,不易被误用 好的接口很容易被正确使用&…...
数值计算的程序设计问题举例
### 数值计算的程序设计问题 #### 1. 结构静力分析计算 **涉及领域**:工程力学、建筑工程 **主要问题**:线性代数方程组(Linear Algebraic Equations) **解释说明**: 在结构静力分析中,我们需要解决复杂的…...
Java之方法的使用
修饰符 返回值 方法名称(形式参数){ } 当无参数的时候形式参数中什么都不写。 列如求两个数相加 修饰符可有可无。 方法重载: 1.方法名相同 2.参数列表不同 3。返回值不影响重载...
sudo 命令:掌握系统权限控制,实现安全高效管理
一、命令简介 sudo 命令允许系统管理员授权普通用户执行特定命令,并以管理员身份运行这些命令,通常需要输入用户自己的密码。 sudo 全称是"substitute user do",意为“替用户做”,也就是“以另一个用户的身…...
AndroidStudio导入so文件
点击app 右键依次选择New-Floder-JNI Floder 创建jni目录 将需要的so文件拷贝到jni目录 在app目录下,build.gradle文件的android{}中添加: sourceSets {main{jniLibs.srcDirs [src/main/jni]}}点击一下Sync Project with Gradle Files 然后编译生成AP…...
Kuebernetes 群集基于 Docker 部署
Kuebernetes 群集基于 Docker 部署 实验报告资源列表基础环境一、准备 Docker1、安装 Docker 二、安装 Kubeadm 工具1、配置 yum 源2、安装 Kubeadm 工具 三、初始化 Master 节点1、配置 Master 节点2、常见故障 四、Node 节点加入集群五、部署网络插件(CNI…...
追随 HarmonyOS NEXT,Solon v3.0 将在10月8日发布
Solon (开放原子开源基金会,孵化项目)原计划10月1日发布 v3.0 正式版。看到 HarmonyOS NEXT 将在 10月8日启用公测,现改为10月8日发布以示庆贺。另外,Solon 将在2025年启动“仓颉”版开发(届时,…...
服装时尚与动漫游戏的跨界联动:创新运营与策划策略研究
摘要:本论文聚焦于服装时尚与动漫游戏的跨界联动现象,深入探讨其在运营和策划方向的策略与实践。通过对相关理论的梳理和实际案例的分析,阐述了跨界联动的背景、意义、模式以及面临的挑战。研究发现,成功的跨界联动能够实现品牌价…...
Redis中String类型的常用命令(append,getrenge,setrange等命令)
Redis----String命令 前言.常见的String存储类型. 常见命令1. set 命令2. get 命令3. mget命令与mset命令4. setnx命令5. setex与psetex命令6. incr与incrby与incrbyfloat命令7. decr与decrby命令8. append命令9. getrange和setrange命令10. strlen命令. 前言. 常见的String存…...
深度拆解:如何在Facebook上做跨境电商?
国内社交媒体正在逐渐兴盛,海外也不例外。在数字营销的新时代,Facebook已成为跨境电商不可或缺的平台之一。通过Facebook的巨大流量,卖家可以更好的触及潜在消费者,以实现销售增长。本文就深度拆解一下,卖家如何利用Fb…...
为啥数据需转换成tensor才能参与后续建模训练
将数据转换为Tensor(张量)格式用于深度学习和机器学习模型训练,主要是出于以下几个关键原因: 数值计算的效率:Tensor(由PyTorch、TensorFlow等库提供)是在GPU上执行高效的数值运算的数据结构。相…...
leetcode:380. O(1) 时间插入、删除和获取随机元素
实现RandomizedSet 类: RandomizedSet() 初始化 RandomizedSet 对象bool insert(int val) 当元素 val 不存在时,向集合中插入该项,并返回 true ;否则,返回 false 。bool remove(int val) 当元素 val 存在时࿰…...
Linux集群部署RabbitMQ
目录 一、准备三台虚拟机,配置相同 1、所有主机都需要hosts文件解析 2、所有主机安装erLang和rabbitmq 3、修改配置文件 4、导入rabbitmq 的管理界面 5、查看节点状态 6、设置erlang运行节点 7、rabitmq2和rabbitmq3重启服务 8、查看各个节点状态 二、添加…...
01DSP学习-了解DSP外设-以逆变器控制为例
(由于是回忆自己简单的DSP学习过程,所以博客看起来有些没有章法,请见谅~) 上一篇博客介绍了学习DSP需要的软件和硬件准备,以及一个DSP的工程包含了哪些东西。我的学习方法是目的导向,即我需要用什么我就学什么,并没有…...
【ArcGIS Pro实操第三期】多模式道路网构建(Multi-model road network construction)原理及实操案例
ArcGIS Pro实操第三期:多模式道路网构建原理及实操案例 1 概述1.1 原理 2 GIS实操2.1 新建文件并导入数据2.2 创建网络数据集2.3 设置连接策略(Setting up connectivity policies)2.4 添加成本(Adding cost attributes)…...
深度学习基础及技巧
机器学习中的监督学习 监督学习是通过对数据进行分析,找到数据的表达模型,对新输入的数据套用该模型做决策 主要分为训练和预测两个阶段 训练阶段:根据原始数据进行特征提取,然后使用决策树、随机森林等模型算法分析数据之间的特…...
Unity 外描边简单实现(Shader Graph)
1:原理 将物体的模型空间的位置(也就是顶点数据)放大,作为一个单独的渲染通道单独渲染,这时候模型是已经发大过的,要想看到外描边的效果,需要将正面显示的东西给去掉,显示背面渲染的…...
备案网站可以做论坛么/武汉seo和网络推广
Activity是存放在栈中。在默认的情况下(standard)Activity在栈中是以先进后出、后进先出的方式进行存放。最开始出现的Activity会存在栈底,最新启动的Activity默认是会存在栈顶。 当我们需要退出应用程序,则要销毁所有的Activity…...
wordpress 插件主题冲突/湖南专业seo公司
2. RBAC实践 创建一个名为deployment-clusterrole的clusterrole a) 该clusterrole只允许创建Deployment、Daemonset、Statefulset的create操作 在名字为app-team1的namespace下创建一个名为cicd-token的serviceAccount,并且将上一步创建clusterrole的权限绑定到该serviceAcc…...
mip手机网站模板/新闻发稿平台有哪些?
前段时间,进行USB拷贝方面的测试,积累了几个测试脚本,也可以进行磁盘间的拷贝,放在这里备份。 主要实现功能: 从一个存储设备拷贝数据到另一个位置,并比较两次拷贝的数据是否一致,并判断系统是…...
淮北网站建设/线上渠道推广有哪些方式
jQuery.post(url, [data], [callback], [type]) 通过远程 HTTP POST 请求载入信息。这是一个简单的 POST 请求功能以取代复杂 $.ajax 。请求成功时可调用回调函数。如果需要在出错时执行函数,请使用 $.ajax。参数 :urlString发送请求地址。data (可选)Map待发送 Key…...
备案上个人网站和企业网站的区别/学seo哪个培训好
OA实施方法论团队建设项目经理的人选要求是具备一定软件技术的、更强调非技术方面的顾问咨询、项目管理的综合素质很强的人才。有效控制项目质量、项目进度、带领团队成功完成项目实施,是九思软件项目经理的职责,而不是合同签订下来,派个会…...
网站开发算是固定资产吗/今日广州新闻头条
Camtasia是一款专业的屏幕录制和软件,用户可以通过它来录制自己的电脑屏幕,包括实时动画、PPT播放,兼以音频录制、视频制作等功能,支持用户一站式完成屏幕录制和后期处理操作。 自软件发行以来,Camtasia每个版本都在不…...