Node.js技术原理分析系列7——Node.js模块加载方式分析
Node.js 是一个开源的、跨平台的JavaScript运行时环境,它允许开发者在服务器端运行JavaScript代码。Node.js 是基于Chrome V8引擎构建的,专为高性能、高并发的网络应用而设计,广泛应用于构建服务器端应用程序、网络应用、命令行工具等。
本系列将分为9篇文章为大家介绍 Node.js 技术原理:从调试能力分析到内置模块新增,从性能分析工具 perf_hooks 的用法到 Chrome DevTools 的性能问题剖析,再到 ABI 稳定的理解、基于 V8 封装 JavaScript 运行时、模块加载方式探究、内置模块外置以及 Node.js addon 的全面解读等主题,每一篇都干货满满。
在上一节中我们探讨了基于 V8 封装 JavaScript 运行时的相关内容,在本节中则主要分享《Node.js模块加载方式分析》相关内容,本文内容为本系列第7篇,以下为正文内容。
1 前言
如今,Node.js 同时实现了 CJS(CommonJS)和 ESM(ECMAScript Module)两套规范,来进行模块管理。
由于早期 JS 没有自己的官方模块规范,在很长一段时间以来 Node.js 一直使用非官方的 CJS 规范。直到 2015 年 ES6 发布,里面定义了 ESM 的规范,各种构建工具和浏览器才逐渐兼容它,而 Node.js 更是在 2019 年的 node 13.2.0 版本才开始正式支持 ESM 特性。
本文以 Node.js 22.7.0 源码为基础,分析 node 对这两个模块规范的具体实现(源码分析方法在最后一节有介绍)。
当我们执行 require/import 的时候,在 node 内部,会发生什么?
2 模块和包的定义
下文中常提到两个概念:模块和包,在文章开始前我们先对它们做一个定义上的澄清。
- 模块
模块是 Node.js 工程的组成部分。模块可以是一个 json 文件,也可以是一个定义了导出值(对象、方法、常量)的文件,也可以是一个三方包。 - 包
包是一个由 package.json 文件描述的文件夹。需注意包也是模块。
3 分析 CJS 模块加载方式
分析时需注意盯紧分析的主要目标,不要迷失在复杂的非关键逻辑和细节中。
下图是 CJS 模块加载的主要函数调用过程:
如图所示,用绿色标出来的节点是核心过程。
核心代码入口在Module._load
节点,所在文件是 lib/internal/modules/cjs/loader.js。
CJS 模块加载过程可以分为两大步骤:
- 解析模块地址
- 正式加载模块
也就是根据解析到的地址,读取模块内容,编译后挂载到module.exports
对象上。
正式加载模块时,会先判断缓存中是否已有,再判断是否是内置模块(内置模块是特殊的处理流程),然后根据模块源码文件的后缀名,进行不同后续逻辑。
接下来分别介绍这几个核心过程。
3.1 解析模块地址
也就是Module._resolveFilename
节点。
这个节点往后都是通过 require 函数入参解析出模块地址的逻辑。例如const test = require('./test')
,通过解析得到的地址会是形如D:\works\node-v22.7.0\test.js
,这样的文件路径。
源码中用的 filename,直译过来是文件名。但是这里用“模块地址”表达比较好,filename 这个名称不太优雅。
3.2 内置模块加载
也就是loadBuiltinModule
节点。
这个节点处理的是内置模块加载业务。
如下代码所示,BuiltinModule.map.get(id)
这一行,是关键代码。
function loadBuiltinModule(id, request) {if (!BuiltinModule.canBeRequiredByUsers(id)) {return;}/** @type {import('internal/bootstrap/realm.js').BuiltinModule} */const mod = BuiltinModule.map.get(id);debug('load built-in module %s', request);// compileForPublicLoader() throws if canBeRequiredByUsers is false:mod.compileForPublicLoader();return mod;
}
BuiltinModule.map
是一个静态变量,意味着程序代码解释阶段,就会写入内存中。它是一个 Map 类型的 对象,以模块名称为 key,存放各种类型的模块的内容。内容包括模块名称、编译后的模块源码等。 这段模块加载的代码只展示了如何从内存取用模块。那么内存中的模块又是什么怎么写入的呢?
内置模块的加载分为两个步骤。
- 注册
是在 node 工程启动时完成的,注册是指将模块源码之外的信息写入内存中的BuiltinModule.map
。 - 加载,即编译模块源码并写入内存。
是指编译模块源码并将已编译源码写入BuiltinModule.map
。
所有模块只有使用(require 或 internalBinding)时才会彻底完成加载。
一个模块加载完成的标志是,内存中有该模块编译好的代码。
3.3 按后缀加载模块
也就是Module._extensions[extension]
节点。
这个节点,按前面解析出的文件后缀名,选用不同的加载逻辑,对模块进行加载。 需要注意的是,除了内置模块,其他所有模块都会在解析模块地址(前文有提到)时,被解析为一个带后缀文件。
如 CJS 模块加载图所示,可分为四类。
- .json
先同步读取 json 文件内容,然后包装成模块,再挂载到module.exports
上 - .js/.ts/.cts
先同步读取 js 文件内容,然后编译,再挂载到module.exports
上 - .node
.node 后缀说明该文件是 addon 的构建产物。.node 文件本质是在 npm install 安装模块时,编译出的动态链接(c++概念)。其加载逻辑是用process.dlopen
方法完成。 - .mjs/.mts
这个分支,用来做 CJS 方式加载.mjs/.mts 文件。
4 分析 ESM 模块加载方式
本文对 ESM 模块加载方式的分析,也是源于源码。源码阅读与调试方式,在最后一节介绍。这里直接用下面的思维导图来记录 ESM 模块加载过程。
如图所示,用绿色标出来的节点是核心过程。
核心代码入口在import
节点,也就是 import 方法,所在文件是 lib/internal/modules/esm/loader.js。
从上到下,导入一个 es module 过程中,依次经历了解析标识符、异步(除非专门设置,默认都是异步)读取模块文件、编译模块代码、实例化模块、执行模块等过程。
接下来依次逐个分析这几个过程。
4.1 标识符解析
4.1.1 什么是标识符?
标识符(specifier)是指使用 import 语句中 from 关键字后面的字符串,或 import() 的入参,或 export 语句中 from 关键字后面的字符串。
例如下面三种示例中的字符串 module-name,都是标识符。
// 示例 1:
import { export1 } from 'module-name';// 示例 2:
import('module-name');// 示例 3:
export * from 'module-name';
在 Node.js 中,node 会根据标识符解析出 url 和 format,两个关键数据。前者是个 URL 类型的对象,后者是字符串。用于后续的模块文件读取和模块代码编译。
4.1.2 url 解析
url 解析是指将标识符解析为 url 的过程。
这里需要回顾一下 url 的定义:URL 的全名是统一资源定位符,它包含 http://
、https://
、ftp://
、file://
、 data:text/plain
、mailto:user@example.com
等多种形式。
url 解析会将相对/绝对路径、三方包等各种形式的标识符解析为 file://
的形式(file://
是引用本地文件的标准方式)。
特别说明一下,严格来说,url 和标识符是两个概念,有不同的边界。但是源码中会将由标识符解析出的 url 继续称作标识符,有些文档也有混用的情况。我觉得是可以接受的。 因为只要 node 支持的 url,都可以直接作为标识符,例如:file:///home/myProject/test.js
。只是通常我们不会这么写。
常见的标识符有三种:
- Relative specifiers like ‘./startup.js’ or ‘…/config.mjs’. They refer to a path relative to the location of the importing file. The file extension is always necessary for these.
- Bare specifiers like ‘some-package’ or ‘some-package/shuffle’. They can refer to the main entry point of a package by the package name, or a specific feature module within a package prefixed by the package name as per the examples respectively. Including the file extension is only necessary for packages without an “exports” field.
- Absolute specifiers like ‘file:///opt/nodejs/config.js’. They refer directly and explicitly to a full path.
实际还可以支持更多形式的标识符,例如:
-
‘#alias’
这是路径别名。如果你想根据环境切换实际依赖,可以通过 package.json 的 imports 字段配置别名来实现。
例如,你可能需要创建一个同时兼容 Node.js 和浏览器的三方包。三方包的 package.json 中部分代码需要配置如下:
// package.json {"imports": {"#dep": {"node": "dep-node-native","default": "./dep-polyfill.js"}},"dependencies": {"dep-node-native": "^1.0.0"} }// index.js import dep from '#dep'// ...
那么,当这个三方包在node工程中使用时,三方包中的
import dep from '#dep'
语句实际会加载dep-node-native
;当这个三方包在前端工程中使用时,三方包中的import dep from '#dep'
语句实际会加载./dep-polyfill.js
. -
下列源码中,
extensionFormatMap
中的各种扩展名const experimentalWasmModules = getOptionValue('--experimental-wasm-modules');const extensionFormatMap = {__proto__: null,'.cjs': 'commonjs','.js': 'module','.json': 'json','.mjs': 'module', };if (experimentalWasmModules) {extensionFormatMap['.wasm'] = 'wasm'; }if (getOptionValue('--experimental-strip-types')) {extensionFormatMap['.ts'] = 'module-typescript';extensionFormatMap['.mts'] = 'module-typescript';extensionFormatMap['.cts'] = 'commonjs-typescript'; }
-
import(./${moduleName}.js)
动态计算模块路径
分析过程中,我有过两个小疑问,也呈现一下,供参考:
-
为什么要解析成 URL 类型?
ESM 规范没有这方面的规定。
经源码分析,除了内置模块,各种形式的标识符都可以解析为 URL 形式。
所以可能是出于统一数据结构的需要。 -
模块文件查找顺序是怎样的?
了解模块加载时的文件查找顺序,有助于我们处理各种 xxx 找不到问题。
但是太过复杂,这里不记录了。如果有必要再根据问题的情况,调试源码比较明智。 只要有调试方法(在后面的章节会给出)和模块加载的思维导图,很容易就能定位处理单个 case。核心代码在 lib/internal/modules/esm/resolve.js 文件的 moduleResolve 方法中。
4.1.3 格式解析
node 中,ESM 源码实现中有一个清晰的 translator 层(lib/internal/modules/esm/translators.js)。通过这个层,可以根据标识符解析出的 format,切换 translator,从而对不同类型的模块做不同的处理。
当前的 node 版本中,有 10 种 translator,分别对应 10 种 format。
translator 是什么?有什么作用?接下来按不同 format,逐个分析一下。
-
module 格式
表示当前导入模块是 esm。translators.set('module', function moduleStrategy(url, source, isMain) {assertBufferSource(source, true, 'load');source = stringify(source);debug(`Translating StandardModule ${url}`);const { compileSourceTextModule } = require('internal/modules/esm/utils');const module = compileSourceTextModule(url, source, this);return module; });// ...function compileSourceTextModule(url, source, cascadedLoader) {const hostDefinedOption = cascadedLoader ? source_text_module_default_hdo : undefined;const wrap = new ModuleWrap(url, undefined, source, 0, 0, hostDefinedOption);if (!cascadedLoader) {return wrap;}// Cache the source map for the module if present.if (wrap.sourceMapURL) {maybeCacheSourceMap(url, source, wrap, false, undefined, wrap.sourceMapURL);}return wrap; }
这段代码就是一个 translator。
其中的入参 source 是前置步骤——异步读取模块文件得到的模块代码。
第 5 行中使用了 require,说明当前版本的 esm 还在依赖 cjs 实现。
第 6 行 new ModuleWrap 中包含了模块源代码编译过程。 -
builtin 格式
表示当前导入的模块是内置模块。translators.set('builtin', function builtinStrategy(url) {debug(`Translating BuiltinModule ${url}`);// Slice 'node:' schemeconst id = StringPrototypeSlice(url, 5);const module = loadBuiltinModule(id, url);cjsCache.set(url, module);if (!StringPrototypeStartsWith(url, 'node:') || !module) {throw new ERR_UNKNOWN_BUILTIN_MODULE(url);}debug(`Loading BuiltinModule ${url}`);return module.getESMFacade(); });
第 5 行,通过内置模块 id,直接从内存获取内置模块。与 cjs 实现相同,具体参考本文第四节 CJS 模块加载分析。
-
json 格式
表示当前导入的模块是 json 文件。 esm 实现对 json 文件的处理方式与 cjs 实现类似。都是读取解析 json 文件后,将其封装为一个模块。translators.set('json', function jsonStrategy(url, source) {// ...try {const exports = JSONParse(stripBOM(source));module = {exports,loaded: true,};} catch (err) {// TODO (BridgeAR): We could add a NodeCore error that wraps the JSON// parse error instead of just manipulating the original error message.// That would allow to add further properties and maybe additional// debugging information.err.message = errPath(url) + ': ' + err.message;throw err;}if (shouldCheckAndPopulateCJSModuleCache) {CJSModule._cache[modulePath] = module;}cjsCache.set(url, module);return new ModuleWrap(url, undefined, ['default'], function () {debug(`Parsing JSONModule ${url}`);this.setExport('default', module.exports);}); });
-
其他
前三种已经包括了 esm 实现的绝大部分使用需要,其他几个不常用,有需要可以到 lib/internal/modules/esm/translators.js,直接看源码。
4.2 异步读取模块文件
除了内置模块,每个模块的加载都会有文件读取环节。
衔接上一节,上一节解析得到了模块 url(文件地址),本节是用解析到的 url 来找到模块文件并读取。
不同于 cjs,除非特别设置,esm 模块加载,无论是静态导入,还是动态导入,都是异步读取模块文件。
4.3 编译模块代码
在编译阶段,会根据步骤 4.1 得到的模块 format,选用对应的 translator,对步骤 4.2 读取到的模块文件进行编译。
阅读源码时需要注意,esm 模块的源码使用了 Promise 高级用法:(moduleJob)modulePromise 节点使用的 moduleProvider,本质是个 async Function,也就是异步任务。这个异步任务在(async Function)moduleProvider 节点处创建,但是在 await moduleJob.linked 节点处才加入任务队列(执行)。
4.4 实例化模块
上一步得到的编译产物会存储到模块包装类 ModuleWrap 的实例中。
这一步在 ModuleWrap 的实例的基础上,完成模块的实例化。
4.5 执行模块
ESM 模块加载完成后,都会立即执行一次。
为什么会立即执行一次呢?看起来这个步骤没有必要,反而空耗性能。
大模型给的回答如下:
- 模块可能包含全局或静态代码,需要初始化
- 有些模块需要向全局作用域注入变量、打补丁等
5 按加载方式分类 Node 模块
经分析,按加载方式分类 Node.js 模块,可以帮助我们更好地记忆、更清晰地理解 node 模块加载过程以及运行原理知识。
如图所示,前三个分类同时适用于 CJS 和 ESM 实现;C++三方模块,也就是 addon,目前只有 CJS 实现了这类模块的加载,ESM 暂不支持加载 addon。
6 CJS 与 ESM 的关联
首先声明,这里讨论的“关联”,仅限于 Node.js 实现的 CJS 和 ESM。
6.1 ESM 对 CJS 有依赖
CJS 和 ESM 在 node 中都是用JS语言实现的。
ESM 源码文件(例如lib/internal/modules/esm/loader.js)本身也是一个 node 模块,其加载是通过 CJS 实现来完成的。
例如前文提到的defaultResolve
节点的源码是这样的:
defaultResolve(originalSpecifier, parentURL, importAttributes) {
defaultResolve ??= require('internal/modules/esm/resolve').defaultResolve;const context = {__proto__: null,conditions: this.#defaultConditions,importAttributes,parentURL,
};return defaultResolve(originalSpecifier, context);
}
defaultResolve 方法内使用 CJS 的 require 方法加载了 ESM 的源码文件 internal/modules/esm/resolve.js。
6.2 相互实现了对对方模块的兼容加载
一般来说,一个 node 工程同时启用 CJS 和 ESM 两种规范,是不推荐的。但是为了方便迁移和过渡,node 的 CJS 和 ESM 相互兼容了对方。
-
CJS 兼容 ESM
前文 4.1.2 介绍 CJS 支持的扩展名时有提到,.mjs 和 .mts 文件都是可以用 CJS 方式加载的。 -
ESM 兼容 CJS
前文有提到 translator,在 ESM 实现中,提供了 require-commonjs 和 require-commonjs-typescript 两个 translator 来兼容CJS模块的加载。源码如下:
// Handle CommonJS modules referenced by `require` calls. // This translator function must be sync, as `require` is sync. translators.set('require-commonjs', (url, source, isMain) => {assert(cjsParse);return createCJSModuleWrap(url, source); });// Handle CommonJS modules referenced by `require` calls. // This translator function must be sync, as `require` is sync. translators.set('require-commonjs-typescript', (url, source, isMain) => {emitExperimentalWarning('Type Stripping');assert(cjsParse);const code = stripTypeScriptTypes(stringify(source), url);return createCJSModuleWrap(url, code); });
7 分析方法介绍
我发现源码阅读最好的姿势就是,一边调试一边读源码。所以开始一个任务时,尽量走通对应的调试流程。
本文的调试的困难除了对未知的恐惧,主要在异步代码的调试。
CJS 源码的调试相对简单,只需开启源码调试(参考下文中 ESM 源码调试的设置),在 require 方法所在行加断点,下钻(step into)即可。
由于 import 语句(例如import test from 'test'
)声明是静态的,它导入的模块在代码执行前已经完成加载,所以无法对其加载过程进行断点调试。
本节主要介绍下 ESM 源码调试,即import()
方法的调试。
- 调试配置
按下图配置即可开始 import() 源码调试。test.js 和 launch.js 的内容如图所示,./testExport.js
文件里随便写点什么,语法能通就行。
- 按下一步(step over)跳过 promise 马甲
- 进入业务代码后,从这里点下钻(step into)
- 从这里再次下钻
- 再次下一步跳过 promise 马甲
- 在481行加个断点,然后点继续按钮(Continue),即可到达481行。
这个断点不要删了,后续我们调试业务,可以直达这里。
接下来就可以边调试,边阅读源码了。
关于OpenTiny
欢迎加入 OpenTiny 开源社区。添加微信小助手:opentiny-official 一起参与交流前端技术~
OpenTiny 官网:https://opentiny.design
OpenTiny 代码仓库:https://github.com/opentiny
TinyVue 源码:https://github.com/opentiny/tiny-vue
TinyEngine 源码: https://github.com/opentiny/tiny-engine
欢迎进入代码仓库 Star🌟TinyEngine、TinyVue、TinyNG、TinyCLI、TinyEditor~
如果你也想要共建,可以进入代码仓库,找到 good first issue标签,一起参与开源贡献~
相关文章:
Node.js技术原理分析系列7——Node.js模块加载方式分析
Node.js 是一个开源的、跨平台的JavaScript运行时环境,它允许开发者在服务器端运行JavaScript代码。Node.js 是基于Chrome V8引擎构建的,专为高性能、高并发的网络应用而设计,广泛应用于构建服务器端应用程序、网络应用、命令行工具等。 本系…...
BFD:网络链路检测与联动配置全攻略
目录 BFD简介 BFD会话建立方式和检测机制 BFD会话建立过程 BFD工作流程 联动功能 BFD与OSPF联动配置需求 BFD与OSPF联动配置实现 BFD与VRRP联动配置需求 BFD与VRRP联动配置实现 单臂回声 BFD默认参数及调整方法 BFD简介 一种全网统一、检测迅速、监控网络中链…...
预防WIFI攻击,保证网络安全
文章总结(帮你们节约时间) WiFi协议存在多种安全漏洞,从去认证攻击到KRACK和PMKID攻击,这些都源于协议设计中的历史遗留问题。ESP32S3微控制器结合Arduino环境,成为强大的WiFi安全研究平台,可用于网络扫描…...
循环神经网络 - 门控循环单元网络
为了解决循环神经网络在学习过程中的长程依赖问题,即梯度消失或爆炸问题,一种非常好的解决方案是在简单循环网络的基础上引入门控机制来控制信息的累积速度,包括有选择地加入新的信息,并有选择地遗忘之前累积的信息。这一类网络可…...
Java 正则表达式综合实战:URL 匹配与源码解析
在 Web 应用开发中,我们经常需要对 URL 进行格式验证。今天我们结合 Java 的 Pattern 和 Matcher 类,深入理解正则表达式在实际应用中的强大功能,并剖析一段实际的 Java 示例源码。 package com.RegExpInfo;import java.util.regex.Matcher; …...
TCPIP详解 卷1协议 六 DHCP和自动配置
6.1——DHCP和自动配置 为了使用 TCP/IP 协议族,每台主机和路由器需要一定的配置信息。基本上采用3种方法:手工获得信息;通过一个系统获得使用的网络服务;使用某种算法自动确定。 拥有一个IP 地址和子网掩码,以及 DN…...
面试宝典(C++基础)-01
文章目录 1. C++基础1.1 C++特点1.2 说说C语言和C++的区别1.3 说说 C++中 struct 和 class 的区别1.4 include头文件的顺序以及双引号""和尖括号<>的区别1.5 说说C++结构体和C结构体的区别1.6 导入C函数的关键字是什么,C++编译时和C有什么不同?1.7 C++从代码…...
【笔记ing】AI大模型-04逻辑回归模型
一个神经网络结构,其中的一个神经网络层,本质就是一个逻辑回归模型 深度神经网络的本质就是多层逻辑回归模型互相连接或采用一定的特殊连接的方式连接在一起构成的。其中每一个层本质就是一个逻辑回归模型。 逻辑回归模型基本原理 逻辑回归࿰…...
【Android】常用参数实践 用户界面UI 布局文件XML
本文将系统总结 Android XML 布局的通用参数和常用布局类型的专属规则 一、通用布局参数 这些参数适用于所有 View 和 ViewGroup,是布局设计的基石。 1. 尺寸控制 android:layout_width 与 android:layout_height 定义视图的宽度和高度,可选值…...
音乐产业新玩法:NFTs如何颠覆传统与挑战未来?
音乐产业新玩法:NFTs如何颠覆传统与挑战未来? 近年来,NFT(Non-Fungible Token,非同质化代币)像一颗新星,迅速在数字艺术、游戏等领域掀起了革命。而在音乐产业,NFT不仅是一种数字所…...
测试基础笔记第三天
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 ⼀、缺陷介绍定义:软件中使⽤中任何问题都为缺陷,简称:bug 二、缺陷编写三、注册模块测试点练习 ⼀、缺陷介绍 定义:…...
HTML5 Web 存储:超越 Cookie 的本地存储新选择
一、引言 在当今的 Web 开发领域,对于用户数据的本地存储需求日益增长。HTML5 带来了一种比传统 cookie 更强大、更安全、更高效的本地存储方式 ——Web 存储。本文将深入探讨 HTML5 Web 存储的相关知识,包括其基本概念、浏览器支持情况、localStorage …...
基于 DB、EAST、SAST 的文本检测算法详解及应用综述
摘要 近年来,随着深度学习在计算机视觉领域的广泛应用,自然场景文字检测技术取得了飞速发展。针对复杂背景、任意形状、多角度文本等问题,学术界和工业界陆续提出了 DB、EAST、SAST 等多种算法。本文详细介绍了这几种主流文本检测方法的原理…...
牙刷生产中的视觉检测,让刷毛缺陷检测高效便捷!
在日常口腔护理中,一把优质牙刷至关重要,而刷毛质量直接决定了牙刷品质。从生产端来看,牙刷制造行业正面临着品质管控的严峻挑战。人工目检在检测刷毛缺陷时,不仅效率低下,还极易因主观因素导致漏检、误检,…...
面向对象编程(OOP)核心概念进阶
面向对象编程(OOP)核心概念进阶 final 关键字 行为特征:作为终结者关键字,用于限制类、方法、变量的可修改性 三层控制力: - 修饰类: 定义不可继承的最终类(如 String、Integer 等不可变类核…...
AI与教育的协奏曲:重构未来学习生态
📝个人主页🌹:慌ZHANG-CSDN博客 🌹🌹期待您的关注 🌹🌹 引言:教育的“智变”来临 在经历了千年的教与学之后,教育终于迎来了最大规模的技术变革浪潮。随着ChatGPT、DeepSeek、Grok 等大语言模型的诞生与不断演进,AI正以前所未有的方式深入影响每一个学生、老师…...
性能炸裂的数据可视化分析工具:DataEase!
今天分享一款开源的数据可视化分析工具,帮助用户快速分析数据并洞察业务趋势,从而实现业务的改进与优化。支持丰富的数据源连接,能够通过拖拉拽方式快速制作图表,并可以方便地与他人分享。 技术栈 前端:Vue.js、Elemen…...
9.thinkphp的请求
请求对象 当前的请求对象由think\Request类负责,该类不需要单独实例化调用,通常使用依赖注入即可。在其它场合则可以使用think\facade\Request静态类操作。 项目里面应该使用app\Request对象,该对象继承了系统的think\Request对象ÿ…...
UBUNTU20.04安装ros2
ubuntu20.04安装ROS2 详细教程_ubuntu20.04 ros2-CSDN博客...
数据可视化工具LightningChart .NET v12.2.1全新发布——支持新的 .NET 目标框架
LightningChart.NET完全由GPU加速,并且性能经过优化,可用于实时显示海量数据-超过10亿个数据点。 LightningChart包括广泛的2D,高级3D,Polar,Smith,3D饼/甜甜圈,地理地图和GIS图表以及适用于科学…...
Python + Playwright:规避常见的UI自动化测试反模式
Python + Playwright:规避常见的UI自动化测试反模式 前言反模式一:整体式页面对象(POM)反模式二:具有逻辑的页面对象 - POM 的“越界”行为反模式三:基于 UI 的测试设置 - 缓慢且脆弱的“舞台搭建”反模式四:功能测试过载 - “试图覆盖一切”的测试反模式之间的关联与核…...
蓝宝石狼组织升级攻击工具包,利用新型紫水晶窃密软件瞄准能源企业
网络安全专家发现,被称为"蓝宝石狼"(Sapphire Werewolf)的威胁组织正在使用升级版"紫水晶"(Amethyst)窃密软件,对能源行业企业发起复杂攻击活动。此次攻击标志着该组织能力显著提升&am…...
高光谱相机:温室盆栽高通量植物表型光谱成像研究
传统植物表型测量依赖人工观察与手工记录,存在效率低、主观性强、无法获取多维数据(如生化成分、三维形态)等缺陷。例如,叶片氮含量需破坏性取样检测,根系表型需挖掘植株,导致数据不连续且难以规模化。此外…...
Android Studio安装平板的虚拟机
其实很简单,但是我刚开始也是一窍不通,所以也查了好多资料才会的,本文仅作为个人学习笔记分享,有跟我一样的小白可以当做一个参考,有什么问题也欢迎大家提出建议,俺会虚心接受并改进的~ 首先我们打开项目&…...
Redis 常问知识
1.Redis 缓存穿透问题 缓存穿透:当请求的数据在缓存和数据库中不存在时,该请求就跳出我们使用缓存的架构(先从缓存找,再从数据库查找、这样就导致了一直去数据库中找),因为这个数据缓存中永远也不会存在。…...
UnityUI:Canvas框架获取鼠标悬浮UI
将下面脚本挂在主体Canvas上,Canvas会对下面所有Image挂上PointerHandler脚本,并且可以通过GetPointEnter方法判断当前鼠标是否悬停在UI上 public class BaseCanvas : MonoBehaviour {public static BaseCanvas Main;private void Awake(){Main this;I…...
NLP实战(3):RNN英文名国家分类
目录 1. 项目需求 2. 模型解析 2.1 网络模型 2.2 准备数据 2.3 双向循环神经网络 3. 代码解析 4. 完整代码 5. 结果 1. 项目需求 对名字的分类,几千个名字,总共来自于18个国家 2. 模型解析 对于自然语言处理来说,输入是一个序列&am…...
东方博宜OJ ——1335 - 土地分割
递归 入门 ————1335 - 土地分割 1335 - 土地分割题目描述输入输出样例问题分析递归解法(欧几里得算法)代码实现总结 1335 - 土地分割 题目描述 把一块m * n米的土地分割成同样大的正方形,如果要求没有土地剩余,分割出的正方形…...
在轨道交通控制系统中如何实现μs级任务同步
轨道交通作为现代城市化进程中的重要支柱,承载着数以亿计的乘客出行需求,同时也是城市经济运行的命脉。无论是地铁、轻轨还是高速铁路,其控制系统的稳定性和可靠性直接关系到运营安全和效率。在这样一个高风险、高复杂度的环境中,…...
【C++教程】进制转换的实现方法
在C中进行进制转换可以通过标准库函数或自定义算法实现。以下是两种常见场景的转换方法及示例代码: 一、使用C标准库函数 任意进制转十进制 #include <string> #include <iostream>int main() {std::string num "1A3F"; // 十六进制数int…...
日志文件爆满_配置使用logback_只保留3天日志文件_每天定时生成一个日志文件---SpringCloud工作笔记206
日志文件爆满,springCloud微服务架构中的,日志爆满如何解决,使用脚本直接删除,会导致, 如果要删除的日志文件,还正在被进程占用,那么你即使使用脚本定时删除了,这个日志文件,那么这个日志文件实际上还是不会删除的,他的大小,依然占用磁盘,就是因为,有进程还在占用它,所以之前说…...
DICOM通讯(ACSE->DIMSE->Worklist)
DICOM 通讯协议中的 ACSE → DIMSE → Worklist 这条通讯链路。DICOM 通讯栈本身是一个多层的协议结构,就像 OSI 模型一样,逐层封装功能。 一、DICOM 通讯协议栈总体架构 DICOM 通讯使用 TCP/IP 建立连接,其上面封装了多个协议层次…...
QML与C++:基于ListView调用外部模型进行增删改查(附自定义组件)
目录 引言相关阅读项目结构文件组织 核心技术实现1. 数据模型设计联系人项目类 (datamodel.h)数据模型类 (datamodel.h)数据模型实现 (datamodel.cpp) 2. 主程序入口点 (main.cpp)3. 主界面设计 (Main.qml)4. 联系人对话框 (ContactDialog.qml)5. 自定义组件CustomTextField.qm…...
# linux 设置宽容模式
linux 设置宽容模式 在Linux系统中,通常没有直接称为“宽容模式”的设置选项,但你可以通过几种方式来模拟或调整系统行为,使其表现得更加“宽容”,特别是在处理错误、权限问题或其他潜在问题时。以下是一些常见的方法:…...
#1 理解物联网
物联不是一个新概念,物联网如其中文译名, 虚拟和物的对应和联接。 对于人类的梦想而言,总是希望自己无比强大,但受限于外部条件而只能为此悻悻念念。 所以人们的目光聚焦在,上世纪70年代发展的传感器、大规模电路、通…...
物联网场景实战:智能电表数据管理与分析(二)
数据管理 数据清洗与预处理 智能电表在数据采集、传输和存储过程中,不可避免地会引入噪声、出现缺失值和异常值等问题,这些问题会严重影响数据的质量和后续分析的准确性,因此数据清洗至关重要。 噪声数据通常是由于测量误差、通信干扰等原…...
linux一次启动多个jar包
linux一次启动多个jar包并且可以自定义路径和端口号 代码使用 分享公司大神使用的一个脚步,可以一次启动多个jar包,也可以指定启动jar包 代码 #! /bin/sh # 端口号 PORTS(8080 8081 8082 8083) # 模块 MODULES(gateway auth system file) # 模块名称 MODULE_NAMES(网关服务 认…...
自然语言交互:NAS进化的下一站革命
自然语言交互:NAS进化的下一站革命 在数据爆炸式增长的数字时代,网络附加存储设备(NAS)早已突破企业级应用的边界,成为个人数字资产管理的核心枢纽。当全球NAS市场年复合增长率稳定在15%之际,耘想科技推出…...
go中我遇到的问题总结
go问题总结 1 - go中的nil等于java中的null吗 在 Go 和 Java 中,nil 和 null 都用于表示“空值”,但它们的实现和使用方式有所不同。 以下是 Go 中的 nil 和 Java 中的 null 之间的对比: 1. Go 中的 nil 在 Go 中,nil 是一个预定义的常量,表示零值。它的行为根据数据类…...
java面试题带答案2025最新整理
文章目录 一、java面试题集合框架1. 请简要介绍 Java 集合框架的体系结构2. ArrayList 和 LinkedList 的区别是什么3. HashMap 的工作原理是什么,它在 JDK 7 和 JDK 8 中有哪些不同4. 如何解决 HashMap 的线程安全问题5. TreeSet 是如何保证元素有序的 二、java面试…...
第七届浙江省大学生网络与信息安全竞赛决赛Unserialize深度解析 1.0
花还会重新开,不同的春来了又来。 - 2025.4.11 0x01 声明 仅作为个人学习使用,仅供参考,欢迎交流 可能是新生赛缘故,突发奇想,想好好梳理此题,顺便写成参考,于是有了这篇文章 当然很多理解可…...
onlyoffice 在线编辑集成
onlyoffice 在线编辑集成 项目中要使用word在线编辑功能,记录一下过程 安装使用docker版本 docker run -itd -p 8001:80 --name kodoffice --restart always registry.cn-hangzhou.aliyuncs.com/kodcloud/kodoffice:7.4.1.1 启动后http://192.168.x.x:8001/web/…...
2.4goweb 项目1
mysql库和表 CREATE DATABASE IF NOT EXISTS book_manager CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;USE book_manager;-- 用户表(用于登录) CREATE TABLE IF NOT EXISTS users (user_id INT AUTO_INCREMENT PRIMARY KEY,username VARCHAR(…...
ESP-ADF外设子系统深度解析:esp_peripherals组件架构与核心设计(系列开篇)
目录 ESP-ADF外设子系统深度解析:esp_peripherals组件架构与核心设计(系列开篇)简介模块概述功能定义架构位置核心特性 接口分析公共API概述1. 外设集合管理API2. 单个外设管理API3. 事件通信API4. 定时器管理API 数据结构关键数据结构分析枚…...
供应链管理:供应链管理的边界
一、追根溯源,什么是真正的财富 序号财富解释1土地作为生产资料,土地是农业、工业、商业的基础 城市中心的土地因稀缺性而价值连城,农业土地的肥沃程度直接影响粮食产量。2资源、矿产提供能源和原材料,支撑工业生产和经济发展。 …...
【Linux网络编程】TCP Echo Server的实现
本文专栏:linux网络编程 本文的基础知识是基于上篇文章:UDP Echo Server的实现 传送门: 【Linux网络编程】UDP Echo Server的实现 -CSDN博客 目录 一,InetAddr类的编写 二,客户端代码编写 创建套接字(s…...
信奥赛CSP-J复赛集训(数学思维专题)(11):P9585 「MXOI Round 2」酒店
信奥赛CSP-J复赛集训(数学思维专题)(11):P9585 「MXOI Round 2」酒店 题目描述 小 C 开了一家酒店,叫做 CC Hotel。 一天,CC Hotel 来了 n n n 位客人。小 C 需要把他们都安排在酒店的某一层…...
python: audioFlux XXCC 提取梅尔频率倒谱系数 MFCC
承上一篇:python:audioFlux 使用教程 XXCC: 倒谱系数,支持所有频谱类型. 可以提取梅尔频率倒谱系数(MFCC) Cepstrum coefficients, supports all spectrum types. 以下是使用 audioflux 库中 XXCC 类计算倒谱系数…...
PHP + Go 如何协同打造高并发微服务?
为什么需要 PHP Go 协同? 在微服务架构中,PHP 和 Go 看似是“两个世界”的语言,但它们的互补性极强: PHP:开发效率高、生态成熟,适合快速实现复杂业务逻辑(如电商订单、用户系统)…...
k8s工具使用
Kubectl Cheat Sheet k8s的命令级别 1.基础命令(初级) 2.基础命令(中级) 3.部署命令 4.集群管理命令 5.故障排查和调试命令 6.高级命令 7.设置命令 8.其它命令 命令行提示 为了使用kubectl命令更加高效,我们可以选择安装一下开源软件来增加操作kubectl命令的快捷方式,同…...