JavaScript模块化-CommonJS规范和ESM规范

news2024/11/20 14:43:58

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 getAge

export 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 = 0
    function 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 = 1

module.exports = {
    username: name,
    age,
    getData
}

exports.b = 4

module.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!');

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

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

相关文章

《安富莱嵌入式周报》第343期:雷电USB4开源示波器正式发布,卓越的模拟前端低噪便携示波器,自带100W电源的便携智能烙铁,NASA航空航天锂电池设计

周报汇总地址&#xff1a;嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 更新一期视频教程 【授人以渔】CMSIS-RTOS V2封装层专题视频&#xff0c;一期视频将常用配置和用法梳理清楚&#xff0…

Win10系统插入带有麦克风的耳机_麦克风不起作用_解决方法_亲测成功---Windows运维工作笔记054

今天我在使用讯飞输入法的时候,想通过讯飞的语音输入法来提高自己的输入效率。 但是这个时候发现一个问题就是我插入我的台式机的是一个带有麦克风的耳机。 但是发现我这个耳机没有办法被电脑识别出麦克风来,所以说就没办法使用讯飞输入法的语音输入功能来直接输入文字了。…

Qt 窗口中鼠标点击事件的坐标探讨

// 鼠标点击事件 void Widget::mousePressEvent(QMouseEvent *event) {/*event->pos()、event->windowPos()和event->localPos()都表示鼠标点击位置在窗口中的位置&#xff0c;它们的值都是一样的&#xff0c;区别在于event->pos()是QPoint类型&#xff0c;event-&…

操作系统-磁盘管理

存储管理中的磁盘管理涉及到几个核心概念&#xff1a;磁道、扇区、磁头、盘面。 磁道&#xff1a;磁盘表面的同心圆&#xff0c;用于记录数据。每个磁道可以存储相同量的信息。 扇区&#xff1a;磁道被进一步划分的更小单元&#xff0c;通常是磁道的最小存储单位。一个常见的扇…

【新闻转载】Storm-0501:勒索软件攻击扩展到混合云环境

icrosoft发出警告&#xff0c;勒索软件团伙Storm-0501近期调整了攻击策略&#xff0c;目前正将目标瞄准混合云环境&#xff0c;旨在全面破坏受害者的资产。 该威胁行为者自2021年首次露面&#xff0c;起初作为Sabbath勒索软件行动的分支。随后&#xff0c;他们开始分发来自Hive…

C++发邮件:如何轻松实现邮件自动化发送?

C发邮件的详细步骤与教程指南&#xff1f;如何在C中发邮件&#xff1f; 无论是定期发送报告、通知客户还是管理内部沟通&#xff0c;自动化邮件系统都能显著提升工作效率。AokSend将详细介绍如何使用C发邮件&#xff0c;实现邮件自动化发送&#xff0c;帮助您轻松管理邮件通信…

10/1 力扣 49.字母异位词分组

基本知识&#xff1a; 关于字符串的排序&#xff1a; 1.多个字符串排序 1.1使用python内置的sorted() 使用该函数后原对象并不发生变化 1.2若多个字符串使用列表进行存储&#xff0c;使用列表的sort()方法 使用该函数后原对象原地变化 2.对单个字符串里的字母进行排序 使…

关于CSS 案例_新闻内容展示

新闻要求 标题:居中加粗发布日期: 右对齐分割线: 提示, 可以使用 hr 标签正文/段落: 左侧缩进插图: 居中显示 展示效果 审核过不了&#xff0c;内容没填大家将就着看吧。 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset&qu…

【异常数据检测】孤立森林算法异常数据检测算法(数据可视化 Matlab语言)

摘要 本文研究了基于孤立森林算法的异常数据检测方法&#xff0c;并在MATLAB中实现了该算法的可视化。孤立森林是一种无监督的异常检测算法&#xff0c;主要通过构建决策树来区分正常数据和异常数据。本文使用真实数据集&#xff0c;通过二维可视化展示了检测结果。实验结果表…

肝郁气滞有什么症状

