研究一下「pnpm」这个神奇的包管理工具

news2024/11/16 19:49:22

最近搬砖 🧱 在搞前端项目部署优化 🎡,大部分项目的包管理工具都已经从 npm/yarn 替换成了 pnpm,整体来看无论是在 install 或是 build 阶段都提速了不少 🚀,借此时机,做个总结!🤔 

pnpm 简介

从 pnpm 官网 的定义来 👀「pnpm 是一个快速的,节省磁盘空间的包管理工具」。它用于管理 JavaScript 依赖包,类似于 npm 或 yarn,旨在解决传统包管理工具在安装和升级依赖时的一些常见问题,例如:占用大量磁盘空间、重复下载依赖项等...  🤕

「pnpm」的主要特点之一是它使用一种称为「虚拟化节点模块」的技术来管理依赖项。它通过在单个磁盘位置存储依赖项的多个版本来减少磁盘占用空间,并通过「软连接 — 符号链接」将它们正确引用到项目中,这种方法还可以加快安装和更新依赖项的速度!🐂🍺

下图是目前 pnpm 官网 benchmarks 将 npm、pnpm、Yarn Classic 和 Yarn PnP 的性能做了对比 🚴‍♂️,从图中可以看出在一些综合场景下 pnpm 比  npm/yarn 快了大概 2-3 倍!🎉

依赖包管理工具的历史

以史为鉴,可以知兴替。在学习 pnpm 之前,我们先了解一下依赖管理工具的历史!🤔️

npm

npm(Node Package Manager, 2010年)是用于 Node.js 的默认包管理器。它可以使开发人员轻松地共享和管理 JavaScript 包。npm 使用「package.json」文件来定义项目的依赖关系,并提供命令行工具来安装、更新和删除依赖项。

通过 npm 生成的 node_modules 结构通常是一个「扁平化的目录结构」,其中包含项目的所有依赖项。当您使用 npm 安装项目的依赖项时,相关的包将直接安装到项目的 node_modules 目录中。例如,假设你的项目依赖于包 A 和包 B,它们的安装目录结构可能如下所示:

Project
└── node_modules
    ├── package-A
    └── package-B

// node_modules 结构是扁平化的,但每个依赖项的包内部结构可能是嵌套的

但这种「扁平化的目录结构」存在一些「缺点」🤕

  1. 冗余的依赖项:由于每个依赖项都被直接放置在 node_modules 目录下,如果多个依赖项依赖于同一个包的不同版本,那么这个包将会在 node_modules 目录下重复出现多次,造成了冗余的存储 🙁
    Project
    └── node_modules
        ├── package-A
        │   └── node_modules
        │       └── shared-package@1.0.0
        └── package-B
            └── node_modules
                └── shared-package@2.0.0
  2. 磁盘空间占用:由于每个依赖项都被直接放置在 node_modules 目录下,即使同一个依赖项在不同的包中重复出现,也会占用额外的磁盘空间 🙁
    Project
    └── node_modules
        ├── package-A
        │   └── node_modules
        │       └── shared-package@1.0.0
        └── package-B
            └── node_modules
                └── shared-package@1.0.0
  3. 安装时间延长:由于扁平化结构中的所有依赖项都位于同一层级,安装时需要递归解析和安装所有依赖项。当项目具有大量依赖项或存在大量依赖项冲突时,按包顺序逐个执行的「串行安装」,会导致安装时间较长 🙁

npm v3

npm v3(2015年)是 npm 包管理器的一个重要里程碑,引入了一些重大的变化和改进!🎉

  1. 扁平化依赖:npm v3 引入了「扁平化依赖」的概念(npm v5 做了完善和优化),即将依赖项的多个版本共享在一个统一的层次结构中。这样可以避免传统的嵌套依赖结构,减少了冗余和冲突,从而降低了项目的磁盘空间占用和安装时间。
    Project
    └── node_modules
        ├── package-A
        ├── package-B
        ├── shared-package@1.0.0
        └── shared-package@2.0.0 // npm v5 才能做到
    
  2. 并行安装:npm v3 引入了「并行安装」的能力,可以同时下载和安装多个依赖项,从而加快整体的安装速度,这对于大型项目或拥有大量依赖项的项目特别有益。

