在前端中,什么是幽靈依賴?
前言
大家好,我是林三心,用最通俗易懂的話講最難的知識點是我的座右銘,基礎是進階的前提是我的初心。
幽靈依賴?
前幾天在公司的技術分享會上,我總是聽到大佬們在提起一個名詞——幽靈依賴,起初我沒有太在意,以為這個不太重要,所以就沒怎么去了解這個名詞。
直到我在做項目pnpm遷移的時候,我突然很想知道它跟幽靈依賴到底有什么關系呢?
其實在了解什么關系之前,我應該去了解一下什么是幽靈依賴,我們接著聊?。?!
什么是幽靈依賴?
比如我現(xiàn)在,我在開發(fā)一個項目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規(guī)則
如果按照node_modules的規(guī)則的話,那么目錄的結(jié)構(gòu)應該是這樣的
node_modules
├── npm-lsx
| └── node_modules
| └── npm-test
但是如果依賴關系很長的話,那么會導致目錄深度非常深,所以我們常用的npm、yarn解決了這個問題
npm、yarn
它們是怎么解決這個問題的呢?npm、yarn為了解決依賴關系過長時,導致的目錄結(jié)構(gòu)過深,采用了扁平化,也就是所有依賴都被拍平到node_modules目錄下。這樣的好處就是,不再有層級過深的問題。
現(xiàn)在目錄結(jié)構(gòu)變成這樣了
// A/node_modules
node_modules
├── npm-lsx
├── npm-test
幽靈依賴
接著上面的示例,繼續(xù)聊,請看下面的代碼
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 了
已知你現(xiàn)在代碼是這樣
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
// 現(xiàn)在
node_modules
├── npm-lsx
那么你的代碼會報錯!
const lsx = require('npm-lsx')
const test = require('npm-test') // 沒有
lsx()
test() // 報錯?。。?/code>
其實這個情況還好,因為這種情況在打包上線過程中就會報錯依賴找不到了,所以不太會造成線上的報錯崩潰
多項目引用同一個幽靈依賴
我現(xiàn)在有兩個項目A和B
A中的node_modules目錄為,現(xiàn)階段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項目已經(jīng)回歸過了,所以他知道,也改了對應的代碼
const test = require('npm-test')
test.run() // 修改代碼
而B項目就沒那么好運了,它并沒有進行回歸,所以并沒有去改代碼!?。?!
const test = require('npm-test') // 有
test() // 直接報錯
這就慘了,B項目在打包階段并不會因為依賴不到npm-test而報錯,所以它會順利上線。。然后,等到了線上運行起來,直接報錯?。?!這是非常嚴重的的事故!