当前位置: 首页 > news >正文

Nodejs 第九章(模块化)

Nodejs 模块化规范遵循两套一 套CommonJS规范另一套esm规范

CommonJS 规范

引入模块(require)支持四种格式

  1. 支持引入内置模块例如 http os fs child_process 等nodejs内置模块
  2. 支持引入第三方模块express md5 koa
  3. 支持引入自己编写的模块 ./ …/ 等
  4. 支持引入addon C++扩展模块 .node文件
const fs = require('node:fs');  // 导入核心模块
const express = require('express');  // 导入 node_modules 目录下的模块
const myModule = require('./myModule.js');  // 导入相对路径下的模块
const nodeModule = require('./myModule.node');  // 导入扩展模块

导出模块exports 和 module.exports

module.exports = {hello: function() {console.log('Hello, world!');}
};

如果不想导出对象直接导出值

module.exports = 123

ESM模块规范

引入模块 import 必须写在头部

注意使用ESM模块的时候必须开启一个选项
打开package.json 设置 type:module

import fs from 'node:fs'

如果要引入json文件需要特殊处理 需要增加断言并且指定类型json node低版本不支持

import data from './data.json' assert { type: "json" };
console.log(data);

加载模块的整体对象

import * as all from 'xxx.js'

动态导入模块

import静态加载不支持掺杂在逻辑中如果想动态加载请使用import函数模式

if(true){import('./test.js').then()
}

模块导出

  • 导出一个默认对象 default只能有一个不可重复export default
export default {name: 'test',
}
  • 导出变量
export const a = 1

Cjs 和 ESM 的区别

  1. Cjs是基于运行时的同步加载,esm是基于编译时的异步加载
  2. Cjs是可以修改值的,esm值并且不可修改(可读的)
  3. Cjs不可以tree shaking,esm支持tree shaking
  4. commonjs中顶层的this指向这个模块本身,而ES6中顶层this指向undefined

nodejs部分源码解析

.json文件如何处理
Module._extensions['.json'] = function(module, filename) {const content = fs.readFileSync(filename, 'utf8');if (policy?.manifest) {const moduleURL = pathToFileURL(filename);policy.manifest.assertIntegrity(moduleURL, content);}try {setOwnProperty(module, 'exports', JSONParse(stripBOM(content)));} catch (err) {err.message = filename + ': ' + err.message;throw err;}
};

使用fs读取json文件读取完成之后是个字符串 然后JSON.parse变成对象返回

.node文件如何处理
Module._extensions['.node'] = function(module, filename) {if (policy?.manifest) {const content = fs.readFileSync(filename);const moduleURL = pathToFileURL(filename);policy.manifest.assertIntegrity(moduleURL, content);}// Be aware this doesn't use `content`return process.dlopen(module, path.toNamespacedPath(filename));
};

发现是通过process.dlopen 方法处理.node文件

.js文件如何处理
Module._extensions['.js'] = function(module, filename) {// If already analyzed the source, then it will be cached.//首先尝试从cjsParseCache中获取已经解析过的模块源代码,如果已经缓存,则直接使用缓存中的源代码const cached = cjsParseCache.get(module);let content;if (cached?.source) {content = cached.source; //有缓存就直接用cached.source = undefined;} else {content = fs.readFileSync(filename, 'utf8'); //否则从文件系统读取源代码}//是不是.js结尾的文件if (StringPrototypeEndsWith(filename, '.js')) {//读取package.json文件const pkg = readPackageScope(filename);// Function require shouldn't be used in ES modules.//如果package.json文件中有type字段,并且type字段的值为module,并且你使用了require //则抛出一个错误,提示不能在ES模块中使用require函数if (pkg?.data?.type === 'module') {const parent = moduleParentCache.get(module);const parentPath = parent?.filename;const packageJsonPath = path.resolve(pkg.path, 'package.json');const usesEsm = hasEsmSyntax(content);const err = new ERR_REQUIRE_ESM(filename, usesEsm, parentPath,packageJsonPath);// Attempt to reconstruct the parent require frame.//如果抛出了错误,它还会尝试重构父模块的 require 调用堆栈//,以提供更详细的错误信息。它会读取父模块的源代码,并根据错误的行号和列号,//在源代码中找到相应位置的代码行,并将其作为错误信息的一部分展示出来。if (Module._cache[parentPath]) {let parentSource;try {parentSource = fs.readFileSync(parentPath, 'utf8');} catch {// Continue regardless of error.}if (parentSource) {const errLine = StringPrototypeSplit(StringPrototypeSlice(err.stack, StringPrototypeIndexOf(err.stack, '    at ')), '\n', 1)[0];const { 1: line, 2: col } =RegExpPrototypeExec(/(\d+):(\d+)\)/, errLine) || [];if (line && col) {const srcLine = StringPrototypeSplit(parentSource, '\n')[line - 1];const frame = `${parentPath}:${line}\n${srcLine}\n${StringPrototypeRepeat(' ', col - 1)}^\n`;setArrowMessage(err, frame);}}}throw err;}}module._compile(content, filename);
};

