Nodejs 第九章(模块化)
Nodejs 模块化规范遵循两套一 套CommonJS
规范另一套esm
规范
CommonJS 规范
引入模块(require)支持四种格式
- 支持引入内置模块例如
http
os
fs
child_process
等nodejs内置模块 - 支持引入第三方模块
express
md5
koa
等 - 支持引入自己编写的模块 ./ …/ 等
- 支持引入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 的区别
- Cjs是基于运行时的同步加载,esm是基于编译时的异步加载
- Cjs是可以修改值的,esm值并且不可修改(可读的)
- Cjs不可以tree shaking,esm支持tree shaking
- 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 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nums[1], …,…...

并发编程 - 线程池中的常见面试题
目录 1. 线程池相比于线程有什么优点 2. 线程池的参数有哪些 3. 线程工厂有什么用 4. 说一下线程的优先级 5. 说一下线程池的执行流程 6. 线程池的拒绝策略有哪些 7. 如何实现自定义拒绝策略 8. 如何判断线程池中的任务是否执行完成 1. 线程池相比于线程有什么优点 有…...
将多个单独的 Excel 文件合并成一个,并添加标题行
要将多个单独的 Excel 文件合并成一个,并添加标题行,可以使用 Python 的 pandas 库。以下是一个示例代码,假设要合并的 Excel 文件都在同一个文件夹中: import os import pandas as pd # 指定文件夹路径 folder_path path/to/fo…...
VPN pptp和l2tp协议破解
代码下载地址: 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语言实例_和校验算法
一、算法介绍 和校验(Checksum)是一种简单的纠错算法,用于检测或验证数据传输或存储过程中的错误。它通过对数据进行计算并生成校验和,然后将校验和附加到数据中,在接收端再次计算校验和并进行比较,以确定…...

安全加密框架图——Oracle安全开发者
Oracle安全开发者 ACLs 设计 ACLs(访问控制列表)时,可以根据以下思路进行设计: 所有者文件权限:确定文件的所有者能够对文件执行哪些操作,如读取、写入、执行等。这可以根据文件的性质和拥有者的职责来决…...
Android databinding 被多次定义
一、报错: AndroidStudio运行代码时,编译器报 Type androidx.databinding.Bindable is defined multiple times...... 二、解决: 点击 Build -> Clean Project,关闭编译器再打开即可。 三、解决过程: 在使用Andro…...
云原生周刊:Kubernetes v1.28 新特性一览 | 2023.8.14
推荐一个 GitHub 仓库:Fast-Kubernetes。 Fast-Kubernetes 是一个涵盖了 Kubernetes 的实验室(LABs)的仓库。它提供了关于 Kubernetes 的各种主题和组件的详细内容,包括 Kubectl、Pod、Deployment、Service、ConfigMap、Volume、…...
机器学习之分类模型
机器学习之分类模型 概述分类模型逻辑回归最近邻分类朴素贝叶斯支持向量机决策树随机森林多层感知机基于集成学习的分类模型VotingBaggingStackingBlendingBoosting 概述 机器学习分类模型通过训练集进行学习,建立一个从输入空间 X X X到输出空间 Y Y Y(…...
学习Vue:创建第一个Vue实例
当您开始探索 Vue.js,第一步就是创建一个 Vue 实例。Vue 实例是 Vue.js 应用程序的核心构建块,它使您能够将数据与用户界面连接起来,实现动态交互。在本文中,我们将详细介绍如何创建您的第一个 Vue 实例。 步骤1:引入 …...

JavaFx基础学习【二】:Stage
一、介绍 窗口Stage为图中标绿部分: 实际为如下部分: 不同的操作系统表现的样式不同,以下都是以Windows操作系统为例,为了使大家更清楚Stage是那部分,直接看以下图,可能更清楚: 有点潦草&…...

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

Redis数据结构——Redis简单动态字符串SDS
定义 众所周知,Redis是由C语言写的。 对于字符串类型的数据存储,Redis并没有直接使用C语言中的字符串。 而是自己构建了一个结构体,叫做“简单动态字符串”,简称SDS,比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页面的样式和布局
在这篇文章中,我将介绍CSS的基本概念、语法、选择器、属性和值,以及如何使用它们来定义Web页面的外观和布局。还将给一些简单而实用的例子,可以跟着我一步一步地编写自己的CSS样式表。 目录 一、什么是CSS 二、CSS的语法 三、CSS的选择器 …...

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

YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...

k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...

Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...