当然,npm v3 也存在一定的「缺点」🤕

  1. npm v3 这种「扁平化依赖」的思想虽然有效避免了冗余的依赖项,但也使得项目/包可以访问到并不依赖的包,可能产生安全问题 ➡️「幽灵依赖」。
  2. 不同包依赖同一个包的版本不同时,多个版本的包只能有一个被提升上来,其余版本的包会嵌套安装到各自的依赖当中,哪个版本的包被提升,依赖于包的安装顺序。

第二条缺点可能不是很好理解,我们来举个 🌰,假设有以下依赖关系:

  • 包 A 依赖于 shared-package@1.0.0
  • 包 B 依赖于 shared-package@2.0.0
  • 包 C 依赖于 包 A 和 包 B

在安装过程中,如果先安装了包 A,那么 shared-package@1.0.0 将被提升到顶层的 node_modules 目录。然后,在安装包 C 的过程中,会发现它依赖于包 A 和包 B,但由于 shared-package@1.0.0 已经被提升,所以 shared-package@2.0.0 会被嵌套安装在包 B 的依赖项中,而不是被提升到顶层。

相反,如果先安装了包 B,那么 shared-package@2.0.0 将被提升到顶层的 node_modules 目录。然后,在安装包 C 的过程中,发现它依赖于包 A 和包 B,但由于 shared-package@2.0.0 已经被提升,所以 shared-package@1.0.0 会被嵌套安装在包 A 的依赖项中。

注:npm v3 作为一个过渡版本,在后续的 npm 版本中进行了进一步的改进和优化。npm v5 引入了更高级的依赖项解析和管理策略,更好地处理了依赖项冗余和安装效率的问题。

yarn

Yarn(2016年)是由 Facebook、Google、Expo 和 Tilde 等公司合作开发的 JavaScript 包管理器。它旨在改进 npm 的性能和可靠性,并引入了一些新功能,如并行安装、版本锁定和离线模式!🎉

下面是 Yarn 的一些优点和解决的问题:

  1. 离线模式:Yarn 可以在离线环境下工作,它会缓存已经下载的依赖项,以便在没有网络连接时重新使用。这对于团队合作、CI/CD 构建和在低速或不稳定的网络环境中工作的开发人员来说特别有用。
  2. 并行安装:Yarn 使用并行安装的方式,可以同时下载多个依赖项,从而提高安装速度。这对于具有大量依赖项或需要频繁重复安装的项目来说,可以显著减少等待时间。
  3. 确定性安装:Yarn 确保在不同的环境中安装相同的依赖项版本,从而避免了由于依赖项版本的差异导致的构建或运行时错误。Yarn 使用 「yarn.lock」 文件来记录依赖项的确切版本,以确保跨团队和环境的一致性。
  4. 优化的网络请求:Yarn 使用了更高效的网络请求算法,可以减少对网络的负载和响应时间。它能够智能地利用缓存和并行下载来最大限度地提高依赖项的获取速度。

这也是为什么当时 yarn 在安装速度上比 npm 有显著提高。需要注意的是,尽管 Yarn 带来了这些优点和改进,npm 也在后续的版本中逐渐改进自身,引入了类似的功能和优化。比如确定性安装这一点,npm 的 package-lock.json 文件就是在 npm v5.0.0 版本中引入的!🎉

Why pnpm?

了解完包管理工具的历史和原理,我们再来看看 pnpm 到底有什么特别之处,为什么 pnpm 是所谓的「快速的,节省磁盘空间的包管理工具」,它是怎么做到的?🤔

store

