【1.8w字深入解析】从依赖地狱到依赖天堂:pnpm 如何革新前端包管理?

news2025/3/13 16:20:12

目录

  • 前言
  • npm 的诞生与发展
  • 嵌套依赖模型存在的问题
  • npm3架构与yarn
  • Yarn 的诞生与局限
    • Yarn 的诞生背景
    • Yarn 仍然存在的问题
  • 何为幽灵依赖
  • 依赖结构的不确定性
  • pnpm王牌登场 -- 网状+平铺结构
    • 安装包速度快
    • 依赖管理
    • 软链接 和 硬链接 机制
  • 幽灵依赖产生的根本原因
    • 包管理工具的依赖解析机制
    • 第三方库历史问题
    • JavaScript 模块解析策略
  • pnpm 项目的依赖治理方案
    • 冗余依赖治理
    • 重叠依赖治理
  • 最后

前言

在现代前端开发中,高效的包管理和依赖治理对于项目的健康发展至关重要。随着项目规模的不断扩大,传统的 npmyarn 在处理依赖关系时可能会遇到诸如依赖重复安装磁盘空间浪费依赖版本冲突等问题。而 pnpm(performant npm)作为新一代包管理工具,不仅显著提升了安装效率,还为项目依赖治理带来了全新的解决方案。

本文将深入探讨 pnpm 的核心特性及其在实际项目中的应用。我们将从以下几个方面展开:

  • 包管理工具的发展历程以及pnpm的独特优势
  • 产生幽灵依赖的根本原因探究
  • 什么是依赖结构的不确定性
  • pnpm 的工作原理及其相比 npm / yarn 的优势
  • 基于 pnpm 的依赖治理最佳实践

通过本文的介绍,希望能够帮助你更好地理解和使用 pnpm,建立起完善的依赖管理体系。(看完还不懂任你说!😘)

在这里插入图片描述


npm 的诞生与发展

在 Web 开发的早期阶段,JavaScript 代码主要以简单的脚本形式存在,开发者通常通过手动下载和管理代码库。随着 2009 年 Node.js 的诞生,JavaScript 开始在服务器端大放异彩,开发者对模块化开发和依赖管理的需求也随之增长。

在这样的背景下,Isaac Z. Schlueter 于 2010 年创建了 npm(Node Package Manager)。作为 Node.js 的标准包管理工具,npm 优雅地解决了模块安装版本管理依赖管理等问题。它引入了革命性的 node_modules 目录结构,允许每个项目维护自己的依赖,实现了依赖的局部安装,使得不同项目能够使用不同版本的包而不会产生冲突。

npm 的出现极大地促进了 JavaScript 生态系统的发展。开发者可以轻松地发布、共享和复用代码,这导致了开源社区的蓬勃发展。截至目前,npm 注册表已经成为世界上最大的软件注册库,拥有超过 200 万个包,每周下载量超过 350 亿次。

在这里插入图片描述

然而,随着项目规模的扩大和依赖数量的增加,npm 的一些固有问题开始显现:

  1. node_modules 体积膨胀:由于依赖嵌套和重复安装,一个简单的项目可能产生数百兆的 node_modules 目录
  2. 安装效率低下:重复的依赖下载和磁盘写入操作导致安装速度慢
  3. 依赖结构复杂:扁平化算法可能导致依赖关系难以预测
  4. 磁盘空间浪费:相同的依赖包在不同项目中重复存储

这些问题推动了包管理工具的进一步发展,催生了 Yarn(2016)和 pnpm(2017)等新一代包管理工具的诞生。


嵌套依赖模型存在的问题

在 npm2 及以前,每个包会将其依赖安装在自己的 node_modules 目录下,这意味着每个依赖也会带上自己的依赖,形成一个嵌套的结构,结构如下::
在这里插入图片描述

假如嵌套的层数很深呢?

node_modules 
└─ 依赖A 
   ├─ index.js 
   ├─ package.json 
   └─ node_modules 
       └─ 依赖B 
       ├─ index.js 
       ├─ package.json
       └─ node_modules 
           └─ 依赖C 
           ├─ index.js 
           ├─ package.json 
           └─ node_modules 
               └─ 依赖D 
               ├─ index.js 
               └─ package.json

