导读
现如今,前端开发的同学已经离不开 npm 这个包管理工具,其优秀的包版本管理机制承载了整个繁荣发展的NodeJS社区,理解其内部机制非常有利于加深我们对模块开发的理解、各项前端工程化的配置以加快我们排查问题(相信不少同学收到过各种依赖问题的困扰)的速度。
本文从三个角度:package.json、版本管理、依赖安装结合具体实例对 npm 的包管理机制进行了详细分析。
一、剖析 package.json
在 Node.js 中,模块是一个库或框架,也是一个 Node.js 项目。Node.js 项目遵循模块化的架构,当我们创建了一个 Node.js 项目,意味着创建了一个模块,这个模块必须有一个描述文件,即 package.json。它是我们最常见的配置文件,但是它里面的配置你真的有详细了解过吗?配置一个合理的 package.json 文件直接决定着我们项目的质量,所以首先带大家分析下 package.json 的各项详细配置。
1.1 必备属性
package.json 中有非常多的属性,其中必须填写的只有两个:name 和 version ,这两个属性组成一个 npm 模块的唯一标识。
npm包命名规则
name 即模块名称,其命名时需要遵循官方的一些规范和建议:
包名会成为模块url、命令行中的一个参数或者一个文件夹名称,任何非url安全的字符在包名中都不能使用,可以使用 validate-npm-package-name 包来检测包名是否合法。
语义化包名,可以帮助开发者更快的找到需要的包,并且避免意外获取错误的包。
若包名称中存在一些符号,将符号去除后不得与现有包名重复
例如:由于react-native已经存在,react.native、reactnative都不可以再创建。
如果你的包名与现有的包名太相近导致你不能发布这个包,那么推荐将这个包发布到你的作用域下。
例如:用户名 conard,那么作用域为 @conard,发布的包可以是@conard/react。
查看包是否被占用
name 是一个包的唯一标识,不得和其他包名重复,我们可以执行 npm view packageName 查看包是否被占用,并可以查看它的一些基本信息:
若包名称从未被使用过,则会抛出 404 错误:
另外,你还可以去 https://www.npmjs.com/ 查询更多更详细的包信息。
1.2描述信息
基本描述
{
"description": "An enterprise-class UI design language and React components implementation",
"keywords": [
"ant",
"component",
"components",
"design",
"framework",
"frontend",
"react",
"react-component",
"ui"
]
}
description用于添加模块的的描述信息,方便别人了解你的模块。
keywords用于给你的模块添加关键字。
当然,他们的还有一个非常重要的作用,就是利于模块检索。当你使用 npm search 检索模块时,会到description 和 keywords 中进行匹配。写好 description 和 keywords 有利于你的模块获得更多更精准的曝光:
开发人员
描述开发人员的字段有两个:author 和 contributors, author 指包的主要作者,一个 author 对应一个人。 contributors 指贡献者信息,一个 contributors 对应多个贡献者,值为数组,对人的描述可以是一个字符串,也可以是下面的结构:
{
"name" : "ConardLi",
"email" : "lisqPersion@163.com",
"url" : "https://github.com/ConardLi"
}
地址
{
"homepage": "http://ant.design/",
"bugs": {
"url": "https://github.com/ant-design/ant-design/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/ant-design/ant-design"
},
}
homepage 用于指定该模块的主页。
repository 用于指定模块的代码仓库。
bugs 指定一个地址或者一个邮箱,对你的模块存在疑问的人可以到这里提出问题。
1.3 依赖配置
我们的项目可能依赖一个或多个外部依赖包,根据依赖包的不同用途,我们将他们配置在下面几个属性下:dependencies、devDependencies、peerDependencies、bundledDependencies、optionalDependencies。
配置规则
在介绍几种依赖配置之前,首先我们来看一下依赖的配置规则,你看到的依赖包配置可能是下面这样的:
"dependencies": {
"antd": "ant-design/ant-design#4.0.0-alpha.8",
"axios": "^1.2.0",
"test-js": "file:../test",
"test2-js": "http://cdn.com/test2-js.tar.gz",
"core-js": "^1.1.5",
}
依赖配置遵循下面几种配置规则:
依赖包名称:VERSION
VERSION是一个遵循SemVer规范的版本号配置,npm install 时将到npm服务器下载符合指定版本范围的包。
依赖包名称:DWONLOAD_URL
DWONLOAD_URL 是一个可下载的tarball压缩包地址,模块安装时会将这个.tar下载并安装到本地。
依赖包名称:LOCAL_PATH
LOCAL_PATH 是一个本地的依赖包路径,例如 file:../pacakges/pkgName。适用于你在本地测试一个npm包,不应该将这种方法应用于线上。
依赖包名称:GITHUB_URL
GITHUB_URL 即 github 的 username/modulename 的写法,例如:ant-design/ant-design,你还可以在后面指定 tag 和 commit id。
依赖包名称:GIT_URL
GIT_URL 即我们平时clone代码库的 git url,其遵循以下形式:
://[[:]@][:][:][/][# | #semver:]
其中 protocal 可以是以下几种形式:
git://github.com/user/project.git#commit-ish
git+ssh://user@hostname:project.git#commit-ish
git+ssh://user@hostname/project.git#commit-ish
git+http://user@hostname/project/blah.git#commit-ish
git+https://user@hostname/project/blah.git#commit-ish
dependencies
dependencies 指定了项目运行所依赖的模块,开发环境和生产环境的依赖模块都可以配置到这里,例如
"dependencies": {
"lodash": "^4.17.13",
"moment": "^2.24.0",
}
devDependencies
有一些包有可能你只是在开发环境中用到,例如你用于检测代码规范的 eslint ,用于进行测试的 jest ,用户使用你的包时即使不安装这些依赖也可以正常运行,反而安装他们会耗费更多的时间和资源,所以你可以把这些依赖添加到 devDependencies 中,这些依赖照样会在你本地进行 npm install 时被安装和管理,但是不会被安装到生产环境:
"devDependencies": {
"jest": "^24.3.1",
"eslint": "^6.1.0",
}
peerDependencies
peerDependencies 用于指定你正在开发的模块所依赖的版本以及用户安装的依赖包版本的兼容性。
上面的说法可能有点太抽象,我们直接拿 ant-design 来举个例子,ant-design 的 package.json 中有如下配置:
"peerDependencies": {
"react": ">=16.0.0",
"react-dom": ">=16.0.0"
}
当你正在开发一个系统,使用了 ant-design ,所以也肯定需要依赖 React。同时, ant-design 也是需要依赖 React 的,它要保持稳定运行所需要的 React 版本是16.0.0,而你开发时依赖的 React 版本是 15.x:
这时,ant-design 要使用 React,并将其引入:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
这时取到的是宿主环境也就是你的环境中的 React 版本,这就可能造成一些问题。在 npm2 的时候,指定上面的 peerDependencies 将意味着强制宿主环境安装 react@>=16.0.0和react-dom@>=16.0.0 的版本。
npm3 以后不会再要求 peerDependencies 所指定的依赖包被强制安装,相反 npm3 会在安装结束后检查本次安装是否正确,如果不正确会给用户打印警告提示。
"dependencies": {
"react": "15.6.0",
"antd": "^3.22.0"
}
例如,我在项目中依赖了 antd 的最新版本,然后依赖了 react 的 15.6.0版本,在进行依赖安装时将给出以下警告:
optionalDependencies
某些场景下,依赖包可能不是强依赖的,这个依赖包的功能可有可无,当这个依赖包无法被获取到时,你希望 npm install 继续运行,而不会导致失败,你可以将这个依赖放到 optionalDependencies 中,注意 optionalDependencies 中的配置将会覆盖掉 depende