pnpm 有一个 store 的概念(为了方便记忆,可以联想成 Vuex 的 store,全局状态管理),store 目录内部使用「基于内容寻址」的文件系统来存储磁盘上所有的文件。

基于内容寻址是一种文件系统的设计原则,以文件内容的哈希值作为文件的唯一标识符,并将文件存储在以哈希值命名的目录中。这样的设计使得相同内容的文件只会存储一次,避免了重复存储相同文件的问题。

在 pnpm 的 store 目录中,每个文件的名称都是其内容的哈希值,因此同一个文件无论在多个项目中使用,都只会被存储一次。当安装或更新依赖项时,pnpm 会检查 store 目录中是否已经存在所需的文件,如果存在则直接复用,避免了重复的下载和存储。

这种基于内容寻址的文件系统设计可以带来一些好处:

  1. 磁盘空间的节省:相同的文件只会存储一次,避免了冗余的存储,尤其是在多个项目共享依赖项时。
  2. 快速的依赖项安装:由于文件已经存在于 store 中,所以安装依赖项时可以直接使用已有的文件,而无需下载和解压,从而提高了安装速度。
  3. 安全性:基于内容寻址的文件系统使用哈希值来标识文件,这意味着文件内容的完整性可以通过哈希校验来验证,提供了一定的数据完整性保护。

通过 store 目录和基于内容寻址的文件系统,pnpm 能够实现依赖项的共享和重复使用,减少磁盘空间占用,并提高依赖项的安装和管理效率。

pnpm install 过程中,我们会看到如下的信息:其中的 content-addressable store 就是以上提到的 store。Mac 中默认会将 store 设置到 {home dir}>/.pnpm-store/v3

link

了解 pnpm 原理之前,我们还需要了解一下「硬连接」和「软连接」🤔

硬链接 hard link

硬链接就是同一个文件的一个或多个文件名,它所引用的是文件的物理数据而不是文件在文件结构中的位置,使得用户可以通过不同的路径引用方式去找到某个文件。pnpm 会在全局的 store 目录里存储项目 node_modules 文件的硬连接。但是,硬链接只能用于文件不能用于目录(可能导致目录循环)。

是不是不太好理解?举个 🌰:

假设有一个名为 "file.txt" 的文件,它有一些数据块 A、B 和 C。当你创建一个硬链接 "link.txt" 指向 "file.txt" 时,实际上只是创建了一个新的文件条目 "link.txt",它指向相同的数据块 A、B 和 C。这样,"file.txt" 和 "link.txt" 都指向相同的数据,它们是相互独立但内容相同的文件。

如果你修改了 "file.txt" 中的内容,那么 "link.txt" 中的内容也会发生相应的改变,因为它们实际上是指向同一组数据块。同样地,如果你删除 "file.txt","link.txt" 仍然可以访问相同的数据块,因为它们共享相同的内容。

硬链接的特点是,无论有多少个硬链接指向相同的数据块,它们都被视为相互独立的完整文件副本。因此,硬链接不会占用额外的磁盘空间,因为它们共享相同的数据块。

软链接 symbolic link

软链接,又称为符号链接,是一个指向另一个文件或目录的引用,类似于创建一个快捷方式。与硬链接不同,软链接创建的链接文件保存的是被链接文件的「路径信息」,而不是实际的文件内容或索引节点。当访问软链接时,操作系统会跟随链接并转到被链接的文件或目录。

这个也不太好理解?再举个 🌰:

假设你有两个文件夹:FolderA 和 FolderB

FolderA 中有一个名为 "Documents" 的文件夹,里面存放着你的文档文件。现在,你希望在 FolderB 中也能访问这些文档文件,但不想复制它们。

你可以创建一个软链接(符号链接)来实现这个目的。在 FolderB 中创建一个名为 "Documents" 的软链接,它指向 FolderA 中的 "Documents" 文件夹。这个软链接就像一个指向实际文件夹的快捷方式。