可以发现,
这样的结构虽然解决了版本冲突、依赖隔离等问题,但却有几个致命的缺点:

  • 磁盘空间占用:每个依赖都会安装自己的依赖,导致了大量的重复,特别是在多个包共享同一依赖的场景下。
  • 深层嵌套问题:这种嵌套结构在文件系统中造成了非常长的路径,然而大多数 Windows 工具、实用程序和 shell 最多只能处理长达 260 个字符的文件和文件夹路径。一旦超过,安装脚本就会开始出错,而且无法再使用常规方法删除 node_modules 文件夹。相关 issue:github.com/nodejs/node…
  • 安装和更新缓慢:每次安装或更新依赖时,npm 需要处理和解析整个依赖树,过程非常缓慢。

npm3架构与yarn

为解决这些问题,npm 在第三个版本进行了重构:github.com/npm…

通过将依赖扁平化,尽可能地减少了重复的包版本,有效减少了项目的总体积,同时也避免了 npm 早期的深层嵌套问题。

在这里插入图片描述

扁平化结构如下:

在这里插入图片描述

代码结构:

node_modules 
└─ 依赖A  
    ├─ index.js 
    ├─ package.json 
    └─ node_modules 
└─ 依赖C   
    ├─ index.js 
    ├─ package.json 
    └─ node_modules 
└─ 依赖B 
    ├─ index.js 
    ├─ package.json 
    └─ node_modules 

node_modules下所有的依赖都会平铺到同一层级。由于require寻找包的机制,如果A和C都依赖了B,那么A和C在自己的node_modules中未找到依赖C的时候会向上寻找,并最终在与他们同级的node_modules中找到依赖包C。 这样就不会出现重复下载的情况。而且依赖层级嵌套也不会太深。因为没有重复的下载,所有的A和C都会寻找并依赖于同一个B包。自然也就解决了实例无法共享数据的问题


Yarn 的诞生与局限

Yarn 的诞生背景

2016 年,Facebook 团队面对 npm 在大型项目中的种种问题,如安装不确定性、性能低下等,推出了新的包管理工具 Yarn(Yet Another Resource Negotiator)。Yarn 在发布之初就展现出了显著的优势:

  1. 确定性安装:通过 yarn.lock 文件确保了在不同环境下安装的依赖版本完全一致
  2. 并行下载:利用并行下载提升了安装速度
  3. 离线模式:引入缓存机制,支持离线安装
  4. 更好的命令行界面:提供了更友好的命令行交互体验

这些改进使得 Yarn 迅速获得了开发者的青睐,成为了 npm 的有力竞争者。


Yarn 仍然存在的问题

然而,Yarn 虽然解决了 npm 的一些问题,但在核心设计上仍然沿用了与 npm 相似的依赖管理模式,因此存在一些根本性问题:

  1. 依赖存储效率问题
  • 仍然采用扁平化的 node_modules 结构
  • 不同项目的相同依赖包会被重复存储,造成磁盘空间浪费
  • monorepo 项目中,即使使用 workspace 功能,依赖重复问题依然存在
  1. 幽灵依赖(Phantom Dependencies)
    {
      "dependencies": {
        "express": "4.17.1"  // express 依赖了 body-parser
      }
    }
    
    • 由于扁平化处理,项目可以直接使用未声明在 package.json 中的依赖
    • 这种隐式依赖可能导致潜在的问题和不可预测的行为

文章后面会详细补充什么是幽灵依赖以及如何解决~

  1. 依赖管理的不确定性
    • 扁平化算法的复杂性可能导致依赖树的结构难以预测
    • 不同的安装顺序可能产生不同的 node_modules 结构

文章后面会举case详细补充什么是依赖管理的不确定性

  1. 安装性能

    # 在大型项目中,即使使用缓存
    yarn install  # 仍然需要大量的文件复制操作
    
    • 虽然有并行下载,但文件复制和链接操作仍然耗时
    • 大型项目的首次安装和清理重装仍然较慢
  2. 磁盘空间占用

    • 即使是小型项目,node_modules 目录也可能占用数百 MB 空间
    • 对于维护多个项目的开发者来说,磁盘空间消耗巨大