在这个快节奏、高压力的时代&#xff0c;我们的身体往往承载着超负荷的情绪与压力&#xff0c;而“肝郁气滞”这一中医术语&#xff0c;正悄然成为许多现代人健康的隐形杀手。它如同体内的“情绪交通堵塞”&#xff0c;不仅影响心情&#xff0c;更波及全身健康。今天&#xff0…

计算机毕业设计 基于Python的新闻采集与订阅平台的设计与实现 Python+Django+Vue 前后端分离 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

【C++】多态,虚函数,重载,重写,重定义,final,override,抽象类,虚函数表,动态绑定,静态绑定详解

目录 1. 多态的定义 1.1 多态的构成条件 1.2 虚函数 1.3 虚函数重写 1.4 重载&#xff0c;重写&#xff0c;重定义 1.5 final 1.6 override 2. 抽象类 3. 多态的原理 3.1 虚函数表 3.2 子类的虚函数表 3.3 多态本质 3.4 动态绑定和静态绑定 4. 多继承关系的虚…

php语法学习

MySQL问题 如果外部mysql与内部mysql冲突&#xff0c;php连接如果已经打开mysql说明他启动的是外部的mysql8&#xff0c;单独点击服务器启动apache就不会冲突。 打开navicat 打开浏览器测试 1.单行和多行注释 2.中文乱码问题 <?php //echo "Hello World 你好&#…

Agr_Reader 1.7.11 极简优美的RSS阅读器,无广告

Agr Reader是一款简洁、优美、符合Material You风格的RSS阅读器。它不仅提供了强大的全文解析功能&#xff0c;默认支持离线阅读&#xff0c;还具备桌面小组件、自定义样式设置等功能。此外&#xff0c;它支持接入FreshRSS、Tiny Tiny RSS等多种RSS服务&#xff0c;并提供沉浸式…

Android studio配置AVD虚拟机

目录 设置虚拟设备参数 安装HAXM 找到HAXM安装包 安装 启动虚拟设备 设置虚拟设备参数 Tools->Devices Manager->Add a new divece一个加号符号的图标->Create Virtual Device 选择尺寸参数&#xff0c;没有合适的话选择New Hardware Profile&#xff0c;调整好…

Spring1

1.Spring系统架构图 (1)核心层 Core Container:核心容器,这个模块是Spring最核心的模块,其他的都需要依赖该模块 (2)AOP层 AOP:面向切面编程,它依赖核心层容器,目的是==在不改变原有代码的前提下对其进行功能增强== Aspects:AOP是思想,Aspects是对AOP思想的具体实现 (3)数据…

深度学习项目----用LSTM模型预测股价(包含LSTM网络简介,代码数据均可下载)

前言 前几天在看论文&#xff0c;打算复现&#xff0c;论文用到了LSTM&#xff0c;故这一篇文章是小编学LSTM模型的学习笔记&#xff1b;LSTM感觉很复杂&#xff0c;但是结合代码构建神经网络&#xff0c;又感觉还行&#xff1b;本次学习的案例数据来源于GitHub&#xff0c;在…

Stm32的bootloader无法使用问题

Stm32的bootloader无法使用问题 用不了一键下载电路 首先简单地对此处涉及的内容进行介绍:如果stm32的BOOT0引脚为低电平时,系统从FLASH中启动,而如果BOOT0引脚为高电平,且BOOT1为低电平时,系统从自举程序(bootloader)中启动. 我在自制照相机设计中加入了ISP一键下载电路,如…

reverse--->恶意代码分析(第一次接触)。

学习笔记。 前言&#xff1a;第一次接触&#xff0c;朋友发给我的。 取自&#xff1a;22年信息安全管理与评估二阶段。 要求&#xff1a; 下载 查壳 32ida打开。 先上微步云沙箱看看&#xff1a; 样本报告-微步在线云沙箱 (threatbook.com)https://s.threatbook.com/repor…

【经典机器学习算法】谱聚类算法及其实现(python)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;深度学习_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前言 2. 前…