理一理 npm, yarn, package.json, pakage-lock.json, yarn.lock 之间的关系 (一)

本文转载自我的个人博客,原文请移步
这是介绍包管理工具(npmyarn)系列的第一篇文章,在这一篇文章中,我们将介绍npm是什么, 以及和npm相关的文件package.json pakage-lock.json是干什么的。下一篇文章中我们将介绍yarnyarn.lock文件。

npm 是什么

我们在写项目时,往往需要用到很多package,也就是包。比如我拿一个React项目举个例子:在一个React项目中我们可能用到的包有:react redux react-redux react-dom react-router-dom axios......这么多包,如果我像平时下软件一样一个一个去他们的官网下,就会很麻烦,而且最大的问题是,当另一个人想在他的电脑上运行你的程序,首先他不知道你安装过哪些包,程序所依赖的包没有完整的安装的话,程序是不能正常运行的。即使你给他一个清单说想运行此程序需要安装这些这些包,那他需要跑去这些包的官网挨个把这些包都安装一边,这是很麻烦的,而且会出现版本更新所导致的版本不匹配等问题。

为了解决这个问题,我们的npm就诞生了。npm的全称是 Node Package Manager,简单来说它就是一个包 (package) 管理工具。npm的公司买了一个仓库用来存放这些我们日常所用的包,我们只需要安装npm,就能很轻易的通过npm来很方便的安装这些包了。比如,我想安装react,那么我只需要在命令提示符中输入npm install react --save,react就会被安装在对应的文件夹中了。npm的优点还有很多,我们慢慢道来。

版本控制 (Semantic Versioning)

大致介绍完了npm,我们再来说一说什么叫版本控制,想要深入理解npm我们就必须知道版本控制是什么。众所周知,我们的包都是不断更新的,所以,一个包,除了包的名字之外,另一个很重要的信息就是它的版本号。比如,在package.json(稍后我们将介绍这个文件)中我们可以看到类似这样的代码:

"react": "^16.7.0"

其中react是包的名字,16.7.0就是所安装的react的包的版本号。这个版本号通常的格式为X.Y.Z,其中X代表大版本号,Y代表小版本号,Z代表补丁号。最着版本的更新,版本号X.Y.Z会变化。补丁号Z的变化代表着bug的修复,不会破坏和改变任何已有的功能。小版本号Y的变化代表添加了新的功能,但是同样不会破坏和改变任何已有的功能。大版本号X的变化意味着版本有较大的改动,使用了这些包的程序可能需要对自己的代码进行更新以适配新的版本。总之,简单来说就是,如果只是版本号YZ有变化,尽管包更新了,但是我们原来的代码依旧有效,该怎么用还是怎么用,包的更新并不会影响我们能原有的代码的功能。但是如果大版本号X更新了,我们可能就需要更新我们的代码来适配新的版本。

Package.json文件的作用

现在我们已经具备的足够的知识来理解package.json的作用了,npm简化了我们安装各类包的过程,而package.json的作用就是记录我们用npm安装过哪些包的。package.json可以通过初始化语句npm init来生成,刚生成的package.json"dependencies"里面是空的,随着你逐渐通过npm安装各类包,安装过哪些包,版本号是什么,都会记录在"dependencies"里面,比如:

"dependencies": {
    "axios": "^0.18.0",
    "react": "^16.7.0",
    "react-dom": "^16.7.0",
    "react-redux": "^6.0.0",
    "react-router-dom": "^4.3.1",
    "react-scripts": "2.1.3",
    "redux": "^4.0.1",
    "redux-thunk": "^2.3.0",
    "styled-components": "^4.1.3"
  }

这样,你只需要在把项目的代码和这个package.json一起交给别人,他就能知道要运行这个目需要安装哪些包了。更方便的是,他只要在自己的电脑上输入npm installnpm就会自动安所有这些需要的包。

你可能注意到了在版本号的左边还有一个^符号,这个符号代表向新兼容,其实也就是说npm install会自动安装大版本号相同的最新的一个版本,什么意思呢,我们举个例子:在我写这篇文章时,react包的最新版本是16.7.0,所以我安装的react的版本号是16.7.0,但是在未来的某一天,当另一个人拿到我的项目代码和package.json时,他通过npm install来安装依赖包,但是,当他安装时react的最新版本已经是16.7.1了,这个时候他通过npm install安装的react包的版本其实是16.7.1。原则上来说其实这个时候程序应该是完全可以运行的,因为我们之前说过,只有大版本号的更新才有可能需要我们更新代码来适配,这也是为什么npm install安装的是大版本号相同的最新版本。

但是问题也有可能就在这个时候出现了,之前我们做说的版本控制 (Semantic Versioning) 其实只是一个大家约定俗成的规矩。原则上小版本号Y和补丁号Z的更新是不应该影响包的已有功能的,但是如果因为某些原因而影响了,就有可能出现这种情况:其他人通过npm install安装完所有的包以后发现程序还是不能正常运行。那么我们有没有一种方法,能够保证所安装的包的版本是一模一样的呢,这就需要我们的pakage-lock.json了。

Pakage-lock.json文件的作用

5.X.X之后的npm版本中,pakage-lock.json是会被自动生成的。pakage-lock.json被创建的目的就是更精确的记录包的各类信息。比如,react包在pakage-lock.json里面长这个样子:

    "react": {
      "version": "16.7.0",
      "resolved": "https://registry.npmjs.org/react/-/react-0.0.0-4a1072194.tgz",
      "integrity": "sha512-ZUj2lkUDLjwJaGu4WD0dYSvsfIyhQt2l/AJDlg4ij+rCDU3fSFKgHWanNovViUoaWHAxgrpft3KGFfvWPZH5LA==",
      "requires": {
        "loose-envify": "^1.1.0",
        "object-assign": "^4.1.1",
        "prop-types": "^15.6.2",
        "scheduler": "^0.12.0"
      }
    },

version记录了包的版本,resolved记录了包的下载来源。这样,在通过npm install命令安装时,不仅能够安装相同版本号的包,而且连包的下载源都是一样的,这样,我们就实现了真正意义上的安装一模一样的依赖包,从而确保程序的成功运行。

那么我们在有了pakage-lock.json后是不是就不需要package.json了呢?不,我们依旧需要package.json。这两个文件之间的关系可以这样来理解:package.json负责的不仅仅是记录各种依赖包,它还记录了其他信息,包括project properties, description, author & license 等等,而pakage-lock.json的作用仅仅是辅助package.json锁定依赖包的版本。pakage-lock.json可有可无,没有的话只是不能锁定版本而已,而package.json是必须要有的。

在这篇文章中,我们主要介绍了npm。在下一篇文章中,我们会介绍另一个包管理工具yarn,并且会着重的理一理yarn与我们今天所说的这些文件的关系。

推荐阅读更多精彩内容