这些问题的存在,促使开发社区继续探索更好的解决方案。pnpm 的出现,通过创新的依赖管理方式,为这些问题提供了更优的解决方案:

  • 采用内容寻址存储,通过硬链接共享依赖
  • 使用符号链接创建严格的依赖结构
  • 避免依赖重复安装和幽灵依赖
  • 显著减少磁盘空间占用

这使得 pnpm 在包管理工具的演进中代表了一个重要的技术突破,为前端工程化带来了新的可能。

最后在详细介绍pnpm之前,我来给大家演示一下幽灵依赖👻和依赖结构的不确定性(lock文件产生的原因)

何为幽灵依赖

由于这个扁平化结构的特点,想必大家都遇到了这样的体验,自己明明就只安装了一个依赖包,打开node_modules文件夹一看,里面却有一大堆。
例如我们在终端执行:

npm init -y

npm i express -S

这时候我们打开 node_modules 文件夹,你会惊奇的发现:
在这里插入图片描述

在这里插入图片描述

我明明只安装了 express,怎么 node_modules 下会出现那么多包?其实很简单,那是因为 express 依赖了一些包,而依赖的这些包又会依赖其它包…npm 则是把这些包拍平了放到了 node_modules 下,这也就导致 node_modules 里出现了这么多包

这就衍生了一个问题:

假设: 引入依赖a,a依赖又依赖于b,逻辑上则结构就应该是:

> -node_module/a 
> -node_module/a/node_module/b

但是在扁平化展开后则变成了:

> -node_module/a > -node_module/b

这样说那岂不是…😈

在这里插入图片描述

把安装express的时候自动下载的body-parser拿出来测试一下嘿嘿😈😈

在这里插入图片描述

import bd from "body-parser"; 
console.log(bd); //成功输出了

在这里插入图片描述

这会带来什么后果和隐患呢?

  • express 在未来版本中移除或更换 body-parser 依赖时,你的项目将意外破损(直接崩了)
  • 你无法控制 body-parser 的具体版本,完全依赖于 express 的依赖声明

依赖结构的不确定性

这个怎么理解,为什么会产生这种问题呢?我们来仔细想想,加入有如下一种依赖结构:

在这里插入图片描述

foo包与bar包同时依赖了base64-js包的不同版本,由于同一目录下不能出现两个同名文件,所以这种情况下同一层级只能存在一个版本的包,另外一个版本还是要被嵌套依赖。

那么问题又来了,既然是要一个扁平化一个嵌套,那么执行npm/yarn install 的时候,通过扁平化处理之后:

究竟是这样呢?

在这里插入图片描述

还是这样:

在这里插入图片描述

答案:这两种结构都有可能

在这里插入图片描述

准确点说哪个版本的包被提升,取决于包的安装顺序! 取决于 foo 和 bar 在 package.json中的位置,如果 foo 声明在前面,那么就是前面的结构,否则是后面的结构

这就是为什么会产生依赖结构的不确定问题,也是 lock 文件诞生的原因,无论是package-lock.json(npm 5.x 才出现)还是yarn.lock,都是为了保证 install 之后都产生确定的node_modules结构。

因此,npm/yarn 本身还是存在扁平化算法复杂package非法访问的问题,影响性能和安全。

pnpm王牌登场 – 网状+平铺结构

pnpm (performant npm) 是一个快速、节省磁盘空间的包管理工具。它于 2017 年发布,是 npm 的替代品,专注于解决传统包管理工具存在的问题。

就这么简单,说白了它跟npmyarn没有区别,都是包管理工具。但它的独特之处在于:

  • 包安装速度极快
  • 磁盘空间利用非常高效

安装包速度快

在这里插入图片描述

从上图可以看出,pnpm的包安装速度明显快于其它包管理工具。那么它为什么会比其它包管理工具快呢?

在这里插入图片描述

我们来可以来看一下各自的安装流程:

npm / yarn :

在这里插入图片描述

  1. resolving:首先他们会解析依赖树,决定要fetch哪些安装包。
  2. fetching:安装去fetch依赖的tar包。这个阶段可以同时下载多个,来增加速度。
  3. wrting:然后解压包,根据文件构建出真正的依赖树,这个阶段需要大量文件IO操作。

pnpm :

在这里插入图片描述

上图是pnpm的安装流程,可以看到针对每个包的三个流程都是平行的,并行处理所以速度当然会快很多。不过pnpm会多一个阶段,就是通过链接组织起真正的依赖树目录结构。

依赖管理

pnpm使用的是npm 2.x类似的嵌套结构,同时使用.pnpm 以平铺的形式储存着所有的包。然后使用Store + Links和文件资源进行关联。

简单说pnpm把会包下载到一个公共目录,如果某个依赖在 sotre 目录中存在了话,那么就会直接从 store 目录里面去 hard-link,避免了二次安装带来的时间消耗,如果依赖在 store 目录里面不存在的话,就会去下载一次。通过Store + hard link的方式,使得项目中不存在NPM依赖地狱问题,从而完美解决了npm3+yarn中的包重复问题。

在这里插入图片描述

我们分别用npmpnpm来安装vite对比看一下:

npmpnpm
在这里插入图片描述
在这里插入图片描述
所有依赖包平铺在node_modules目录,包括直接依赖包以及其他次级依赖包node_modules目录下只有.pnpm和直接依赖包,没有其他次级依赖包
没有符号链接(软链接)直接依赖包的后面有符号链接(软链接)的标识

软链接 和 硬链接 机制

硬链接:pnpm 通过使用全局的 .pnpm-store 来存储下载的包,使用硬链接来重用存储在全局存储中的包文件,这样不同项目中相同的包无需重复下载,节约磁盘空间。
在这里插入图片描述


软链接:pnpm 将各类包的不同版本平铺在 node_modules/.pnpm 下,对于那些需要构建的包,它使用符号链接连接到存储在项目中的实际位置。这种方式使得包的安装非常快速,并且节约磁盘空间。

在这里插入图片描述

举个例子,项目中依赖了 A,这时候可以通过创建软链接,在 node_modules 根目录下创建 A 软链指向了 node_modules/.pnpm/A/node_modules/A。此时如果 A 依赖 B,pnpm 同样会把 B 放置在 .pnpm 中,A 同样可以通过 软链接依赖到 B,避免了嵌套过深的情况。


依赖处理方式:依赖包 —(软链接)— > .pnpm ----(硬链接) —> 全局的 Store

我们使用刚刚的express来举个🌰:

执行pnpm install :

在这里插入图片描述

  1. 打开 node_modules 可以看到,确实不是扁平化的了,依赖了 express,那 node_modules 下就只有 express,没有幽灵依赖
  2. 同时下面还有个 .pnpm 文件夹,展开 .pnpm 后可以看到,所有的依赖都在这里铺平了

在这里插入图片描述

  1. 所有的依赖都是从全局 store 硬连接到了 node_modules/.pnpm 下,然后包和包之间的依赖关系是通过软链接组织的
  2. .pnpm是个一个虚拟store(Virtual store),里面的依赖包硬链接到真实Store(Content-addressable store)中,真实Store才是依赖包文件真正的存储位置
  3. package.json中的依赖(比如express)通过软链接,指向.pnpm下对应的依赖包
  4. 每次pnpm安装先检查Store,如果已经存在,直接通过硬链接的形式连接到.pnpm;如果不存在,则先下载,然后再硬链接

类似如下的🌰:

node_modules
└── A // symlink to .pnpm/A@1.0.0/node_modules/A
└── B // symlink to .pnpm/B@1.0.0/node_modules/B
└── .pnpm
    ├── A@1.0.0
    │   └── node_modules
    │       └── A -> <store>/A
    │           ├── index.js
    │           └── package.json
    └── B@1.0.0
        └── node_modules
            └── B -> <store>/B
                ├── index.js
                └── package.json

