如何进行依赖提升
yarn 通过递归解析从 package.json 中将所有依赖转换为依赖树。一个未经过优化的依赖树可能会非常复杂,依赖关系会有很多层级。一些包会在依赖树中出现很多次,可能会是互相冲突的版本。yarn 通过使用提升来删除尽可能多的节点来优化(去重)依赖树。
yarn 的官方文档里面写:没有一种方法可以决定如何去转换树,不同的包管理器会做出不同的判断(有些针对包流行度,大小,最高版本等等进行了优化)。出于这个原因,无法保证最终的提升结果,除非在 清单文件里始终可以访问到那些包。
文档里没有描述 yarn 的提升算法,但是提到了某些确定的提升保证,这些保证表明不应该在 node_modules 文件目录做一些特殊的布局。一些很小的依赖修改都会导致 node_modules 布局出现很大的变化。
另外需要注意的事,只有包的版本号在语义版本控制意义上兼容时才能被提升,也就是它们有相同的主版本号(但不是 0)
使用 yarn why 和 yarn.lock 去理解提升
一旦安装完成,你就可以使用 yarn why 去找到 yarn 具体是如何处理特定的包。
例如(注:下面都是 yarn v1):
# 执行
yarn why @maiertech/gatsby-theme-posts-core
# 输出
[1/4] Why do we have the module "@maiertech/gatsby-theme-posts-core"...?
[2/4] Initialising dependency graph...
[3/4] Finding dependency...
[4/4] Calculating file sizes...
=> Found "@maiertech/gatsby-theme-posts-core@0.8.0"
info Reasons this module exists
- "@maiertech#gatsby-theme-digital-garden" depends on it
- Hoisted from "@maiertech#gatsby-theme-digital-garden#@maiertech#gatsby-theme-posts-core"
info Disk size without dependencies: "108KB"
info Disk size with unique dependencies: "4.18MB"
info Disk size with transitive dependencies: "164.24MB"
info Number of shared dependencies: 374
Done in 1.61s.
上面的输出告诉了我们,安装的版本是 0.8.0,是 @maiertech#gatsby-theme-digital-garden 的依赖项,而且被提升了,因为只在整个项目中使用了一次。
再看另一个例子:
[1/4] Why do we have the module "@maiertech/gatsby-theme-pages-core"...?
[2/4] Initialising dependency graph...
[3/4] Finding dependency...
[4/4] Calculating file sizes...
=> Found "@maiertech/gatsby-theme-pages-core@0.5.0"
info Has been hoisted to "@maiertech/gatsby-theme-pages-core"
info This module exists because it’s specified in "dependencies".
info Disk size without dependencies: "76KB"
info Disk size with unique dependencies: "8.15MB"
info Disk size with transitive dependencies: "76KB"
info Number of shared dependencies: 379
=> Found "@made-up-scope/gatsby-theme-base#@maiertech/gatsby-theme-pages-core@0.4.0"
info This module exists because "@made-up-scope#gatsby-theme-base" depends on it.
info Disk size without dependencies: "72KB"
info Disk size with unique dependencies: "8.14MB"
info Disk size with transitive dependencies: "72KB"
info Number of shared dependencies: 379
Done in 6.32s.
这次 yarn 在依赖树中找到多个节点,一个是 package.json 指定的 0.5.0版本,另一个是 @made-up-scope/gatsby-theme-base 的依赖中指定的 0.4.0版本。0.5.0 的版本被提升了,因为两个的主版本号都是0,所以不能代替,需要同时存在两个版本。
yarn.lock
...
"@maiertech/gatsby-theme-pages-core@^0.4.0":
version "0.4.0"
resolved "https://registry.yarnpkg.com/@maiertech/gatsby-theme-pages-core/-/gatsby-theme-pages-core-0.4.0.tgz#d7226567f882c009c51415361666c90449637712"
integrity sha512-ohzfpaL6Q4hZX0AZ8hS0tUqY+w6I6mr+cyYFTDqqAMPY3GBZPexFFgxCCspd1sAMJqaWgBmcDH6BkQYDXVymDA==
dependencies:
...
"@maiertech/gatsby-theme-pages-core@^0.5.0":
version "0.5.0"
resolved "https://registry.yarnpkg.com/@maiertech/gatsby-theme-pages-core/-/gatsby-theme-pages-core-0.5.0.tgz#f8cc30886b85e635b3b0b952d6f488cfcb20f0ce"
integrity sha512-x+A8idApFORR5vD8aTTDEqYi83OvcZL9Tdt168Cih9o+UbYWXb8ne+T+V7klHjnZ63UL0R4F908vo3yTeIVbNw==
dependencies:
...
...
注意 yarn.lock 没有告诉我们哪些包被提升了。
再看一个例子:
yarn why v1.22.5
[1/4] Why do we have the module "browserslist"...?
[2/4] Initialising dependency graph...
[3/4] Finding dependency...
[4/4] Calculating file sizes...
=> Found "browserslist@4.16.3"
info Reasons this module exists
- "gatsby" depends on it
- Hoisted from "gatsby#browserslist"
- Hoisted from "gatsby#autoprefixer#browserslist"
- Hoisted from "gatsby#gatsby-legacy-polyfills#core-js-compat#browserslist"
- Hoisted from "gatsby#babel-preset-gatsby#@babel#preset-env#@babel#helper-compilation-targets#browserslist"
- Hoisted from "gatsby#optimize-css-assets-webpack-plugin#cssnano#cssnano-preset-default#postcss-colormin#browserslist"
- Hoisted from "gatsby#optimize-css-assets-webpack-plugin#cssnano#cssnano-preset-default#postcss-merge-rules#browserslist"
- Hoisted from "gatsby#optimize-css-assets-webpack-plugin#cssnano#cssnano-preset-default#postcss-minify-params#browserslist"
- Hoisted from "gatsby#optimize-css-assets-webpack-plugin#cssnano#cssnano-preset-default#postcss-normalize-unicode#browserslist"
- Hoisted from "gatsby#optimize-css-assets-webpack-plugin#cssnano#cssnano-preset-default#postcss-reduce-initial#browserslist"
- Hoisted from "gatsby#optimize-css-assets-webpack-plugin#cssnano#cssnano-preset-default#postcss-merge-rules#caniuse-api#browserslist"
- Hoisted from "gatsby#optimize-css-assets-webpack-plugin#cssnano#cssnano-preset-default#postcss-merge-longhand#stylehacks#browserslist"
info Disk size without dependencies: "124KB"
info Disk size with unique dependencies: "3.81MB"
info Disk size with transitive dependencies: "3.81MB"
info Number of shared dependencies: 5
Done in 0.96s.
yarn 告诉我们 browserslist 是 gatsby 的直接依赖,还在依赖树中出现了好几次,所有的指定版本都和 4.16.3 兼容,因此 yarn 提升了 4.16.3 版本,yarn.lock 里面显示的 browserslist 版本都和4.16.3 兼容。
yarn.lock
...
browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.12.2, browserslist@^4.14.5, browserslist@^4.16.1:
version "4.16.3"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717"
integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==
dependencies:
caniuse-lite "^1.0.30001181"
colorette "^1.2.1"
electron-to-chromium "^1.3.649"
escalade "^3.1.1"
node-releases "^1.1.70"
...
再看另一个例子,把 browserslist 版本固定到 4.16.2,这改变了很多东西:
[1/4] Why do we have the module "browserslist"...?
[2/4] Initialising dependency graph...
[3/4] Finding dependency...
[4/4] Calculating file sizes...
=> Found "browserslist@4.16.2"
info Has been hoisted to "browserslist"
info This module exists because it’s specified in "dependencies".
info Disk size without dependencies: "124KB"
info Disk size with unique dependencies: "3.81MB"
info Disk size with transitive dependencies: "3.81MB"
info Number of shared dependencies: 5
=> Found "gatsby#browserslist@4.16.3"
info This module exists because "gatsby" depends on it.
info Disk size without dependencies: "124KB"
info Disk size with unique dependencies: "3.81MB"
info Disk size with transitive dependencies: "3.81MB"
info Number of shared dependencies: 5
...
8 more instances omitted.
...
=> Found "stylehacks#browserslist@4.16.3"
info This module exists because "gatsby#optimize-css-assets-webpack-plugin#cssnano#cssnano-preset-default#postcss-merge-longhand#stylehacks" depends on it.
info Disk size without dependencies: "124KB"
info Disk size with unique dependencies: "3.81MB"
info Disk size with transitive dependencies: "3.81MB"
info Number of shared dependencies: 5
Done in 1.96s.
yarn 提升了 4.16.2 版本(固定的版本),在依赖树中还有其他实例(共10个)。yarn 存储了4.16.3 版本的拷贝,这意味着将一个依赖固定到一个特定版本会导致node_module 布局出现巨大变化。现在有10个节点是 4.16.3 版本的拷贝还有一个提升节点是 4.16.2 版本的拷贝(原来是一个提升节点是 4.16.3 版本的拷贝)。这个结果说明实际 node_modules 布局变化很可能不是你预想的那样。
yarn.lock
...
browserslist@4.16.2:
version "4.16.2"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.2.tgz#f79d67cd37e8d80ff0835fe7bc456e406fb1582c"
integrity sha512-oi5WJ1XukqFwgGsMxja1dySAzyWaXZqWSEWDedulO5M63JDw1rgGQbegfVZvxQyXLwkHm44xUbLsgP8C1iHeNg==
dependencies:
...
browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.12.2, browserslist@^4.14.5, browserslist@^4.16.1:
version "4.16.3"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717"
integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==
dependencies:
...
...
总结
-
yarn总是会尽可能的提升包来减少依赖树上的节点 -
如果使用
^指定依赖项版本,那么提升效果会最好,而越多使用固定版本,越会限制yarn提升包 -
可以结合
yarn why和yarn.lock来解决依赖冲突问题 -
提升可能会隐藏缺失的依赖项,因为提升的依赖可能是项目任何地方导入的。如果你忘记在
package.json中声明一个依赖然后导入它,碰巧在另一个节点中提升了这个依赖,它依然可以工作 -
yarn始终遵循package.json中的声明,即使在你导入一个依赖,yarn始终会提供一个和指定版本兼容的版本
中英文对应
-
提升
hoist -
依赖
dependencies -
依赖树
dependencies tree -
包
packages -
清单文件
manifestspackage.json -
布局
layout -
主版本
major version