现在,当你在 FolderB 中访问 "Documents" 文件夹时,实际上是通过软链接跳转到 FolderA 中的 "Documents" 文件夹。你可以查看、编辑或操作那些文档文件,就像它们实际存在于 FolderB 中一样。

如果你在 FolderA 的 "Documents" 文件夹中添加、删除或修改文件,这些变化也会反映在 FolderB 的 "Documents" 软链接中,因为它们指向同一个文件夹。

硬链接 vs 软连接

如果你还是分不清的话,你就记:硬连接就是那个 "link.txt";软连接就是路径(快捷方式) 🤷‍♂️

.pnpm

.pnpm/ 为虚拟磁盘目录,它以平铺的形式储存着所有的包。pnpm 使用「软链接 + 平铺」目录结合的方式来构建一个嵌套结构:

软连接

当我们在项目中安装依赖,执行 pnpm add f 后,项目中的 node_modules 目录为:

node_modules
├── .pnpm
└── f // 这是一个软连接

node_modules 中只有一个叫 .pnpm 的文件夹以及一个叫做 f 的软链接。 f 是此项目/应用必须拥有访问权限的包,在访问 node_modules 中的 f 时,会形成一个到 .pnpm 中的软链接,链接到的文件是从全局  store 硬链接过来的。

node_modules
├── f
└── .pnpm
       └── f@1.0.0
             └── node_modules
                   └── f -> <store>/f

由于硬链接机制,每次安装依赖的时候,如果有项目/包都用到相同的依赖,那么这个依赖实际上最优情况(即版本相同)只需要安装一次,避免了二次安装带来的消耗。因此每个依赖包寻址都要经过这三层结构:

node_modules/f // 在 node_modules 文件夹寻找依赖,并遵循就近原则

—> 软链接 node_modules/.pnpm/f@1.0.0/node_modules/f // 解决一个项目内的代码重复引用问题

—> 硬链接 ~/.pnpm-store/v3/files/00/xxxxxx // 解决项目间的包重复拷贝问题

平铺

pnpm 包的依赖项与依赖包的位置位于同一目录级别:f 这个依赖的内部相关依赖会被平铺到 .pnpm/f@1.0.0/node_modules/ 这个目录下面,而不是 .pnpm/f@1.0.0/node_modules/f/node_modules/

node_modules
├── f -> ./.pnpm/f@1.0.0/node_modules/f
└── .pnpm
       ├── b@1.0.0
       │    └── node_modules
       │          └── b -> <store>/b
       └── f@1.0.0
             └── node_modules
                   ├── f -> <store>/f
                   └── b -> ../../b@1.0.0/node_modules/b

这个平铺的结构使得所有被提升的包都可以访问。不但保留了包之间的相互隔离,而且避免了创建的嵌套 node_modules 引起的长路径问题。

比如,添加 q@2.0.0 作为  b 和  f 的依赖项,即使层级现在更深(f > b > q),但目录深度没有发生变化:

node_modules
├── f -> ./.pnpm/f@1.0.0/node_modules/f
└── .pnpm
      ├── b@1.0.0
      │    └── node_modules
      │          ├── b -> <store>/b
      │          └── q -> ../../q@2.0.0/node_modules/q
      ├── f@1.0.0
      │    └── node_modules
      │          ├── f -> <store>/f
      │          ├── b -> ../../b@1.0.0/node_modules/b
      │          └── q -> ../../q@2.0.0/node_modules/q
      └── q@2.0.0
            └── node_modules
                  └── q -> <store>/q

解析模块时,当 f@1.0.0 需要  b 时,Node 不会使用在  f@1.0.0/node_modules/b 的  b,而是其实际位置 b@1.0.0/node_modules/b。 因此,b 也可以解析其在  b@1.0.0/node_modules 中的依赖项。

同级依赖 peer dependencies

pnpm 的一个重要特性是确保一个 package 的特定版本只有一组依赖项,但对于具有同级依赖(peer dependencies)的 package,它允许安装多个不同版本的 peer dependencies,以满足不同版本要求的兼容性需求。这样既能节省磁盘空间,又能满足依赖关系的正确性。