node_modules 中的 A 和 B 两个目录会软连接到 .pnpm 这个目录下的真实依赖中,而这些真实依赖则是通过 hard link 存储到全局的 store 目录中。

在这里插入图片描述


对于store,你应该记住:

pnpm下载的依赖全部都存储到store中去了,store是pnpm在硬盘上的公共存储空间。

pnpm的store在Mac/linux中默认会设置到{home dir}>/.pnpm-store/v3;windows下会设置到当前盘符的根目录下。使用名为 .pnpm-store的文件夹名称。

项目中所有.pnpm/依赖名@版本号/node_modules/下的软连接都会连接到pnpm的store中去。

幽灵依赖产生的根本原因

然而就算使用 pnpm,幽灵依赖还是难以根除,我们不妨分析一下幽灵依赖产生的根本原因。

包管理工具的依赖解析机制

这就是前面介绍的平铺式带来的问题,这边就不重复讲述了。

第三方库历史问题

由于历史原因或开发者的疏忽,有些项目可能没有正确地声明所有直接使用的依赖。对于三方依赖,幽灵依赖已经被当做了默认的一种功能来使用,提 issue 修复的话,周期很长,对此 pnpm 也没有任何办法,只能做出妥协。

下面是 pnpm 的处理方式:

  • 对直接依赖严格管理:对于项目的直接依赖,pnpm 保持严格的依赖隔离,确保项目只能访问到它在package.json 中声明的依赖。

  • 对间接依赖妥协处理:考虑到一些第三方库可能依赖于未直接声明的包(幽灵依赖),pnpm 默认启用了 hoist 配置。这个配置会将一些间接依赖提升(hoist)到一个特殊的目录 node_modules/.pnpm/node_modules中。这样做的目的是在保持依赖隔离的同时,允许某些特殊情况下的间接依赖被访问。

在这里插入图片描述

JavaScript 模块解析策略

Node.js 的模块解析策略允许从当前文件夹的 node_modules 开始,向上遍历文件系统,直到找到所需模块。

这种解析策略,虽然提供了灵活性,也使得幽灵依赖更容易产生,因为它允许模块加载那些未直接声明在项目package.json 中的依赖。

综合来看,幽灵依赖在目前是无法根除的,只能通过一些额外的处理进行管控,比如 eslint 对幽灵依赖的检查规则、pnpm 的 hoist 配置等。

pnpm 项目的依赖治理方案

对于依赖治理,大概涉及到以下几个部分:

  • 冗余依赖治理:例如遗留的未使用依赖重复声明的依赖过时的依赖版本,导致 package.json 愈发混乱。
  • 重叠依赖治理:例如 monorepo 项目中根目录和子项目的重复依赖,加大了 package.json 的管理成本,同一依赖的多个版本并存,依赖版本冲突。

冗余依赖治理

例如遗留的未使用依赖重复声明的依赖过时的依赖版本

对于冗余的情况,可以按照如下顺序检查:

  1. 执行 pnpm why <package-name>,用来找出项目中一个特定的包被谁所依赖,给出包的依赖来源。
  2. 全局搜索包名,检查是否有被引入。
  3. 了解包的作用,判断项目中是否存在包的引用。
  4. 删除包,执行 pnpm i 后,分别运行、打包项目,查看是否有明显问题。

按照顺序执行完毕后,仍然可能存在问题,这是没法完全避免的,可以进一步通过测试进行排查。

在这里插入图片描述

重叠依赖治理

对于 monorepo 而言,依赖的管理就比较复杂了,这边可以通过人肉+脚本的方式进行治理。

为方便识别重叠依赖,可以编写一个脚本,遍历子项目中的 package.json 将与根目录重叠的依赖进行输出:

import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import chalk from 'chalk'; // 引入 chalk

// 获取当前文件的目录路径,确保脚本可以在不同环境下正确执行
const __dirname = path.dirname(fileURLToPath(import.meta.url));

// 修改后的读取 package.json 文件函数保持不变
function readPackageJson(filePath) {
  try {
    const jsonData = fs.readFileSync(filePath, 'utf8');
    return JSON.parse(jsonData);
  } catch (error) {
    console.error(`读取文件失败: ${filePath}`, error);
    return null;
  }
}

