认识javascript中的模块化
什么是模块化?
将程序⽂件依据⼀定规则拆分成多个文件,拆分出来每个⽂件就是⼀个模块,模块中的数据都是私有的,模块之间互相隔离。如果不进行隔离,可能会造成模块间的变量定义有冲突,导致程序崩溃
为啥要使用模块化?
1.全局污染:引入不同js文件时,js当中同名的函数、变量等因引入先后顺序的问题导致相互覆盖,造成调用不清晰的问题
2.依赖混乱:引入js文件顺序不同导致页面效果混乱
模块化优点?
1.防止命名冲突
2.代码复用
3.提高代码的维护性
哪些模块化规范?
IIFE (Immediately Invoked Function Expression):这不是一个模块格式,而是一种 JavaScript 函数的执行方式。它创建一个立即执行的函数,通常用于避免全局作用域的污染。在打包工具中,IIFE 格式通常用于生成一个可以直接在浏览器中运行的自包含脚本。
CommonJS (CJS):这是 Node.js 使用的模块系统。它使用 require() 来导入模块,module.exports 或 exports 来导出。它是动态的,可以在运行时加载模块,但不支持 tree-shaking。
AMD (Asynchronous Module Definition):这种格式主要用于浏览器,支持异步加载和依赖管理。它使用 define() 函数来定义模块。
UMD (Universal Module Definition):这种格式旨在兼容 AMD 和 CommonJS,同时还能在全局变量中运行。它通常用于让库能在各种环境中运行,包括浏览器和 Node.js。
SystemJS:这是一个动态模块加载器,可以在浏览器中加载各种模块格式,包括 ES6、AMD、CommonJS 和全局脚本。
ES Modules (ESM):这是 ECMAScript 6 (ES6) 引入的官方标准格式。它支持导入和导出语句,以及静态分析和 tree-shaking。它是唯一的静态模块系统,意味着你可以在编译时确定导入和导出的内容。
历史课上一上
直接定义依赖 (1999): 由于当时 js 文件非常简单,模块化方式非常简单粗暴 —— 通过全局方法定义、引用模块。这种定义方式与现在的 commonjs 非常神似,区别是 commonjs 以文件作为模块,而这种方法可以在任何文件中定义模块,模块不与文件关联。
闭包模块化模式 (2003): 用闭包方式解决了变量污染问题,闭包内返回模块对象,只需对外暴露一个全局变量。
模版依赖定义 (2006): 这时候开始流行后端模版语法,通过后端语法聚合 js 文件,从而实现依赖加载,说实话,现在 go 语言等模版语法也很流行这种方式,写后端代码的时候不觉得,回头看看,还是挂在可维护性上。
注释依赖定义 (2006): 几乎和模版依赖定义同时出现,与 1999 年方案不同的,不仅仅是模块定义方式,而是终于以文件为单位定义模块了,通过 lazyjs 加载文件,同时读取文件注释,继续递归加载剩下的文件。
外部依赖定义 (2007): 这种定义方式在 cocos2d-js 开发中普遍使用,其核心思想是将依赖抽出单独文件定义,这种方式不利于项目管理,毕竟依赖抽到代码之外,我是不是得两头找呢?所以才有通过 webwpack 打包为一个文件的方式暴力替换为 commonjs 的方式出现。
Sandbox模式 (2009): 这种模块化方式很简单,暴力,将所有模块塞到一个 sanbox 变量中,硬伤是无法解决明明冲突问题,毕竟都塞到一个 sandbox 对象里,而 Sandbox 对象也需要定义在全局,存在被覆盖的风险。模块化需要保证全局变量尽量干净,目前为止的模块化方案都没有很好的做到这一点。
依赖注入 (2009): 就是大家熟知的 angular1.0,依赖注入的思想现在已广泛运用在 react、vue 等流行框架中。但依赖注入和解决模块化问题还差得远。
CommonJS (2009): 真正解决模块化问题,从 node 端逐渐发力到前端,前端需要使用构建工具模拟。
Amd (2009): 都是同一时期的产物,这个方案主要解决前端动态加载依赖,相比 commonJs,体积更小,按需加载。
Umd (2011): 兼容了 CommonJS 与 Amd,其核心思想是,如果在 commonjs 环境(存在 module.exports,不存在 define),将函数执行结果交给 module.exports 实现 Commonjs,否则用 Amd 环境的 define,实现 Amd。
Labeled Modules (2012): 和 Commonjs 很像了,没什么硬伤,但生不逢时,碰上 Commonjs 与 Amd,那只有被人遗忘的份了。
YModules (2013): 既然都出了 Commonjs Amd,文章还列出了此方案,一定有其独到之处。其核心思想在于使用 provide取代 return,可以控制模块结束时机,处理异步结果;拿到第二个参数 module,修改其他模块的定义(虽然很有拓展性,但用在项目里是个搅屎棍)。
ES2015 Modules (2015): 就是我们现在的模块化方案,还没有被浏览器实现,大部分项目已通过 babel 或 typescript 提前体验。
这张图看的比较清晰:
一个个拉出来溜溜
1. commonjs
cjs 是 Node 中的模块规范,导入和导出的 API 为:
- equire
- exports
在服务端被广泛使用。CJS 可以通过使用 require() 函数和 module.exports 来识别。require() 是一个可用于从另一个模块导入 symbols 到当前作用域的函数。 module.exports 是当前模块在另一个模块中引入时返回的对象。
API 天生是同步的。在源文件中按 require 的顺序瞬时加载模块。CJS 模块不能在浏览器端使用,除非它被转译器打包。像 Babel 和 Traceur 那样的转译器,如果环境原生不支持新版本的 JavaScript,转译器将它们编译成支持的 JS 版本。
2. 异步模块定义(AMD)
异步模块定义(AMD)是一种JavaScript模块的定义规范,它支持模块的异步加载,解决了传统同步加载方式下的诸多问题。AMD规范通过定义清晰的接口,让模块可以被异步加载和组织,这使得模块间的依赖关系可以明确,提高了代码的可维护性和可扩展性。
通过两个主要的函数——define和require,来完成模块的定义和加载。这种方式不仅支持模块的异步加载,还允许开发者在浏览器中有效地组织和管理模块依赖,提升了项目的模块化和代码的复用性。
define函数的基本语法
define(id?, dependencies?, factory);
参数说明:
-
id 是一个可选参数,用于指定模块的唯一标识符。通常不推荐在代码中显式指定 id ,因为模块系统会根据文件路径自动生成模块标识符。
-
dependencies 是一个数组,列出当前模块依赖的其他模块。如果当前模块依赖于其他模块,需要在这个数组中按照依赖顺序列出这些模块的标识符。
-
factory 是一个函数,返回模块的导出内容。这是一个关键参数,定义了模块的实现逻辑。工厂函数可以有三种形式:
- 无参数工厂函数:返回模块的导出对象或值。
- 单参数工厂函数:工厂函数接收一个参数,通常是一个依赖模块的数组。
- 多参数工厂函数:工厂函数可以接收多个参数,每个参数对应一个依赖模块。
define(['jquery', 'underscore'], function($, _) {return {name: 'Module A',sayHello: function() {console.log('Hello from Module A!');}};
});
requirequire 函数
用于加载一个或多个AMD模块,其基本语法如下:
require([dependencies], callback);
- dependencies 参数:一个数组,包含了所有需要加载的模块标识符。
- callback 参数:一个函数,依赖模块加载完成后执行的回调函数。回调函数的参数对应于
- dependencies 数组中的模块。
AMD 脱胎于 CJS,支持异步模块加载。AMD 和 CJS 的主要区别在于它是否支持异步模块加载。RequireJS 使用 AMD 在浏览器端工作。
可以顺便了解下CMD
3. 通用模块定义(UMD)
UMD(Universal Module Definition)是一种通用的模块定义规范,旨在使同一个模块能够在不同的环境中使用,包括浏览器、Node.js 等。它采用一种灵活的方式,既支持类似 CommonJS 的模块加载方式,也支持类似 AMD 的异步加载方式,同时兼容浏览器全局变量的使用。
UMD 模块通常通过一定的判断逻辑来确定当前的模块加载环境,从而决定采用何种加载方式。
看一个网上的示例,了解下
(function (root, factory) {if (typeof define === "function" && define.amd) {// AMDdefine(["dependency"], factory);} else if (typeof define === "function" && define.cmd) {// CMD 环境define(function (require, exports, module) {module.exports = factory(require("dependency"));});} else if (typeof exports === "object") {// Node.js/CommonJSmodule.exports = factory(require("dependency"));} else {// Browser globalsroot.YourModule = factory(root.Dependency);}
})(typeof self !== "undefined" ? self : this, function (Dependency) {// 你的模块代码return YourModule; // 暴露模块
});
4. ESM
ESM允许开发者将代码拆分为多个独立的文件,这些文件可以相互引用和依赖。每个文件都可以导出一个或多个值(如函数、对象或基本类型),并且可以从其他文件导入这些值。
1.导出(Export)
在ESM中,你可以使用export关键字来导出模块中的值。有两种主要的导出方式:命名导出和默认导出。
- 命名导出:允许你导出多个值,并且每个值都有一个唯一的名称。
// math.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
- 默认导出:每个模块都可以有一个默认导出。当其他模块导入该模块时,默认导出将作为默认的值被导入。
// greeting.js
const greet = (name) => `Hello, ${name}!`;
export default greet;
2.导入(Import)
- 导入命名导出:你可以使用花括号来导入特定的命名导出。
// app.js
import { add, subtract } from './math.js';
console.log(add(1, 2)); // 输出 3
console.log(subtract(3, 1)); // 输出 2
- 导入默认导出:你可以为默认导出指定任何名称。通常,这个名称是默认导出所在模块的名称或一个与该模块功能相关的名称。
// app.js
import greet from './greeting.js';
console.log(greet('World')); // 输出 "Hello, World!"
- 导入所有导出:你还可以使用星号(*)作为通配符来导入模块中的所有导出。这将返回一个包含所有导出的对象。然而,这种用法并不常见,因为它可能导致命名冲突并使代码难以维护。但在某些场景下,如模块重新导出时,它可能是有用的。
// app.js
import * as math from './math.js';
console.log(math.add(1, 2)); // 输出 3
console.log(math.subtract(3, 1)); // 输出 2
3.静态导入和动态导入
在ESM中,导入是静态的,这意味着导入操作在编译时而不是运行时进行。这有助于进行诸如静态分析和代码拆分等优化。然而,有时你可能需要在运行时动态地导入模块。为此,你可以使用import()函数,它返回一个Promise对象,该对象在模块加载完成时解析为模块对象。
import('./dialog.js').then((dialogModule) => {dialogModule.openDialog();}).catch((error) => {console.error('Error loading dialog module:', error);});
4.顶级await
ESM还支持顶级await表达式,允许你在模块的顶层代码中等待异步操作完成。这对于需要在模块初始化时加载资源或获取配置的场景非常有用。
import {getUser} from ‘. /data/User’
let user=await getUser();
5.Tree Shaking:
Tree-Shaking 是一种基于 ES Module 规范的 Dead Code Elimination
技术,它会在运行过程中静态分析模块之间的导入导出,确定 ESM
模块中哪些导出值未曾其它模块使用,并将其删除,以此实现打包产物的优化。Tree Shaking 较早前由 Rich Harris 在
Rollup 中率先实现,Webpack 自 2.0 版本开始接入,至今已经成为一种应用广泛的性能优化手段。
5. 系统模块SystemJs
SystemJS是一个通用的模块加载器,支持多种模块规范,包括CommonJS、AMD、全局模块对象和ES6模块。通过使用插件,它还可以加载CoffeeScript和TypeScript。这使得开发者可以在项目中灵活使用不同的模块规范,而无需担心兼容性问题。
有兴趣的看他的官网:https://github.com/systemjs/systemjs.git
6. 函数表达式(IIFE)
IIFE(立即执行函数表达式)是一种在定义时立即执行的 JavaScript 函数。这种函数形式非常有用,特别是当需要创建一个新的作用域以避免污染全局作用域或需要执行一段代码但不希望这段代码之后再被引用或重用。
(function() {// 函数体
})();/*或者箭头函数*/
(() => {// 函数体
})();
有没有想起 “闭包”
了解它了,有啥用?
写出来的脚本框架是要给人使用的,既然要使用,就需要关注在哪里使用(环境),要不然白白浪费时间,这上面的概念你或许从不关注,那是因为webspack vite这种打包工具帮你干了,若你要发布自己的脚手架项目,不了解怎么能行?多读书,不然老是被人骗…
好了,到这里吧. stop!, 有时间说一说 vite中怎么实现以上场景,!
相关文章:

认识javascript中的模块化
什么是模块化? 将程序⽂件依据⼀定规则拆分成多个文件,拆分出来每个⽂件就是⼀个模块,模块中的数据都是私有的,模块之间互相隔离。如果不进行隔离,可能会造成模块间的变量定义有冲突,导致程序崩溃 为啥要使…...

容器设计模式:Sidecar
文章目录 容器设计模式:Sidecar 模式1. 什么是 Sidecar 模式?2. Sidecar 模式的原理2.1 工作机制2.2 常见用途 3. Sidecar 模式示例示例:日志收集 4. Sidecar 模式的架构图图例: 5. Sidecar 模式的优点6. Sidecar 模式的局限性7. …...

ensp 静态路由配置
A公司有广州总部、重庆分部和深圳分部3个办公地点,各分部与总部之间使用路由器互联。广州、重庆、深圳的路由器分别为R1、R2、R3,为路由器配置静态路由,使所有计算机能够互相访问,实训拓扑图如图所示 绘制拓扑图 给pc机配置ip地址…...

MATLAB图卷积神经网络GCN处理分子数据集节点分类研究
全文链接:https://tecdat.cn/?p38570 本文主要探讨了如何利用图卷积网络(GCN)对图中的节点进行分类。介绍了相关的数据处理、模型构建、训练及测试等环节,通过对分子数据集的操作实践,展示了完整的节点分类流程&#…...

Android-Glide详解
目录 一,介绍 二,使用 三,源码分析思路 四,with源码分析 五,模拟Glide生命周期管理 一,介绍 Glide目前是安卓最主流的加载图片的框架,也是源码最为复杂的框架之一。 要想完完全全吃透Glide的源…...

2.Nuxt学习 组件使用和路由跳转相关
组件定义和使用 普通组件的使用 在Nuxt的项目中,可以直接在components文件夹下建立组件 在页面中直接使用 无需引入 多层级组件的使用 我们有时候会需要多层级组件来简化代码结构 比如我们需要给Banner组件添加一个子组件 我们直接建立其名称的文件夹 写入子组…...

代码开发相关操作
使用Vue项目管理器创建项目:(vue脚手架安装一次就可以全局使用) windowR打开命令窗口,输入vue ui,进入GUI页面,点击创建-> 设置项目名称,在初始化git下面输入:init project&…...

动态导出word文件支持转pdf
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、功能说明二、使用步骤1.controller2.工具类 DocumentUtil 导出样式 前言 提示:这里可以添加本文要记录的大概内容: 例如ÿ…...

登陆harbor发现证书是错误的, 那么如何更新harbor的证书呢
Error response from daemon: Get "https://172.16.21.35/v2/": tls: failed to verify certificate: x509: certificate is valid for 127.0.0.1, ::1, 172.16.21.30, not 172.16.21.35 版本 v2.10.1-b7b88476 不需要从头看, 直接看最下面的成功的证书创建 这里面首…...

【Leetcode Top 100】199. 二叉树的右视图
问题背景 给定一个二叉树的 根节点 r o o t root root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。 数据约束 二叉树的节点个数的范围是 [ 0 , 100 ] [0,100] [0,100] − 100 ≤ N o d e . v a l ≤ 100…...

React自学:如何使用localStorage,以及如何实现删除笔记操作
1. 初始化notes 以下这段代码完成了这些操作: 调用 localStorage.getItem("notes") 从浏览器的本地存储中获取名为 “notes” 的数据。使用 JSON.parse 将获取到的字符串解析成数组。如果本地存储中没有 “notes” 数据(返回值为 null&#…...

go语言使用websocket发送一条消息A,持续接收返回的消息
在Go语言中实现一个WebSocket客户端,可以使用gorilla/websocket这个非常流行的库来处理WebSocket连接。下面是一个简单的示例,展示了如何创建一个WebSocket客户端,向服务器发送消息"A",并持续接收来自服务器的响应。 首…...

如何对小型固定翼无人机进行最优的路径跟随控制?
控制架构 文章继续采用的是 ULTRA-Extra无人机,相关参数如下: 这里用于guidance law的无人机运动学模型为: { x ˙ p V a cos γ cos χ V w cos γ w cos χ w y ˙ p V a cos γ sin χ V w cos γ w sin χ…...

C++常见面试题-初级2
1. C和C有什么区别? C是面向对象的语言,而C是面向过程的语言;C引入new/delete运算符,取代了C中的malloc/free库函数;C引入引用的概念,而C中没有;C引入类的概念,而C中没有࿱…...

Spring Security 6 系列之二 - 基于数据库的用户认证和认证原理
之所以想写这一系列,是因为之前工作过程中使用Spring Security,但当时基于spring-boot 2.3.x,其默认的Spring Security是5.3.x。之后新项目升级到了spring-boot 3.3.0,结果一看Spring Security也升级为6.3.0,关键是其风…...

mfc140.dll是什么东西?mfc140.dll缺失的几种具体解决方法
mfc140.dll是Microsoft Foundation Classes(MFC)库中的一个动态链接库(DLL)文件,它是微软基础类库的一部分,为Windows应用程序的开发提供了丰富的类库和接口。MFC库旨在简化Windows应用程序的开发过程&…...

【STM32 Modbus编程】-作为主设备写入多个线圈和寄存器
作为主设备写入多个线圈和寄存器 文章目录 作为主设备写入多个线圈和寄存器1、硬件准备与连接1.1 RS485模块介绍1.2 硬件配置与接线1.3 软件准备2、写入多个线圈2.1 数据格式2.2 发送数据2.3 结果3、写入多个寄存器3.1 数据格式3.2 发送数据3.3 结果本文将实现STM32作为ModBus主…...

Windows安全中心(病毒和威胁防护)的注册
文章目录 Windows安全中心(病毒和威胁防护)的注册1. 简介2. WSC注册初探3. WSC注册原理分析4. 关于AMPPL5. 参考 Windows安全中心(病毒和威胁防护)的注册 本文我们来分析一下Windows安全中心(Windows Security Center…...

微积分复习笔记 Calculus Volume 2 - 4.2 Direction Fields and Numerical Methods
4.2 Direction Fields and Numerical Methods - Calculus Volume 2 | OpenStax...

深入理解旋转位置编码(RoPE)及其在大型语言模型中的应用
文章目录 前言一、 旋转位置编码原理1、RoPE概述2、 复数域内的旋转1、位置编码生成2、 应用位置编码二、RoPE的实现细节1、RotaryEmbedding类设计2、apply_rotary_pos_emb函数3、demo_apply_rotary_pos_emb函数三、完整RoPE代码Demo前言 随着自然语言处理(NLP)领域的快速发…...

内网穿透的应用-在OpenWrt上轻松搭建SFTP服务,安全传输文件不再难!
文章目录 前言1. 安装openssh-sftp-server2. 安装cpolar工具3.配置SFTP远程访问4.固定远程连接地址 前言 本次教程我们将在OpenWRT系统上安装SFTP服务,并结合cpolar内网穿透,创建安全隧道映射22端口,实现在公网环境下远程OpenWRT SFTP&#…...

【图像处理lec3、4】空间域的图像增强
目录 1. 空间域图像增强的背景与目标 2. 空间域处理的数学描述 3. 灰度级变换 4. 幂律变换(Power-Law Transformation) 5、 分段线性变换 Case 1: 对比度拉伸 Case 2: 灰度切片 Case 3: 按位切片 6、对数变换(Logarithmic Transform…...

【算法day13】二叉树:递归与回溯
题目引用 找树左下角的值路径总和从中序与后序遍历构造二叉树 今天就简简单单三道题吧~ 1. 找到树左下角的值 给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 示例 1: 输入: root [2,1,3] 输出: 1 我们…...

上海亚商投顾:创业板指缩量下跌 多只高位股午后跌停
上海亚商投顾前言:无惧大盘涨跌,解密龙虎榜资金,跟踪一线游资和机构资金动向,识别短期热点和强势个股。 一.市场情绪 市场全天震荡调整,创业板指领跌,高位股开始出现退潮,建设工业、星光股份、…...

单步调试Android Framework——App冷启动
纸上得来终觉浅,绝知此事要躬行。 —— [宋]陆游 基于aosp_cf_x86_64_phone-trunk_staging-eng , 下面是具体断点位置。 第一部分,桌面launcher进程 com.android.launcher3.touch.ItemClickHandler onClickonClickAppShortcutstartAppShor…...

统计一个目录下的文件及目录数量-linux010
要统计一个目录下的文件数量(包括子目录中的文件),可以使用以下命令: 1. 统计所有文件数量(包括子目录) 在终端中运行以下命令: find /path/to/directory -type f | wc -l 解释:…...

spring RestTemplate使用说明
rest-template是spring对httpclient的逻辑封装,它底层还是基于httpclient,所以一些配置其实跟httpclient是强相关的。 基本配置 rest-template可以不带参数,使用默认配置,也可以指定ClientHttpRequestFactory参数,Cl…...

thinkphp:try-catch捕获异常
使用简单的例子,实现了一个简单的try-catch捕获异常的实例 //开始事务Db::startTrans(); try{ //有异常抛出异常 if(存在错误){ throw new \Exception("异常信息"); } // 提交事务 Db::commit(); // 返回成功信息 ... } catch (\…...

shardingsphere分库分表跨库访问 添加分片规则
shardingsphere分库分表跨库访问 添加分片规则 建立 JDBC 环境 创建表 t_order: CREATE TABLE t_order (tid bigint(20) NOT NULL,tname varchar(255) DEFAULT NULL,goods_id bigint(20) DEFAULT NULL,tstatus varchar(255) DEFAULT NULL,PRIMARY KEY (tid) ) E…...

c++:std::map下标运算符的不合理使用
这是我分析之前遗留代码时发现的一个隐藏点;不过我并不认为这样使用std::map是合理的。 看看简化后的代码,v1、v2的值应该是多少呢? #include <map>std::map<int, int> cm[2];int get_cm_value(int device, int ctrl) { auto …...