当前位置: 欣欣网 > 码农

在前端中,什么是幽灵依赖?

2024-04-21码农

前言

大家好,我是林三心, 用最通俗易懂的话讲最难的知识点 是我的座右铭, 基础是进阶的前提 是我的初心。

幽灵依赖?

前几天在公司的技术分享会上,我总是听到大佬们在提起一个名词—— 幽灵依赖 ,起初我没有太在意,以为这个不太重要,所以就没怎么去了解这个名词。

直到我在做项目 pnpm迁移 的时候,我突然很想知道它跟 幽灵依赖 到底有什么关系呢?

其实在了解什么关系之前,我应该去了解一下什么是 幽灵依赖 ,我们接着聊!!!

什么是幽灵依赖?

比如我现在,我在开发一个项目A,项目A中我装了 npm-lsx 这个包,而这个 npm-lsx 的包依赖了 npm-test ,咱们来看看这两处 packages.json

依赖关系是:A <- npm-lsx <- npm-test

// A/packages.json
{
"name""A",
"dependencies": {
"npm-lsx""^1.0.0"
}
}
// A/node_modules/npm-lsx/packages.json
{
"name""npm-lsx",
"dependencies": {
"npm-test""^1.0.0"
}
}

node_modules规则

如果按照 node_modules 的规则的话,那么目录的结构应该是这样的

node_modules
├── npm-lsx 
| └── node_modules
| └── npm-test

但是如果依赖关系很长的话,那么会导致目录深度非常深,所以我们常用的 npm、yarn 解决了这个问题

npm、yarn

它们是怎么解决这个问题的呢? npm、yarn 为了解决依赖关系过长时,导致的目录结构过深,采用了 扁平化 ,也就是所有依赖都被拍平到 node_modules 目录下。这样的好处就是,不再有层级过深的问题。

现在目录结构变成这样了

// A/node_modules
node_modules
├── npm-lsx 
├── npm-test

幽灵依赖

接着上面的示例,继续聊,请看下面的代码

const lsx = require('npm-lsx')
const test = require('npm-test')
lsx()
test()

你们觉得这段代码有问题吗?其实运行起来是没问题的。但是问题来了,我们项目中居然能直接引用 npm-test 这个包!!!

我们都知道依赖关系是:

A <- npm-lsx <- npm-test

按理说, A 是不能直接引用 npm-test 的,因为没有直接依赖关系啊!!!但是因为前面说了, npm、yarn 会将依赖拍平在 A的node_modules 中,这导致了 A 可以直接 require('npm-test')

我们称这个 npm-test 幽灵依赖 !!!

幽灵依赖的坏处?

某天 npm-lsx 不依赖 npm-test 了

已知你现在代码是这样

const lsx = require('npm-lsx')
const test = require('npm-test')
lsx()
test()

某天 npm-lsx 升级了!它不再依赖 npm-test 了!那么此时 A node-modules 中变了!

// 以前
node_modules
├── npm-lsx 
├── npm-test
// 现在
node_modules
├── npm-lsx

那么你的代码会报错!

const lsx = require('npm-lsx')
const test = require('npm-test'// 没有
lsx()
test() // 报错!!!

其实这个情况还好,因为这种情况在打包上线过程中就会报错依赖找不到了,所以不太会造成线上的报错崩溃

多项目引用同一个幽灵依赖

我现在有两个项目 A B

A 中的 node_modules 目录为,现阶段 npm-test 的版本为 1.0.0

// A/node_modules
node_modules
├── npm-lsx 
├── npm-test 版本号:1.0.0

A 中某个文件的代码为

const test = require('npm-test')
test()

B 中某个文件的代码为

const test = require('npm-test')
test()

当有一天 npm-lsx 所依赖的 npm-test 升级了!版本升级为了 2.0.0

// A/node_modules
node_modules
├── npm-lsx 
├── npm-test 版本号:2.0.0

此时 test 的用法也变了~而因为 A 项目已经回归过了,所以他知道,也改了对应的代码

const test = require('npm-test')
test.run() // 修改代码

B 项目就没那么好运了,它并没有进行回归,所以并没有去改代码!!!!

const test = require('npm-test'// 有
test() // 直接报错

这就惨了, B 项目在打包阶段并不会因为依赖不到 npm-test 而报错,所以它会顺利上线。。然后,等到了线上运行起来,直接报错!!!这是非常严重的的事故!

结语

点个【赞】和【在看】是对林三心最大的鼓励,林三心会非常开心的~~~

关注公众号【前端之神】,回复【加群】,即可获得加入【千人前端学习大群】的方式,交流找工作,摸鱼,吹水~