// 修改后的比较依赖函数保持不变
function compareDependencies(rootDeps, childDeps, depType, childName) {
  const overlaps = [];
  for (const [dep, version] of Object.entries(childDeps)) {
    if (rootDeps[dep]) {
      const versionCompare = (rootDeps[dep] === version)
      // 如果子项目中的依赖在根目录中也存在,则记录下来
      overlaps.push(`${dep}: ${chalk.blueBright(version)} (在根目录中为: ${chalk.blueBright(rootDeps[dep])}) ${versionCompare ? chalk.green('✔') : chalk.red('✘')}`);
    }
  }
  return {
    overlaps: overlaps.length > 0 ? `${chalk.greenBright('- 重叠的',depType)}\n` + overlaps.join('\n') + '\n\n' : '',
  };
}

function main() {
  const rootPackageJsonPath = path.join(__dirname, 'package.json');
  const rootPackageJson = readPackageJson(rootPackageJsonPath);
  if (!rootPackageJson) {
    console.error('无法读取根目录的 package.json 文件');
    return;
  }

  // 修改输出为终端输出,使用 chalk 增加颜色
  console.log(chalk.bold('📖 依赖分析报告\n'));

  const packagesDir = path.join(__dirname, 'packages');
  const childDirs = fs.readdirSync(packagesDir).filter(child => fs.statSync(path.join(packagesDir, child)).isDirectory());

  for (const child of childDirs) {
    const childPackageJsonPath = path.join(packagesDir, child, 'package.json');
    const childPackageJson = readPackageJson(childPackageJsonPath);
    if (childPackageJson) {
      console.log(chalk.bold(`🟢 子项目 ${child}`));
      ['dependencies', 'devDependencies', 'peerDependencies'].forEach(depType => {
        const { overlaps } = compareDependencies(
          rootPackageJson[depType] || {},
          childPackageJson[depType] || {},
          depType,
          child
        );
        console.log(overlaps);
      });
    }
  }
}

main();