同级依赖 peer dependencies?了解,举个 🌰:

在你的项目中,你安装了 React 和 React-DOM 作为主要的依赖项。然后,你决定使用一个名为 "react-router" 的路由库来实现应用程序的导航功能。你使用 pnpm 安装了 "react-router",虽然 "react-router" 它依赖于 React 和 React-DOM,但 "react-router" 并不直接将 React 和 React-DOM 作为自己的依赖项进行安装,而是将它们标记为同级依赖(peer dependencies)。这意味着 "react-router" 期望项目的根目录中已经安装了与其所需的 React 和 React-DOM 版本匹配的依赖项。它不会在自己的依赖树中直接安装 React 和 React-DOM,而是依赖于项目根目录中已经安装的版本。通过这种方式,"react-router" 和主项目共享同一个 React 和 React-DOM 的实例,确保它们使用的是相同版本的库。这有助于避免不同版本之间的冲突和兼容性问题。

对于定义了 peer dependencies 的包来说,意味着为 peer dependencies 内容是敏感的,也就是说对于不同的 peer dependencies ,这个包可能拥有不同的表现,因此  pnpm 针对不同的 peer dependencies 环境,可能对同一个包创建多份拷贝。

明白,举个 🌰:

比如包 f 同级依赖了 b 与 c ,我们在  Monorepo 环境两个 Package A 和  Package B 下分别安装不同版本的 b 和 c,Package A 安装的是 b@1.0.0+c@1.0.0,Package B 安装的是 b@1.0.0+c@1.1.0,则其在 .pnpm 中的结构为:

node_modules
└── .pnpm
     ├── f@1.0.0_b@1.0.0+c@1.0.0
     │     └── node_modules
     │           ├── f
     │           ├── b -> ../../b@1.0.0/node_modules/b
     │           ├── c -> ../../c@1.0.0/node_modules/c
     │           ├── u -> ../../u@1.0.0/node_modules/u
     │           └── p -> ../../p@1.0.0/node_modules/p
     ├── f@1.0.0_b@1.0.0+c@1.1.0
     │     └── node_modules
     │           ├── f
     │           ├── b -> ../../b@1.0.0/node_modules/b
     │           ├── c -> ../../c@1.1.0/node_modules/c
     │           ├── u -> ../../u@1.0.0/node_modules/u
     │           └── p -> ../../p@1.0.0/node_modules/p
     ├── b@1.0.0
     ├── c@1.0.0
     ├── c@1.1.0
     ├── u@1.0.0
     ├── p@1.0.0

可以看到,安装了两个相同版本的 f ,对应相同的软链接,但却分别拥有不同的名称: f@1.0.0_b@1.0.0+c@1.0.0 、 f@1.0.0_b@1.0.0+c@1.1.0 。这也是  pnpm 规则严格的体现:任何包都不应该有全局副作用。

总结

  1. pnpm 使用软链接和硬链接的组合,同时采用了平铺的目录结构。这意味着所有的包都被存储在一级子目录下,并且依赖关系使用软链接来连接。这样的设计有助于节省磁盘空间和提高安装速度。

  2. pnpm 的平铺结构确保所有的包都在一级子目录下,这与传统的 npm 文件结构相同。每个包只能访问自身的依赖项,而不会直接访问其他包的依赖项。这种结构的好处是每个包都有自己的上下文环境,避免了潜在的冲突和版本问题。

  3. 在 pnpm 中,同一个包的不同版本会分别存储,并且不会互相影响。每个版本的包被拉平后都会保留在存储中,而不会被替换或删除。这样做是为了确保不同项目对于特定版本的依赖项的一致性,即使其他项目可能依赖于不同的版本。

