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

一、前言

传统方式下,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.hqwc.cn/news/13652.html

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

相关文章

15年前的手机并没有jvm虚拟机,为何可以运行Java游戏

2000年代初期&#xff0c;随着移动通信技术的发展&#xff0c;手机逐渐普及。那个时代的手机功能相对比较单一&#xff0c;主要用于打电话和发送短信。但是&#xff0c;随着技术的进步&#xff0c;人们开始在手机上玩游戏&#xff0c;而其中最受欢迎的游戏就是Java游戏。在那个…

MoblieNetV1、V2、V3、ViT四种Moblie模型的分析对比

1、MoblieNetV1 2017年提出&#xff0c;论文地址为&#xff1a;https://arxiv.org/pdf/1704.04861.pdf 1.1 相关知识 提到了标准卷积、深度可分卷积、点卷积&#xff0c;并分析了不同卷积结构的计算量&#xff0c;&#xff08;假设 D k D_k Dk​为ksize&#xff0c;M为卷积的…

软考:中级软件设计师:存储管理,分区存储,页式存储,逻辑地址,物理地址

软考&#xff1a;中级软件设计师:存储管理&#xff0c;分区存储 提示&#xff1a;系列被面试官问的问题&#xff0c;我自己当时不会&#xff0c;所以下来自己复盘一下&#xff0c;认真学习和总结&#xff0c;以应对未来更多的可能性 关于互联网大厂的笔试面试&#xff0c;都是…

Apollo、RocketMQ加载顺序问题

在SpringCloudAlibaba框架中&#xff0c;因Nacos配置中心管理权限过于简单&#xff0c;决定用Apollo代替Nacos配置中心&#xff0c;但在启动时&#xff0c;Nacos、Redis等配置读取正常&#xff0c;RocketMQ由于启动过早&#xff0c;无法从Apollo读取自己的服务地址配置。 报错…

7.7~7.8学习总结

StringBuider&#xff1a;线程不安全&#xff0c;效率高 StringBuffer&#xff1a;线程安全&#xff0c;效率低&#xff1b; 用法举例&#xff1a; class TWC {public static void main(String []args){StringBuilder sbnew StringBuilder("小麻子爱吃粑粑");Syst…

redis的主从复制,哨兵和cluster集群

一、redis的高可用 在web服务器中&#xff0c;高可用是指服务器可以正常访问的时间&#xff0c;衡量的标准是在多长时间内可以提供正常服务(99.9%、99.99%、99.999%等等)。 高可用的计算公式是1-&#xff08;宕机时间&#xff09;/&#xff08;宕机时间运行时间&#xff09;有…

常见排序算法—面试编程题2023

常见排序算法—面试编程题2023 最近在看一些面试题&#xff0c;发现很多面试过程中都会要求手写排序编程题&#xff0c;经过一番查找整理&#xff0c;可以快速学习和使用相关排序算法题&#xff0c;通俗易懂&#xff0c;手撕代码吊打面试官。 一、冒泡排序 冒泡排序 是一种简…

C语言实现三子棋

三子棋 1. 三子棋玩法2. 程序设计思路2.1 准备工作2.2 主函数设计2.3 创建菜单界面2.4 设计棋盘2.4.1 初始化棋盘2.4.2 打印棋盘 2.5 下棋2.5.1 玩家下棋2.5.2 电脑下棋 2.6 判断输赢2.8 头文件函数声明 结束语 1. 三子棋玩法 三子棋的玩法很简单&#xff0c;两个人依次在9宫格…

【ElasticSearch】ES案例:旅游酒店搜索

文章目录 一、项目分析二、需求1&#xff1a;酒店搜索功能三、需求2&#xff1a;添加过滤功能四、需求3&#xff1a;我附近的酒店五、需求4&#xff1a;置顶花广告费的酒店 一、项目分析 启动hotel-demo项目&#xff0c;访问localhost:servicePort&#xff0c;即可访问static下…

【CANopen】周立功轻松入门CANopen笔记

前言 想学习些新东西了&#xff0c;原本想直接学学Ethercat&#xff0c;但是简单看了看对象字典啥的概念一头雾水的&#xff0c;决定先从CANopen开始&#xff0c;Ethercat看着头疼。Etehrcat和CANopen有挺多类似的地方。感谢ZLG的这个入门笔记&#xff0c;我似乎是看懂了些&am…

ITIL 4服务连续性管理实践

一、目的和描述 关键信息 服务连续性管理实践的目的是确保灾难发生时&#xff0c;服务的可用性和性能能够保持在足够的水平。本实践提供了一个框架机制&#xff0c;利用产生有效响应的能力来构建组织的弹性&#xff0c;以保障关键利益相关者的利益&#xff0c;还有组织的声誉…

数据库视图与索引经典题

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 视图与索引视图&#xff1a;定义视图创建视图删除视图查询视图视图的作用 索引索引的概念索引的类型设计索引 视图与索引 视图&#xff1a; 视图是从一个或几个基…