核心流程就是先遍历所有子项目的 package.json ,然后通过compareDependencies方法检查子项目的依赖是否在根目录存在,然后对重叠的依赖进行版本一致性检查,最后对 分别处理不同类型的依赖(dependencies / devDependencies / peerDependencies

执行效果如下:

📖 依赖分析报告

🟢 子项目 A
- 重叠的 dependencies
@babel/runtime-corejs3: ^7.14.0 (在根目录中为: ^7.14.0) ✔
……

- 重叠的 devDependencies
@commitlint/cli: ^13.1.0 (在根目录中为: ^13.1.0) ✔
@commitlint/config-conventional: ^13.1.0 (在根目录中为: ^13.1.0) ✔
……

🟢 子项目 B

- 重叠的 devDependencies
typescript: ^4.4.0 (在根目录中为: ^4.3.5)zx: ^4.2.0 (在根目录中为: ^4.2.0)chalk: ^4.1.0 (在根目录中为: ^4.1.0)

通过这种方式我们就可以有目的性的去逐个检查依赖,依据一种合理的 monorepo 依赖管理模式进行处理,下面是一种合适的处理规则:

  • 将共享的开发时依赖移至根目录的 package.json,如 jest、eslint、lint-stage。
  • 对于需要特定版本以保证兼容性的依赖,考虑使用 resolutions 字段强制解析为特定版本。
  • 为需要发包的工具、类库提供 peerDependencies 字段。
  • 对于运行时依赖,如果所有子项目都有依赖,将删除子项目中的声明,提升至根目录,同时在需要发包的工具、类库的 peerDependencies 中声明相关的依赖。
  • 发包时,通过调用脚本将目标子项目中的 peerDependencies 内容转移至 dependicies

最后

虽然 pnpm 的优势非常明显,但目前 pnpm 的生态还在成长阶段,一些功能还没法在网络上找到最佳实践,这需要一定的时间去沉淀,但经过权衡,拥抱 pnpm 无疑是一个非常好的选择!

最后,如果这篇文章对你有帮助,可以给作者点赞关注支持一波~

在这里插入图片描述

参考资料:

https://pnpm.io/zh/blog/2020/05/27/flat-node-modules-is-not-the-only-way

https://juejin.cn/post/7358267939441950720#heading-25

https://juejin.cn/post/7053340250210795557#heading-4

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2300410.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

137,【4】 buuctf web [SCTF2019]Flag Shop

进入靶场 都点击看看 发现点击work会增加&#xffe5; 但肯定不能一直点下去 抓包看看 这看起来是一个 JWT&#xff08;JSON Web Token&#xff09;字符串。JWT 通常由三部分组成&#xff0c;通过点&#xff08;.&#xff09;分隔&#xff0c;分别是头部&#xff08;Header&…

【c++】c++内存管理

目录 c和c的内存分布回顾C语言动态管理内存的方式malloccallocreallocfree C动态管理内存的方式new和deleteoperator new和operator delete定位new c和c的内存分布 回顾C语言动态管理内存的方式 malloc void* malloc (size_t size);malloc可以在堆上开辟指定内存的空间&#…

EtherNet/IP转Modbus TCP:新能源风电监控与分析实用案例

EtherNet/IP转Modbus TCP&#xff1a;新能源风电监控与分析实用案例 一、案例背景 在某新能源汽车电池生产线上&#xff0c;需要将采用EtherNet/IP协议的电池检测设备与采用ProfiNet协议的生产线控制系统进行集成&#xff0c;以实现对电池生产过程的全面监控和数据采集。 二、…

数字电路-基础逻辑门实验

基础逻辑门是数字电路设计的核心元件&#xff0c;它们执行的是基本的逻辑运算。通过这些基本运算&#xff0c;可以构建出更为复杂的逻辑功能。常见的基础逻辑门包括与门&#xff08;AND&#xff09;、或门&#xff08;OR&#xff09;、非门&#xff08;NOT&#xff09;、异或门…

国产编辑器EverEdit - 如虎添翼的功能:快速选择

1 快速选择 1.1 应用场景 快速选择适用于批量选择和修改的场景&#xff0c;比如&#xff1a;变量改名。 1.2 使用方法 1.2.1 逐项快速选择 将光标放置在单词前或单词中&#xff0c;选择主菜单查找 -> 快速选择 -> 快速选择或使用快捷键Ctrl D 注&#xff1a;光标放…

国内外网络安全政策动态(2025年1月)

▶︎ 1.国家互联网信息办公室发布《个人信息出境个人信息保护认证办法&#xff08;征求意见稿&#xff09;》 1月3日&#xff0c;国家互联网信息办公室发布《个人信息出境个人信息保护认证办法&#xff08;征求意见稿&#xff09;》。根据《意见稿》&#xff0c;个人信息出境个…

68页PDF | 数据安全总体解决方案:从数据管理方法论到落地实践的全方位指南(附下载)

一、前言 这份报告旨在应对数字化转型过程中数据安全面临的挑战&#xff0c;并提供全面的管理与技术体系建设框架。报告首先分析了数字化社会的发展背景&#xff0c;强调了数据安全在国家安全层面的重要性&#xff0c;并指出数据安全风险的来源和防护措施。接着&#xff0c;报…

AI大模型的文本流如何持续吐到前端,实时通信的技术 SSE(Server-Sent Events) 认知

写在前面 没接触过 SSE&#xff08;Server-Sent Events&#xff09;&#xff0c;AI大模型出来之后&#xff0c;一直以为文本流是用 WebSocket 做的偶然看到返回到报文格式是 text/event-stream,所以简单认知&#xff0c;整理笔记博文内容涉及 SSE 认知&#xff0c;以及对应的 D…

Electron:使用electron-react-boilerplate创建一个react + electron的项目

使用 electron-react-boilerplate git clone --depth 1 --branch main https://github.com/electron-react-boilerplate/electron-react-boilerplate.git your-project-name cd your-project-name npm install npm start 安装不成功 在根目录加上 .npmrc文件 内容为 electron_…

Spring Boot三:Springboot自动装配原理

精心整理了最新的面试资料&#xff0c;有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 原理初探 pom.xml 核心依赖在父工程中 spring-boot-dependencies所有的jar包都在这里管理 我们在写或者引入一些依赖的时候&#xff0c;不需要指定版本 启动器 <…

2024 年 CSDN 博客之星年度评选:技术创作与影响力的碰撞(统计时间2025-02-17 11:06:06)

摘要&#xff1a;在技术的海洋里&#xff0c;每一位博主都像是一座独特的灯塔&#xff0c;用自己创作的光芒照亮他人前行的道路。2024 年 CSDN 博客之星年度评选活动&#xff0c;正是对这些灯塔的一次盛大检阅&#xff0c;让我们看到了众多优秀博主在技术创作领域的卓越表现以及…

Java零基础入门笔记:(3)程序控制

前言 本笔记是学习狂神的java教程&#xff0c;建议配合视频&#xff0c;学习体验更佳。 【狂神说Java】Java零基础学习视频通俗易懂_哔哩哔哩_bilibili Scanner对象 之前我们学的基本语法中我们并没有实现程序和人的交互&#xff0c;但是Java给我们提供了这样一个工具类&…

后端生成二维码,前端请求接口生成二维码并展示,且多个参数后边的参数没有正常传输问题处理

一、后端代码 1、controller GetMapping("/generateQRCode/{url}")ApiOperation(value "生成url链接二维码",notes "生成url链接二维码")public JsonResult<NewsQRCodeVo> generateQRCode(PathVariable String url,HttpServletRespons…

(8/100)每日小游戏平台系列

项目地址位于&#xff1a;小游戏导航 新增一个打地鼠游戏&#xff01; 打地鼠&#xff08;Whack-a-Mole&#xff09;是一款经典的休闲游戏&#xff0c;玩家需要点击随机出现的地鼠&#xff0c;以获取分数。游戏时间有限&#xff0c;玩家需要在规定时间内尽可能多地击中地鼠&am…

[Python人工智能] 五十.PyTorch入门 (5)快速搭建神经网络及模型保存

从本专栏开始,作者正式研究Python深度学习、神经网络及人工智能相关知识。前文讲解PyTorch构建分类神经网络。这篇文章将介绍如何利用PyTorch快速构建神经网络,之前的代码比较复杂,通过自定义Net类实现,本文通过Torch函数定义神经网络。前面我们的Python人工智能主要以Tens…

SpringBoot+Vue+数据可视化的动漫妆造服务平台(程序+论文+讲解+安装+调试+售后等)

感兴趣的可以先收藏起来&#xff0c;还有大家在毕设选题&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;我会一一回复&#xff0c;希望帮助更多的人。 系统介绍 在当今数字化高速发展的时代&#xff0c;动漫产业迎来了前所未有的繁荣&#xff0c;动漫…

基于web的留守儿童网站的设计与实现

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

OpenCV中的边缘检测

边缘检测是图像处理和计算机视觉中的关键技术之一&#xff0c;旨在识别图像中像素强度发生显著变化的区域&#xff0c;这些区域通常对应于物体的边界或轮廓。边缘检测在机器视觉中具有重要的需求背景&#xff0c;主要体现在以下几个方面&#xff1a; 图像分割&#xff1a;边缘…

家里WiFi信号穿墙后信号太差怎么处理?

一、首先在调制解调器&#xff08;俗称&#xff1a;猫&#xff09;测试网速&#xff0c;网速达不到联系运营商&#xff1b; 二、网线影响不大&#xff0c;5类网线跑500M完全没问题&#xff1b; 三、可以在卧室增加辅助路由器&#xff08;例如小米AX系列&#xff09;90~200元区…

【前端学习笔记】Webpack

1.介绍 Webpack 是一个现代 JavaScript 应用程序的静态模块打包工具&#xff0c;它将 JavaScript、CSS、图片、字体等资源文件打包成一个或多个静态文件&#xff0c;以供浏览器使用。当 webpack 处理应用程序时&#xff0c;它会在内部从一个或多个入口点构建一个 依赖图(depend…