如果缓存过这个模块就直接从缓存中读取,如果没有缓存就从fs读取文件,并且判断如果是cjs但是type为module就报错,并且从父模块读取详细的行号进行报错,如果没问题就调用 compile

Module.prototype._compile = function(content, filename) {let moduleURL;let redirects;const manifest = policy?.manifest;if (manifest) {moduleURL = pathToFileURL(filename);//函数将模块文件名转换为URL格式redirects = manifest.getDependencyMapper(moduleURL);//redirects是一个URL映射表,用于处理模块依赖关系manifest.assertIntegrity(moduleURL, content); //manifest则是一个安全策略对象,用于检测模块的完整性和安全性}/*** @filename {string}  文件名* @content {string}   文件内容*/const compiledWrapper = wrapSafe(filename, content, this);let inspectorWrapper = null;if (getOptionValue('--inspect-brk') && process._eval == null) {if (!resolvedArgv) {// We enter the repl if we're not given a filename argument.if (process.argv[1]) {try {resolvedArgv = Module._resolveFilename(process.argv[1], null, false);} catch {// We only expect this codepath to be reached in the case of a// preloaded module (it will fail earlier with the main entry)assert(ArrayIsArray(getOptionValue('--require')));}} else {resolvedArgv = 'repl';}}// Set breakpoint on module startif (resolvedArgv && !hasPausedEntry && filename === resolvedArgv) {hasPausedEntry = true;inspectorWrapper = internalBinding('inspector').callAndPauseOnStart;}}const dirname = path.dirname(filename);const require = makeRequireFunction(this, redirects);let result;const exports = this.exports;const thisValue = exports;const module = this;if (requireDepth === 0) statCache = new SafeMap();if (inspectorWrapper) {result = inspectorWrapper(compiledWrapper, thisValue, exports,require, module, filename, dirname);} else {result = ReflectApply(compiledWrapper, thisValue,[exports, require, module, filename, dirname]);}hasLoadedAnyUserCJSModule = true;if (requireDepth === 0) statCache = null;return result;
};

首先,它检查是否存在安全策略对象 policy.manifest。如果存在,表示有安全策略限制需要处理
将函数将模块文件名转换为URL格式,redirects是一个URL映射表,用于处理模块依赖关系,manifest则是一个安全策略对象,用于检测模块的完整性和安全性,然后调用wrapSafe

function wrapSafe(filename, content, cjsModuleInstance) {if (patched) {const wrapper = Module.wrap(content);//支持esm的模块 //import { a } from './a.js'; 类似于eval//import()函数模式动态加载模块const script = new Script(wrapper, {filename,lineOffset: 0,importModuleDynamically: async (specifier, _, importAssertions) => {const loader = asyncESM.esmLoader;return loader.import(specifier, normalizeReferrerURL(filename),importAssertions);},});// Cache the source map for the module if present.if (script.sourceMapURL) {maybeCacheSourceMap(filename, content, this, false, undefined, script.sourceMapURL);}//返回一个可执行的全局上下文函数return script.runInThisContext({displayErrors: true,});}

wrapSafe调用了wrap方法

let wrap = function(script) {return Module.wrapper[0] + script + Module.wrapper[1];
};
//(function (exports, require, module, __filename, __dirname) {//const xm = 18
//\n});
const wrapper = ['(function (exports, require, module, __filename, __dirname) { ','\n})',
];

wrap方法 发现就是把我们的代码包装到一个函数里面

//(function (exports, require, module, __filename, __dirname) {

//const xm = 18 我们的代码

//\n});

然后继续看wrapSafe函数,发现把返回的字符串也就是包装之后的代码放入nodejs虚拟机里面Script,看有没有动态import去加载,最后返回执行后的结果,然后继续看_compile,获取到wrapSafe返回的函数,通过Reflect.apply调用因为要填充五个参数[exports, require, module, filename, dirname],最后返回执行完的结果。

相关文章:

Nodejs 第九章(模块化)

Nodejs 模块化规范遵循两套一 套CommonJS规范另一套esm规范 CommonJS 规范 引入模块(require)支持四种格式 支持引入内置模块例如 http os fs child_process 等nodejs内置模块支持引入第三方模块express md5 koa 等支持引入自己编写的模块 ./ …/ 等支…...

shell之正则表达式及三剑客grep命令

一、正则表达式概述 什么是正则表达式? 正则表达式是一种描述字符串匹配规则的重要工具 1、正则表达式定义: 正则表达式,又称正规表达式、常规表达式 使用字符串描述、匹配一系列符合某个规则的字符串 正则表达式 普通字符: 大小写字母…...

LeetCode 热题 100 JavaScript--33. 搜索旋转排序数组

整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&#xff09;上进行了 旋转&#xff0c;使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nums[1], …,…...

并发编程 - 线程池中的常见面试题

目录 1. 线程池相比于线程有什么优点 2. 线程池的参数有哪些 3. 线程工厂有什么用 4. 说一下线程的优先级 5. 说一下线程池的执行流程 6. 线程池的拒绝策略有哪些 7. 如何实现自定义拒绝策略 8. 如何判断线程池中的任务是否执行完成 1. 线程池相比于线程有什么优点 有…...

将多个单独的 Excel 文件合并成一个,并添加标题行

要将多个单独的 Excel 文件合并成一个&#xff0c;并添加标题行&#xff0c;可以使用 Python 的 pandas 库。以下是一个示例代码&#xff0c;假设要合并的 Excel 文件都在同一个文件夹中&#xff1a; import os import pandas as pd # 指定文件夹路径 folder_path path/to/fo…...

VPN pptp和l2tp协议破解

代码下载地址&#xff1a; https://download.csdn.net/download/m0_37567738/88215516?spm1001.2014.3001.5501...

4.3、Flink任务怎样读取Kafka中的数据

目录 1、添加pom依赖 2、API使用说明 3、这是一个完整的入门案例 4、Kafka消息应该如何解析 4.1、只获取Kafka消息的value部分 ​4.2、获取完整Kafka消息(key、value、Metadata) 4.3、自定义Kafka消息解析器 5、起始消费位点应该如何设置 ​5.1、earliest() 5.2、lat…...

C语言实例_和校验算法

一、算法介绍 和校验&#xff08;Checksum&#xff09;是一种简单的纠错算法&#xff0c;用于检测或验证数据传输或存储过程中的错误。它通过对数据进行计算并生成校验和&#xff0c;然后将校验和附加到数据中&#xff0c;在接收端再次计算校验和并进行比较&#xff0c;以确定…...

安全加密框架图——Oracle安全开发者

Oracle安全开发者 ACLs 设计 ACLs&#xff08;访问控制列表&#xff09;时&#xff0c;可以根据以下思路进行设计&#xff1a; 所有者文件权限&#xff1a;确定文件的所有者能够对文件执行哪些操作&#xff0c;如读取、写入、执行等。这可以根据文件的性质和拥有者的职责来决…...

Android databinding 被多次定义

一、报错&#xff1a; AndroidStudio运行代码时&#xff0c;编译器报 Type androidx.databinding.Bindable is defined multiple times...... 二、解决&#xff1a; 点击 Build -> Clean Project&#xff0c;关闭编译器再打开即可。 三、解决过程&#xff1a; 在使用Andro…...

云原生周刊:Kubernetes v1.28 新特性一览 | 2023.8.14

推荐一个 GitHub 仓库&#xff1a;Fast-Kubernetes。 Fast-Kubernetes 是一个涵盖了 Kubernetes 的实验室&#xff08;LABs&#xff09;的仓库。它提供了关于 Kubernetes 的各种主题和组件的详细内容&#xff0c;包括 Kubectl、Pod、Deployment、Service、ConfigMap、Volume、…...

机器学习之分类模型

机器学习之分类模型 概述分类模型逻辑回归最近邻分类朴素贝叶斯支持向量机决策树随机森林多层感知机基于集成学习的分类模型VotingBaggingStackingBlendingBoosting 概述 机器学习分类模型通过训练集进行学习&#xff0c;建立一个从输入空间 X X X到输出空间 Y Y Y&#xff08…...

学习Vue:创建第一个Vue实例

当您开始探索 Vue.js&#xff0c;第一步就是创建一个 Vue 实例。Vue 实例是 Vue.js 应用程序的核心构建块&#xff0c;它使您能够将数据与用户界面连接起来&#xff0c;实现动态交互。在本文中&#xff0c;我们将详细介绍如何创建您的第一个 Vue 实例。 步骤1&#xff1a;引入 …...

JavaFx基础学习【二】:Stage

一、介绍 窗口Stage为图中标绿部分&#xff1a; 实际为如下部分&#xff1a; 不同的操作系统表现的样式不同&#xff0c;以下都是以Windows操作系统为例&#xff0c;为了使大家更清楚Stage是那部分&#xff0c;直接看以下图&#xff0c;可能更清楚&#xff1a; 有点潦草&…...

C语言——动态内存函数(malloc、calloc、realloc、free)

了解动态内存函数 前言&#xff1a;一、malloc函数二、calloc函数三、realloc函数四、free函数 前言&#xff1a; 在C语言中&#xff0c;动态内存函数是块重要的知识点。以往&#xff0c;我们开辟空间都是固定得&#xff0c;数组编译结束后就不能继续给它开辟空间了&#xff0…...

Redis数据结构——Redis简单动态字符串SDS

定义 众所周知&#xff0c;Redis是由C语言写的。 对于字符串类型的数据存储&#xff0c;Redis并没有直接使用C语言中的字符串。 而是自己构建了一个结构体&#xff0c;叫做“简单动态字符串”&#xff0c;简称SDS&#xff0c;比C语言中的字符串更加灵活。 SDS的结构体是这样的…...

【计算机网络】TCP协议超详细讲解

文章目录 1. TCP简介2. TCP和UDP的区别3. TCP的报文格式4. 确认应答机制5. 超时重传6. 三次握手7. 为什么两次握手不行?8. 四次挥手9. 滑动窗口10. 流量控制11. 拥塞控制12. 延时应答13. 捎带应答14. 面向字节流15. TCP的连接异常处理 1. TCP简介 TCP协议广泛应用于可靠性要求…...

Salesforce特别元数据部署技巧

标准的picklist字段部署 <?xml version"1.0" encoding"UTF-8" standalone"yes"?> <Package xmlns"http://soap.sforce.com/2006/04/metadata"><types><members>Opportunity.StageName</members><…...

[前端系列第2弹]CSS入门教程:从零开始学习Web页面的样式和布局

在这篇文章中&#xff0c;我将介绍CSS的基本概念、语法、选择器、属性和值&#xff0c;以及如何使用它们来定义Web页面的外观和布局。还将给一些简单而实用的例子&#xff0c;可以跟着我一步一步地编写自己的CSS样式表。 目录 一、什么是CSS 二、CSS的语法 三、CSS的选择器 …...

非计算机科班如何丝滑转码?

转码&#xff0c;也就转行为程序员&#xff0c;已成为当今数字化时代的一种重要技能。随着科技的发展&#xff0c;越来越多的人开始意识到掌握编程技能的重要性&#xff0c;而非计算机科班出身的朋友们&#xff0c;想要丝滑转码&#xff0c;也许可以从以下几个方面入手。 一、明…...

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

MFC内存泄露

1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

前端导出带有合并单元格的列表

// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

CSS设置元素的宽度根据其内容自动调整

width: fit-content 是 CSS 中的一个属性值&#xff0c;用于设置元素的宽度根据其内容自动调整&#xff0c;确保宽度刚好容纳内容而不会超出。 效果对比 默认情况&#xff08;width: auto&#xff09;&#xff1a; 块级元素&#xff08;如 <div>&#xff09;会占满父容器…...

佰力博科技与您探讨热释电测量的几种方法

热释电的测量主要涉及热释电系数的测定&#xff0c;这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中&#xff0c;积分电荷法最为常用&#xff0c;其原理是通过测量在电容器上积累的热释电电荷&#xff0c;从而确定热释电系数…...

Golang——6、指针和结构体

指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...

PostgreSQL——环境搭建

一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在&#xff0…...