JS Node 模块化解释:AMD、UMD、CommonJS、 ESM

news2024/9/23 1:36:58

一、前言

传统方式下,JS 若想引入其它 JS 文件时,通常使用 <script> 语法来完成,然而引入的 JS 往往易于造成命名污染,为了解决这问题,模块化 开发的概念逐渐浮现。

本文将以完整的 Demo 将各大模块模块的概念和写法进行阐述与演示,希望对你有帮助。

二、AMD

2.1 介绍

AMD 就是为了解决命名污染而研发的,同时还支持按需加载,是第一个引入 模块化 开发的规范插件,要想使用 AMD 语法得借助一款插件 RequireJS

注意,AMD 只适用于浏览器,虽然也支持 Node,但不如 Node 自家的 CJS,后面会讲。

2.2 使用

  1. 目录结构
    在这里插入图片描述

  2. 引入 requirejs.js 插件

// 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>
  <script data-main="app.js" src="https://requirejs.org/docs/release/2.3.6/r.js"></script>
</head>
<body>
  <div id="app">
    Hello,world
  </div>
</body>
</html>

参数解释:data-main 代表 JS 入口文件,当 src 加载完插件后,会立刻执行 app.js

  1. app.js 入口文件内进行一些 AMD 配置
// app.js
requirejs.config({
  baseUrl: 'modules/', // 引入模块的基路径。
});
  1. 使用 define 定义模块
// modules/tools.js
define('tools', function() {
  const getDeviceType = function() {
    return 'Android'
  }
  return {
    getDeviceType: getDeviceType,
  }
})
  1. 使用 require 加载模块
// app.js
requirejs.config({
  baseUrl: 'modules/',
});
// 根据上述配置的 baseUrl + 'tools' 去加载这个模块。 
require(['tools'], function(tools) {
  console.log('tools', tools)
})

效果:
在这里插入图片描述

小结:define && require = AMD,更多高级 API 和配置可参考官方。

三、CommonJS

3.1 介绍

CommonJS 也常被称为 CJS,与 ADMI 一样属于模块化语法,不过它是用来兼容后端 Nodejs 语言,庆幸的是,CJS 在 Node.js 中已内置,开箱即用,无需引入插件。

3.2 使用

  1. 案例结构
    在这里插入图片描述

  2. 使用 exports.module 定义模块

// modules/tools.js
const getDeviceType = () => {
  return 'Android'
}

exports.module = {
  getDeviceType,
}
  1. 使用 require 加载模块
const tools = require('./modules/tools')
const app = () => {
  console.log('tools', tools)
}
app();
  1. 执行 node
    在这里插入图片描述

小结:exports.module && require = CJS

提示:文件后缀也可以是 .cjs,Node 会自动识别。

四、UMD

4.1 介绍

UMDAMD + CommonJS 的结合体,同时还兼容了 script 标签引入,对组件库或框架库来说,解决了以前一套代码无法多端使用的难题。UMD 模块可借助 Rollup 工具来完成。

4.2 使用

  1. 安装 rollup
npm i rollup -g
  1. 案例结构
    在这里插入图片描述
    modules/tools.js 模块代码如下:

    const getDeviceType = () => {
      return 'Android'
    }
    export { getDeviceType }
    

    注意:代码采用的是 ESM 写法,后面会讲到。

  2. 通过 rollup 将文件打包成 UMD 产物

    rollup ./modules/tools.js  --file ./modules/tools_umd.js  --format umd --name=tools
    

    解释:

    • --file 表示自定义输出产物的目录和文件名。
    • --format 表示文件的转换格式,这里我们转成 umd 即可。打包后的 tools_umd.js 将放在 modules/ 目录下,产物内容如下:
    • --name 表示兼容 script 标签,将数据挂载到指定的全局对象变量中,后续通过 window[name] 来获取模块。

构建完的产物如下:

(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  typeof define === 'function' && define.amd ? define(['exports'], factory) :
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.tools = {}));
})(this, (function (exports) { 'use strict';

  const getDeviceType = () => {
    return 'Android'
  };
  exports.getDeviceType = getDeviceType;
}));
  1. 在浏览器引入 UMD 产物
  • 首先在 index.html 中引入 AMD 插件 requirejs,并执行 app-web.js ,如下:
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script data-main="app-web.js" src="https://requirejs.org/docs/release/2.3.6/r.js"></script>
</head>

<body>
  <div id="app">
    Hello,world
  </div>
</body>
</html>
  • app-web.js 中引入加载刚刚打包好的 UMD 模块:
requirejs.config({
  baseUrl: 'modules/',
});
require(['tools_umd'], function (tools) {
  console.log('tools', tools)
})

效果:

在这里插入图片描述

  1. 在浏览器通过 script 标签引入 UMD 产物
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <!-- <script data-main="app-web.js" src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"></script> -->
  <script src="./modules/tools_umd.js"></script>
</head>

<body>
  <div id="app">
    Hello,world
  </div>
</body>
</html>

效果:
在这里插入图片描述

  1. 在 Node 引入 UMD 产物
const tools = require('./modules/tools_umd.js')
const app = () => {
  console.log('Tools are: ', tools)
}
app()

效果:
在这里插入图片描述

  1. 在浏览器中通过 ESM 引入 UMD 产物:

由于 UMD 仅兼容 AMD/CJS ,可使用 rollup 将 format 设置 esm 版本即可:

rollup ./modules/tools.js --file ./modules/tools_esm.js --format esm

本质上和原先的 tools.js 代码没区别:

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <!-- <script data-main="app-web.js" src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"></script> -->
  <!-- <script src="./modules/tools_umd.js"></script> -->
  <script type="module" src="app.js"></script>
</head>

<body>
  <div id="app">
    Hello,world
  </div>
</body>

</html>
// app.js
import { getDeviceType  } from './modules/tools_esm.js';
const app = () => {
  console.log('Tool method:', getDeviceType)
}
app()

请添加图片描述

小结:

  1. UMD = AMD + CJS + Script 标签
  2. UMD 需要借助打包工具如 Webpack/Rollup
  3. 除了 Webpack/Rollup 工具,还可以结合其它工具如 Babel,可以将最新的 ES 语法转成低版本的语法,当然这是题外话,对 Babel 话题感兴趣的可参考:JS & 介绍 Babel 的使用及 presets & plugins 的概念

五、ESM

5.1 介绍

由于浏览器始终得借助于 ADMUMD 来进行模块化开发,官方实在看不下去,决定让浏览器内置一套模块化机制,俗称 ESM,目的就是减少 AMDUMD 的依赖实现统一规范,它与 CJS 一样,现代浏览器已经内置好了。

由于 ESM 技术较新,一些旧浏览器无法兼容,因此现在大多项目要借助 Webpack/Rollup 等构建工具将 ESM 打包成兼容的语法;也就说,我们可以在项目中写 ESM 代码,但在运行期间,代码会以兼容语法的方式来执行。

5.2 使用

其实在前面的 UMD 章节第 7 点我们已经使用过 ESM 语法了,除了 export 导出方式,还有其它导出的方式 export default ,它可以导出完整的对象,接下来将基于此语法作为演示。

提示:浏览器使用 ESM 的前提条件是给 script 标签加上 type="module"
另外,这里我们不再借助 Rollup 构建转换工具,现代浏览器本身就支持 ESM。

  1. 案例结构:
    请添加图片描述
  2. 原来的 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>
  <script type="module" src="app.js"></script>
</head>
<body>
  <div id="app">
    Hello,world
  </div>
</body>
</html>
  1. 改造 app.js
// app.js
import tools from './modules/tools.js';
const app = () => {
  console.log('Tools are:', tools)
}
app()
  1. 改造 modules/tools.js,使用 export default 方式导出
// modules/tools.js
const getDeviceType = () => {
  return 'Android'
}
const getDeviceVersion = () => {
  return '1.0.0'
}
const tools = {
  getDeviceType,
  getDeviceVersion
}
export default tools

效果:
在这里插入图片描述
5. exportexport default 支持混合使用

一个 JS 文件中,export 可以有无限个,但 export default 只能有一个。

// modules/tools.js
...
export { getDeviceType, getDeviceVersion }
export default tools
// app.js
import tools from './modules/tools.js';
import { getDeviceType } from './modules/tools.js';
const app = () => {
  console.log('Tools are:', tools)
  console.log('Method is:', getDeviceType)
}
app()

请添加图片描述

六、Nodejs 如何使用 ESM?

6.1 介绍

ESM 语法已经成为现代模块化标准规范,Nodejs12.0.0 版本就开始支持 ESM ,前提条件是,文件名后缀必须为 .mjs ,若想省略 .mjs ,可在 package.json 设置 type: "module" 即可;

提示:若 A.js 想引入 B.mjs ,也要在 package.json 设置 "type": "module" ,否则 node 运行时也会报错,如果是 A.mjs 引入 B.mjs 则不用配置。

6.2 使用

  1. 案例结构
    请添加图片描述

  2. 改造 tools.mjs ,代码如同上述的 ESM

const getDeviceType = () => {
  return 'Android'
}

const getDeviceVersion = () => {
  return '1.0.0'
}

const tools = {
  getDeviceType,
  getDeviceVersion
}

export { getDeviceType, getDeviceVersion }