综合来说,pnpm 使用软链接和硬链接的组合来优化依赖项的共享和存储,采用平铺的目录结构来确保每个包的上下文隔离,同时保留不同版本的包以维持依赖项的一致性。这些设计选择有助于减少磁盘空间占用,提高安装速度,并提供更可靠的依赖项管理。

End 🎉

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

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

相关文章

TypeScript 中【类型断言】得使用方法

类型断言的概念 有些时候开发者比TS本身更清楚当前的类型是什么&#xff0c;可以使用断言&#xff08;as&#xff09;让类型更加精确和具体。 类型断言&#xff08;Type Assertion&#xff09;表示可以用来手动指定一个值的类型。 类型断言语法&#xff1a; 值 as 类型 或 <…

vue3 实现多层级列表

文章目录 需求背景解决效果index.vue视频效果 需求背景 需要在统一个列表下&#xff0c;实现商品和规格得管理和联动 解决效果 index.vue <!--/*** author: liuk* date: 2023/7/7* describe: 商品列表 */--> <template><div class"container">&…

textarea自适应高度二——(设置隐藏div获取高度和仿element-ui组件)

文章目录 前言一、通过隐藏div的方式来设置文本域自适应高度1. 新增一个文本域样式一个的dom&#xff0c;但是里面的textarea改为div2. 隐藏div的class3.设置文本域高度的方法 二、仿element-ui组件设置textarea自适应高度1.element-ui中自适应效果2. 看源码&#xff0c;盘逻辑…

病毒专题丨 plugx病毒

一、病毒简述 之前分析了一下&#xff0c;分析的较为简单&#xff0c;这次又详细分析了一下。 文件名称 00fbfaf36114d3ff9e2c43885341f1c02fade82b49d1cf451bc756d992c84b06 文件格式 RAR 文件类型(Magic) RAR archive data, v5 文件大小 157.74KB SHA256 00fbfaf36114d3ff9e…

【编程中的数学】:冰雹猜想

今天和大家分享一个令人着迷的数学谜题——冰雹猜想。这个谜题曾在1976年引起轰动&#xff0c;当时《华盛顿邮报》以头版头条刊登了一篇关于它的报道。让我们一起探索这个数学游戏的奥秘。 70年代中期&#xff0c;美国一所名牌大学的校园内兴起了一种数学游戏&#xff0c;这个游…

微信小程序使用vant时间选择器二次封装成自定义区间时间选择

目录 1.引入vant组件库 2.wxml页面 3.js页面 1.引入vant组件库 1.安装vant # 通过 npm 安装 npm i vant/weapp -S --production # 通过 yarn 安装 yarn add vant/weapp --production # 安装 0.x 版本 npm i vant-weapp -S --production 2.将 app.json 中的 "style&quo…

2-需求

目录 1.需求的定义 1.1.用户需求 1.2.软件需求 PS&#xff1a;软件需求规格说明书 2.为什么有需求&#xff1f; PS&#xff1a;为什么需求对软件测试人员如此重要&#xff1f; 3.测试人员眼里的需求 4.如何深入了解需求&#xff1f; 4.1.参加需求评审会议 4.2.查阅文…

数据结构初阶--二叉树OJ1

目录 二叉树的最大深度思路分析代码实现 相同的树思路分析代码实现 单值二叉树思路分析代码实现 二叉树的前序遍历思路分析代码实现 翻转二叉树思路分析代码实现 对称二叉树思路分析代码实现 另一棵树的子树思路分析代码实现 二叉树的最大深度 先来看题目描述 思路分析 题目…

QT学习—串口LED小项目

前言——博主刚开始接触QT&#xff0c;本文参考博主嵌入式大杂烩的一篇博文易懂 | 手把手教你编写你的第一个上位机&#xff0c;初步学习一下QT开发。 文章目录 一、QT安装二、新建工程三、创建上位机界面3.1 修改控件名3.2 添加槽函数 四、上位机程序打包五、上位机测试六、总…

不要用 in + 子查询

