当输入 npm run xxx后发生了什么?

746 阅读4分钟

我正在参加「掘金·启航计划」

背景

最近在从0到1写一个脚手架,如果只是按照教程实现一下,那也很简单,花不了多少时间。而真正费时间的是弄懂背后的原理,原理很重要,如果我们不弄懂,那么知识就会很零散,很容易忘记。我们总不能在脑子里装一大堆的使用方法吧,你说呢

前置知识

环境变量的作用

你可能有个疑问,我们在Shell命令行中输入一个命令,Shell是如何知道去哪找到这个命令所对应的执行文件,然后去执行它?

这通常是通过环境变量PATH来进行搜索的,熟悉window的同学可能知道有这么一个叫PATH的环境变量。这个环境变量保存了Shell中执行的命令所对应的可执行文件的路径。

那环境变量存放在哪里呢? 我们可以通过echo $PATH来查看:

image.png

可以看到环境变量PATH有好几个路径,当我们在终端输入一个命令的时候,电脑就会依次按照PATH中设定的路径来到目录中查找,如果存在同名的环境变量,则执行先找到的那个。

接下来我们进入到比较熟悉的

/usr/local/bin

然后在命令行输入ll命令

image.png

这里面的文件都是一些链接问答那个,他们链接到真正执行的文件。

如何添加环境变量

现在你安装了mongodb的文件,在/usr/local/mongodb/bin路径下,一般你需要进入到这个路径才能启动mongodb,但是每次都要进入到这个文件太麻烦了,我能不能在任何地方输入mongodb都能启动程序呢?

通过设置环境变量即可完成,也就是把/usr/local/mongodb/bin路径添加到PATH上就行了,当你在命令行输入mongodb时,操作系统会从PATH上找到对应的路径,然后执行可执行文件。

vi .bash_profile 
// 输入: 
export PATH=$PATH://usr/local/mongodb/bin ($PATH代表已经存在的路径,它可以放前面或者后面) 
// 立即生效 
source .bash_profile

【注意】vi .bash_profile 表示在用户主目录下设置,针对当前特定的用户起作用的环境变量。你也可以设置系统目录下的profile文件,那么将会对所有用户都生效。

bin文件夹介绍

在文章中我们会多次提到一个文件名binbin文件夹是用来干什么的呢

bin是单词binary的缩写 是二进制的意思
由于一些约定俗成的原因我们一般都将可执行文件放到 bin 目录中.

npm install 的时候发生了什么?

npm install后会根据package.json中配置的需要下载的依赖,或者命令后面的参数把包下载到本地,但是肯定不止下载包这么简单。如果你下载的包的package.json中,有bin属性的话,那么会把库目录中bin文件里面的内容,放到node_modules/.bin中。

举个例子

比如我使用npm install下载lerna包后 image.png

然后我们在.bin目录中使用ls -al查看lerna.js软连接对应的真实链接

image.png

会看到指向真实的node_module包的lerna/cli.js文件

npm install 的时候,npm 就帮我们把这种软连接配置好了,其实这种软连接相当于一种映射,执行npm run xxx的时候,就会到 node_modules/bin 中找对应的映射文件,然后再找到相应的js文件来执行。

为什么会把库目录中bin文件里面的内容,放到node_modules/.bin中?

因为node_modules/.bin中的js文件就像快捷方式,你执行快捷方式,就会访问库中的bin目录下的文件

image.png

为什么直接执行js文件不能执行?

因为$PATH中没有你这个命令呀,你在命令行输入的东西,他都会把你当成一个命令

我们在桌面上创建一个test.js文件,如何通过命令执行他

如果要想直接执行js文件

  • 第一: 需要映射软连接 image.png

首先我们输入一个echo $PATH命令找到bin目录,只要在bin目录下创建一个软连接指向我们创建的js文件,那么我们执行命令,就是在执行这个js文件

image.png

  • 第二: 需要在文件头部加上
#!/usr/bin/env node

这句话的意思就是直接去环境变量中找node命令

所以/usr/bin/env node test.js === node test.js

npm install -g的时候发生了什么?

npm install差不多, 只是会在全局的node_module文件的bin目录下创建软连接,软连接指向库中的实际文件。并注册到全局的$PATH

而我们在npm install -g的时候,其实是将相关文件安装在/usr/local/lib/node_modules目录下,同时,在/usr/local/bin目录下会有一个映射脚本,将其指向/usr/local/lib/node_modules下的真实文件。

/usr/local/bin目录 截屏2021-08-12 上午11.23.31.png

/usr/local/lib/node_modules目录 截屏2021-08-12 上午11.33.07.png

什么时候使用npm install,什么时候使用npm install -g呢?

  • 如果需要在命令行执行任务的时候就需要全局安装

我们在命令行执行命令的时候,都是通过环境变量查找的,所以只有全局安装才会放到环境变量中,如果只是在本地安装,执行全局命令会报错的,因为找不到, 比如webpack包,如果你没有全局安装,那么你想执行本地包的话,那么只能node ./node_modules/webpack/bin/index.js

  • 如果需要require的时候就需要在本地安装
    require加载npm包的时候,只会在本地node_module里面去找,如果没有就会报错,所以就算你全局有安装,本地没有安装,也会报错的, 关于require的加载原理,大家可以看我的另一篇文章require源码(小白级教程)

npm run xxx 的时候发生了什么

经过上面的学习我们就很好懂了

{ 
    // ... 
    "scripts": { 
        "start": "node ./src/index.js", 
        "build": "react-scripts build", 
     }, 
    // ... 
}

当我们执行

npm run build

的时候

  1. 首先执行package.json中的script中的build命令
  2. 然后开始执行react-scripts命令
  3. 此时在环境变量$PATH中查找是否存在react-scripts命令,如果存在则返回实际链接文件,如果不存在则查找本地node_modules/bin中是否存在react-script.js文件, 如果都不存在则返回not found
  4. 如果在全局找到的,那么会指向全局安装的node_modules中库的实际执行文件,如果是在本地node_modules/bin中找到链接,那么会在本地node_modules中库的实际执行文件
  5. 通过/usr/bin/env node 执行文件

使用npm run xxx的好处是什么?

"scripts": {
    "start": "webpack -v"
  },

因为好多包我们全局并没有安装,就算是下载别人的项目,我们也是安装到本地node_modules中,而不是全局,所以npm run xxx 可以让我们执行本地包的命令

npm link的时候发生了什么

我们在自己写脚手架的时候会常常用到npm link

作用

用于设置软连接,自己进行测试

为什么可以

npm link可以帮助我们模拟包安装后的状态,它会在系统中做一个快捷方式映射,让本地的包就好像install过一样,可以直接使用。

在MAC中,我们在终端可以直接敲命令,其实是在执行/usr/local/bin目录下的脚本,这个目录可以认为是我们的全局命令所在的地方。

npm link做的事也差不多,只不过它在/usr/local/lib/node_modules里存的不是真实的文件,而是存了一个快捷方式,指向你当前执行npm link的目录。如果开发的是node包,则执行的命令名和真实执行的文件入口,会通过项目的package.jsonbin的配置来获取。

参考