前言
本文参加了由公众号@若川视野 发起的每周源码共读活动。从简单到进阶学习源码中的巧妙之处,旨在于将学习的东西应用到实际的开发中,同时借鉴源码中的思想,规范自我开发,以及锻炼自己的开发思维。也欢迎大家加入大佬的源码共读活动。一起卷起来。
疑问
有没有小伙伴遇到我们在使用脚手架或者在其他的团队里面通过npm或者yarn 提示不能使用npm 或者yarn 去安装 需要使用特定的包管理工具,报错如下:
我们通过提示按照特定的方式去安装即可安装成功. 遇到这种情况 我相信好多小伙伴和我一样 都是按照提示进行安装即可。并没有深入的思考为什么我们不能通过我们使用的包管理工具去安装呢。直到看到 若川大佬的包管理工具 恍然大悟,接下来我们一起去了解一下这其中的奥秘。
only-allow
only-allow 仅仅使用的意思 其使用的语法规范则是:
///package.json
script:{
preinstall:npx only-allow pnpm ///npm yarn 都可以 代表的是我们只能使用pnpm npm yarn 进行包管理
}
///在包管理中 包下载的过程是
依次执行
# install 之前执行这个脚本
preinstall
# 执行 install 脚本
install
# install 之后执行这个脚本
postinstall
到这里 小伙伴会问 npx only-allow pnpm 是如何进行包管理工具的限制的呢 那我们从源码中寻找答案
only-allow 源码
#!/usr/bin/env node
//获取我们运行时候的包管理工具 npm yarn pnpm
const whichPMRuns = require('which-pm-runs')
///输出框的样式
const boxen = require('boxen')
///npx only-allow pnpm 我们使用的时候则是 process.argv则是命令行中的命令,process.argv.slice(2)则获取的是pnpm 或者npm 或者 yarn process.argv 则是数组的形式
const argv = process.argv.slice(2)
if (argv.length === 0) {
console.log('Please specify the wanted package manager: only-allow <npm|pnpm|yarn>')
process.exit(1)
}
const wantedPM = argv[0]
//如果不是npm pnpm yarn 的时候则报错退出
if (wantedPM !== 'npm' && wantedPM !== 'pnpm' && wantedPM !== 'yarn') {
console.log(`"${wantedPM}" is not a valid package manager. Available package managers are: npm, pnpm, or yarn.`)
process.exit(1)
}
// 获取当前系统的正在运行的包管理进程
const usedPM = whichPMRuns()
//如果不相等,则进行对应的提示
if (usedPM && usedPM.name !== wantedPM) {
const boxenOpts = { borderColor: 'red', borderStyle: 'double', padding: 1 }
switch (wantedPM) {
case 'npm':
console.log(boxen('Use "npm install" for installation in this project', boxenOpts))
break
case 'pnpm':
console.log(boxen(`Use "pnpm install" for installation in this project.
If you don't have pnpm, install it via "npm i -g pnpm".
For more details, go to https://pnpm.js.org/`, boxenOpts))
break
case 'yarn':
console.log(boxen(`Use "yarn" for installation in this project.
If you don't have Yarn, install it via "npm i -g yarn".
For more details, go to https://yarnpkg.com/`, boxenOpts))
break
}
process.exit(1)
}
process.argv 属性返回一个数组,由命令行执行脚本时的各个参数组成。它的第一个成员总是 node,第二个成员是脚本文件名,其余成员是脚本文件的参数。
which-pm-runs这个插件可以获取到我们运行时 我们运行的报的管理工具是npm pnpm还是yarn
which-pm-runs
上面我们说到了which-pm-runs 获取包的管理工具,那他是如何进行包的管理工具的获取的. 我们瞅瞅源码吧
'use strict'
module.exports = function () {
if (!process.env.npm_config_user_agent) {
return undefined
}
console.log(process.env.npm_config_user_agent)
//process.env.npm_config_user_agent npm/6.14.16 node/v14.19.1 win32 x64
return pmFromUserAgent(process.env.npm_config_user_agent)
}
function pmFromUserAgent (userAgent) {
const pmSpec = userAgent.split(' ')[0] // npm/6.14.16
const separatorPos = pmSpec.lastIndexOf('/')
return {
name: pmSpec.substr(0, separatorPos),
version: pmSpec.substr(separatorPos + 1)
}
}
process.env.npm_config_user_agent 这个属性可以获取到我们正在运行的包管理工具 输出的则是类似于 npm/6.14.16 node/v14.19.1 win32 x64 这种的字符串 有包管理工具的名称版本 我们的操作系统 等信息。
以上就是我们对于only-allow的学习,only-allow 主要的作用就是对于包管理工具的统一 方式因为不同意而造成的业务等其他方便的损失。
总结
知识点
- 通过对于only-allow的学习我们掌握了 only-allow 通过获取运行时候的包管理工具和我们preinstall 设置的包管理工具做对不,如果不一致则提示错误,否则进行包的下载。
- 掌握了
which-pm-runs是通过process.env.npm_config_user_agent去获取我们正在运行的包管理工具。 - 掌握了
process.argv是node进程中传入的命令行参数,其返回一个数组,第一个参数是node 第二个是 脚本名称 其他的则是参数 - 了解了pnpm 是高效的npm 在依赖的安装和pnpm和yarn 都不同,在速度和效率上得到了非常大的改善。
npm和yarn 和npm 区别:
-
npm yarn
npm 和yarn的依赖关系在npm 3之前采用的是嵌套的关系,100个包里面引入了100个相同的第三方依赖的时候,则会重复的下载100个第三方依赖,在npm 3之后 采用的是依赖平铺的方式 会将所有的依赖平铺到node_modules的根目录下 如果一个项目中存在相同依赖不同版本的话,则选一个版本放在根目录下 其他版本仍采用的是嵌套的方式.,这种方式就会产生一个幻影依赖,所谓的幻影依赖则是我们的page.json中没有定义依赖,但是我们可以在项目中去使用,由于我们没有定义 在线上使用的时候他的版本是不固定的.
-
pnpm
pnpm 采用的是软链接的方式 就是在.pnpm文件中配置软链接的目录,使用的时候则在.pnpm文件中查找对应的硬链接 也就是资源在磁盘的真实位置, pnpm 下载的依赖包采用改的是平铺的方式,也就是说不同项目之间存在相同的依赖的时候 只下载一次,相同依赖不同版本 则更新不同的文件。
实际开发中的应用
最近公司一直在Jenkins对于前端打包,但有个问题出现了 jenkins上打的包因为某些原因 每次打的包都会清空上次的依赖 导致 依赖得重新下载,非常的耗时。在提高打包效率的方便我们也做了好多的尝试 比如:加缓存loader ,optimization.splitChunks 运用hard-source-webpack-plugin new webpack.DllPlugin 都达不到预期的效果,因为hard-source-webpack-plugin 是基于第一个打包提速的。甚至我们还自己通过docker+nexuts(也可以通过docker+vertical)搭建了内部的npm以达到包版本的稳定性和下载包的速度,但是效果不明显。很是郁闷。直到看到了pnpm这个库 果断的使用, 在包依赖的下载上面提高了速度,也间接的提高了jenkins的总体打包速度。
遇到的问题
- 通过pnpm 安装完依赖后报
parseComponets的问题 这个主要的vue 的版本和vue-template-complier的版本不一致所导致的 这两个的版本得保持一直 同时版本号前不能带有^ 或者~ - 通过pnpm 安装完依赖后报 一些我们没有下载或者引入的依赖 暂且我们称为幽灵依赖,这些依赖是怎么来的呢通过npm 卸载的依赖无论是显性依赖还是阴性(幽灵)依赖都会提升到根的node_modules 下面,我们在项目中可以直接使用幽灵依赖。但是通过pnpm 下载的依赖则不使用,项目中直接去访问幽灵依赖的时候 则访问不到,因为访问的根node_modules 中没有对应幽灵依赖的软链接。怎么解决呢。 pnpm 提供了一种依赖结构方式 是按照pnpm的方式去构建依赖结构 在项目的根目录下创建.npmrc文件 文件中指定 node-linker=hoisted 即可 可参考pnpm