export default tools
  1. 改造 app.js
import tools from './modules/tools.mjs'
const app = () => {
  console.log('Tools are:', tools)
}
app()
  1. node-esm 跟目录下执行 npm init -y,将会生成 package.json,里面我们只需新增 "type": "module" 即可
{
  "name": "node-esm",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "type": "module"
}

提示:我们可以把前面的 tools.mjs 换成 tools.js ,因为我们已经设定了 "type: "module" ,但为了方便演示,这里就不变更了。

  1. 运行
node app.js

效果:
请添加图片描述

注意:一个 .js 文件不可能同时存在 ESM 或者 CJS 两种语法,也就是你的项目只能选择其中一种模块化语法来开发,否则执行时会抛出错误。像前端常见的 Vite 脚手架,我们之所以能够写 ESM 语法而不用在当前项目设定 module 的原因是, vite 内部的 package.json 早已设定好 "type": "module",通过 vite 来执行项目时会自动解析我们写的ESM 代码。

----------------------------------------------------------------结尾----------------------------------------------------------------

完整 demo 已放到 github 。

参考文献:
https://requirejs.org/docs/api.html#usage
https://adostes.medium.com/authoring-a-javascript-library-that-works-everywhere-using-rollup-f1b4b527b2a9
https://blog.logrocket.com/how-to-use-ecmascript-modules-with-node-js/

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

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

相关文章

树莓派(香橙派)交叉编译

目录 1、交叉编译是什么 2、为什么要交叉编译&#xff1f; 3、交叉编译需要用到什么工具&#xff1f; 4、&#xff08;香橙派&#xff09;交叉编译工具链的安装 5、 交叉编译服务端客户端 6、 带wiringPi库的交叉编译如何进行 1、交叉编译是什么 交叉编译是在一个平台上生…

叠加态和超级定位:量子世界的奇特现象

亲爱的读者&#xff0c; 欢迎回到我们的量子力学系列文章。在前几篇文章中&#xff0c;我们介绍了量子力学的起源、基本概念以及波函数作为描述量子世界的数学工具。今天&#xff0c;我们将深入探索量子力学中的奇特现象&#xff0c;包括叠加态和超级定位。 在量子力学中&…

sklearn高级功能

包括构造数据集、正则化、交叉验证 1.构造数据集 from sklearn import datasets import matplotlib.pyplot as plt #构造数据 #用函数来建立 100 个 sample&#xff0c;有一个 feature&#xff0c;和一个 target&#xff0c;这样比较方便可视化。 X, y datasets.make_regres…

Python开发项目基于大数据的反电信诈骗管理系统设计与实现

博主介绍&#xff1a;擅长Java、微信小程序、Python、Android等&#xff0c;专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不然下次找不到哟 Java项目精品实战案例…

行为型模式-策略模式(一)

今天就说一说设计模式中的策略模式&#xff0c;从名字来讲&#xff0c;意思就是&#xff0c;对应不同的情况&#xff0c;就有一种解决问题的办法&#xff0c;不同的情况&#xff0c;就有不同的应对方法&#xff0c;这就是策略模式&#xff0c;非常的智能化。 也可以参考菜鸟 …

leetcode 876.链表的中间结点(快慢指针问题)

⭐️ 往期相关文章 &#x1f4ab; 链接1&#xff1a;leetcode 206.反转链表 &#x1f4ab; 链接2&#xff1a;leetcode 203.移除链表元素 &#x1f4ab; 链接3&#xff1a;数据结构-手撕单链表代码详解 ⭐️ 题目描述 &#x1f31f; leetcode链接&#xff1a;链表的中间结点 …

手把手教大家实现 npm 包,并发布 npm 仓库,搭建文档(一)

我就知道你会点进来看看&#xff0c;吹牛逼的&#xff0c;哈哈&#xff0c;不过呢&#xff0c;我正在朝着这个方向前进&#xff0c;希望大家给我鼓励鼓励&#xff0c;希望点击进来小伙伴点点赞&#xff0c;点点关注。 说实话&#xff0c;写这个项目的目的&#xff0c;从我自己…

ModaHub魔搭社区:阿里云CTO周靖人:魔搭社区将成为中国最大的“大模型自由市场”

7月7日消息&#xff0c;阿里云CTO周靖人在上海世界人工智能大会上宣布了阿里云的发展计划。 他表示&#xff0c;阿里云将致力于推动中国大模型生态的繁荣&#xff0c;并为大模型创业公司提供全方位的支持。阿里云魔搭社区已经聚集了180万AI开发者和900多个优质AI模型&#xff0…

springboot---定时任务实现