前两天我的 VIP 用户向我抛出了一个 SQL 问题&#xff0c;他的 MySQL 是 8.x版本&#xff1a; 大概意思如下 sql &#xff1a; select * from A where id in (select max(id) as id from A where task_id in(1,2,3) group by task_id );这个 A 表中是有 task_id 这个索引的。 …

【转换】编码转换工具笔记

应用场景 应用场景是程序整合第三方库多平台运行&#xff0c;第三方库window平台编译&#xff0c;代码移植到linux出现bom问题 思考解决 windows使用utf-8编码&#xff0c;linux使用utf-8无bom编码 工具主要针对utf-8编码文件&#xff0c;能够批量添加删除BOM&#xff0c;无…

SpringBoot获取项目日志

目的 对于布署在远端的服务&#xff0c;我们想快速的获取到日志。对于使用了日志服务&#xff0c;也可能因为上报间隔太长&#xff0c;日志不够实时。 所以想通过一些方式&#xff0c;可以不用进入到容器内也可以简单快速获取到日志&#xff0c;而且是实时的日志。目标就是获…

c语言进阶-动态内存管理

重点学习内容 动态内存管理四大函数 Malloc 内存申请函数 返回值是无类型的指针&#xff0c;指向分配的内存的首地址。申请失败会返回空指针。 malloc返回值是void*类型&#xff0c;使用时需要强制转换成所需类型。 malloc和free匹配使用&#xff0c;但是如果不free释放内存&…

解析3D视觉系统在引导金属件上下料中的应用

原创 | 文 BFT机器人 引言 Introduction 机器视觉技术作为一种高科技的智能化技术&#xff0c;正在工业生产领域发挥着越来越重要的作用。它利用计算机视觉技术&#xff0c;通过获取、处理和分析图像&#xff0c;实现对产品和工艺过程的监测、检测和控制。 随着人工智能技术的…

[SUCTF2019]hardcpp

前言 又遇到ollvm了 解混淆 可以直接用angr运行脚本去除除控制流平坦化&#xff0c;最好在ancoda等管理环境里面安装angr不然问题很多 https://github.com/Pure-T/deflat 去除前 去除后&#xff0c;它将多余的直接nop了 分析 主要加密区域位于匿名函数这一块&#xff0c…

前端学习——Web API(Day1)

Web API基本认知 Web API 基本认知 作用和分类 DOM DOM树 DOM对象 获取DOM对象 根据CSS选择器来获取DOM元素 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" …

【教学类-36-08】转学“纪念册“留念(生肖用midjounery-niji)

作品样式 背景需求&#xff1a; 即将离开班级&#xff0c;我想收集一份28个孩子的绘画册做——留念转学纪念册. 材料准备&#xff1a; 幼儿照片 ——3月初到中6班拍摄的幼儿手持学号名字纸的照片&#xff08;为了背诵幼儿信息而拍摄的照片&#xff0c;统一竖版&#xff09; 生…

jmeter 连接数据库常见报错

1. 不允许主机连接到MySQL 报错信息&#xff1a; Response message:java.sql.SQLException: Cannot create PoolableConnectionFactory (null, message from server: "Host 192.168.1.6 is not allowed to connect to this MySQL server") 说明&#xff1a;本机的地…

代码随想录day10

232. 用栈实现队列 思路&#xff1a;用两个list去模拟栈的操作&#xff0c;一个入栈list&#xff0c;一个出栈list. 并且了解栈的操作&#xff0c;pop,peek,push. 代码&#xff1a; def __init__(self):self.stack1 [] #入栈self.stack2[] #出栈def push(self, x: int) ->…

【Linux】常用网络命令:ping\netstat\mount\ifconfig

ping命令用于检测主机&#xff0c;执行ping命令指令会使用ICMP传输协议&#xff0c;发出请求回应的信息&#xff0c;若远程主机的网络功能没有问题&#xff0c;就会回应该信息。   netstat命令用于显示网络状态&#xff0c;利用netstat 指令可让你得知linux系统的网络情况。…