定时任务 1. scheduled注解实现1.1. 用法1.2. 参数详解1.2.1. cron1.2.2. zone1.2.3. fixedDelay1.2.4. fixedDelayString1.2.5. fixedRate1.2.6. fixedRateString1.2.7. initialDelay1.2.8. initialDelayString 1.3. 示例 1. scheduled注解实现 1.1. 用法 任意类中创建一个方…

ASL-QPSO|改进量子粒子群自适应算法及其实现(Matlab)

作者在前面的文章中介绍了量子粒子群算法&#xff0c;量子粒子群算法不但继承粒子群算法的优点&#xff0c;还有它自身计算模型更加简洁&#xff0c;控制参数更少等更加突出的优势&#xff0c;但依然存在着一定的局限性。 例如也会存在着早熟收敛的问题&#xff0c;随着迭代次数…

金属工件表面粗糙度测量方法概述

引言: 在加工零件等的表面上,存在着不同高低、深度和间隙凹凸等的复杂形状。其中,深度且间隔较小的表面坑洼不平被称为表面粗糙度。 产品外包装、汽车仪表盘或是触摸屏等加工零件表面的“闪闪发亮”“粗糙无光泽”等外观区别,就源自这个表面粗糙度的差异。 此外,拿在手里…

Linux分布式应用 Zabbix监控软件 概述 安装

zabbix 是什么&#xff1f; ●zabbix 是一个基于 Web 界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案。 ●zabbix 能监视各种网络参数&#xff0c;保证服务器系统的安全运营&#xff1b;并提供灵活的通知机制以让系统管理员快速定位/解决存在的各种问题。 ●…

ESP32(掌控板) 内置图片与动画显示

ESP32&#xff08;掌控板&#xff09; 内置图片与动画显示 本程序实现了对Mind的MicroPython库内置的图片和动画的显示。Mind上的MicroPython库由于在显示上采用缓存形式&#xff0c;不易出现屏幕闪烁&#xff0c;但对数字显示的支持较差。本程序通过机械按键进入图片选择界面&…

我的华为玩机技巧携手晤对科技,共探创意玩法与新可能

众所周知&#xff0c;华为作为全球领先的智能手机品牌&#xff0c;一直以来致力于提供最好的用户体验&#xff0c;而“我的华为”App&#xff0c;作为华为设备出厂自带的默认应用之一&#xff0c;为华为用户提供丰富的功能&#xff0c;涵盖HarmonyOS 升级、服务、玩机技巧、花粉…

Django学习笔记

Django学习笔记 初识Django安装Django创建Django项目APP启动Django快速上手再写一个页面templates模板静态文件 模板语法请求和相应登录案例 数据库操作安装第三方模块ORM Django官网 : https://docs.djangoproject.com/en/4.2/Django中文文档参考网站&#xff1a;https://yiyi…

java 并发 随笔7 ThreadLocal源码走读

0. 刚刚见了下老朋友&#xff0c;桌球撞起来的感觉很爽 可以看到 Thread 是内部是维护了局部变量的(thread-local-map) 1. 源码走读 很多的细节都在代码块中备注了 package java.lang;// 现在回来起来&#xff0c;很多经验不太丰富的人之所以在接触、学习java.lang.thread的…

初识定时任务

了解定时任务 我们在开发系统的时候&#xff0c;常常会遇到需要定时的去执行一些业务&#xff0c;例如&#xff1a;定时备份数据库、定时生成报告、定时发送通知、定时批处理等各种自动化操作。 那此时我们就需要通过使用定时任务来完成这些业务需求。并且在日常的开发中定时任…

【从删库到跑路】一文带你明白MySQL数据库的 事务 操作

&#x1f38a;专栏【MySQL】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【如愿】 大一同学小吉&#xff0c;欢迎并且感谢大家指出我的问题&#x1f970; 文章目录 &#x1f354;事务⭐简介⭐普通操作——不使用事务&#x1f3…

深入流行推荐引擎第一部分:推荐系统基础

深入流行推荐引擎第一部分&#xff1a;推荐系统基础 1. 推荐引擎及其类型什么是推荐引擎&#xff1f;基于内容的推荐&#xff08;Content-Based Recommendations&#xff09;协作推荐&#xff08;Collaborative Recommendations&#xff09;混合动力推荐&#xff08;Hybrid Rec…

【stability.ai】SDXL:改进高分辨率图像合成的潜在扩散模型

github&#xff1a;https://github.com/Stability-AI/stablediffusion 试用&#xff1a; https://clipdrop.co/stable-diffusion https://dreamstudio.ai/ 介绍 近年来&#xff0c;深度生成建模在自然语言、音频和视觉媒体等各个数据领域取得了巨大的突破。本文将重点关注视…