背景
过去常常使用script标签将外部资源库直接放在 HTML 中,这会暴露一个全局变量供我们使用,但是当项目很大的时候,这些变量很难被管理:
- 在 HTML 中加载所有的 JS 文件,这会让其变得混乱;
- 很多时候我们实际上会在计算机中直接下载一个库文件(例如 JQuery),但是每当新版本出来时,我们不得不手动下载并更新这些文件;
- 在 npm 之前没有包含我们所需要的所有包的一个个存储库,这对于我们手动下载库并管理它们增加了很大的困难;
什么是 npm
Node Package Manager。它既是我们计算机上的一个软件,也是一个 Node.js 包存储(管理)库,用于 Node 插件的管理(安装、卸载和管理依赖等)。
下载
npm 会随着 Node.js 安装而安装。
常用指令
本章会持续更新。
检查 npm 版本
npm -v
初始化一个包管理文件(package.json)
这个文件存储了整个项目的配置。
npm init
它会让你填写一些基本信息。
安装包
npm install 包名
npm i 包名
npm i 包名1 包名2
npm i 包名@版本号
注意:如果不指定版本号,默认下载的是最新的版本。
安装 package.json 中所有的依赖包:
npm i
全局安装包:
这样在计算机上每一个项目都可以使用:
npm i 包名 -g
设置安装包的依赖:
生产环境依赖:
该选项会将包下载到该项目(地址)下,这些包是我们项目中所需要的依赖(比如 UI 组件库,JS 部分工具包,框架等):
npm install 包名 --save
开发环境依赖:
该选项会将包下载到该项目(地址)下,一般是开发过程中需要借助它完成一些工作,但是项目内容并不需要它(比如打包工具 webpack 等):
npm install 包名 --save-dev
卸载包
npm uninstall 包名
查看当前 npm 镜像
npm config get registry
切换镜像
npm config set registry https://registry.npmjs.org
上面是 npm 原镜像,还有:
查看代理
npm config get proxy
更多权限
可以在 npm 语句前加上一个 sudo 来获取更高的权限,它会让你输入用户和密码(Linux):
sudo npm i
打包
将开发的代码进行打包,打包后的代码可以发布到 npm 公共仓库里供其他人使用,打包后的文件是以.tgz为后缀。
npm pack
默认打包后的文件名是以 package.json 里的 name 和 version(版本号)用-拼接而成。
指定打包名称
npm pack . --name=mylibname
登录 npm 账号
npm login
登出 npm 账号
npm logout
发布包到 npm 上
注意:执行该操作必须要先登录 npm 的账号!
npm publish
package.json 配置信息
package.json 是 npm 管理包的文件,使用npm init后自动创建的,里面配置了包的所有信息(名字、版本号、依赖项等)。下面是它具体配置信息:
{
"name": "element-plus", // 包名。如果要发布这个包,name是必填的,它是发布包的名字;如果不发布就不是必须的 (name)
"version": "1.0.0", // 版本号。如果要发布这个包,version是必填的(version)
"description": "A Component Library for Vue 3", // 包的描述(description)
"keywords": [
// 包的关键字(keywords)
"element-plus",
"element",
"component library",
"ui framework",
"ui",
"vue"
],
"homepage": "https://element-plus.org/", // 该包项目的首页地址(可以是文档链接地址、项目官网等)
"bugs": {
// 项目提交问题地址(bugs)
"url": "https://github.com/element-plus/element-plus/issues"
},
"license": "MIT", // 表示项目的开源许可证(license)
"author": "Barney Rubble <b@rubble.com> (http://barnyrubble.tumblr.com/)", // 跟包相关的人的信息 (people fields: author, contributors)
"funding": {
// 指定捐赠地址(如果对你有帮助的话可以请我喝咖啡。。)(funding)
"type": "individual",
"url": "http://example.com/donate"
},
"files": [
// 包作为依赖项(dependency)时(就是作为我们所下载的包)所需包含的文件(files)
"LICENSE",
"README.md",
"index.js",
"cjs/",
"umd/",
"jsx-runtime.js",
"jsx-dev-runtime.js",
"react.shared-subset.js"
],
"main": "lib/index.js", // 包的入口文件(main)
"browser": {
// 浏览器环境下指定入口文件(默认是在node的环境下)
"my-module": "./src/browser.js"
},
"bin": "./path/to/program", // (bin)
"man": "./man/doc.1", // 指定单个文件或文件名数组,以供man查找。
"directories": {
// 指定项目的目录结构(可选,默认为CommonJS的结构)
"lib": "src/lib", // 指定项目的库文件目录
"bin": "src/bin", // 指定项目的可执行文件目录
"man": "src/man", // 指定项目的手册文件目录
"doc": "src/doc" // 指定项目的文档文件目录
},
"repository": {
// 代码所在的位置,通常在github上
"type": "git",
"url": "git+https://github.com/element-plus/element-plus.git"
},
"scripts": {
// 定义在包不同的生命周期下的脚本命令(scripts)
"start": "node app.js",
"start-server": "node app.js"
},
"config": {
// 用于设置script字段里的脚本运行时的参数(config)
"port": "8080"
},
"dependencies": {
// 生产环境依赖包(dependencies)
"lodash": "^4.17.21"
},
"devDependencies": {}, // 开发环境依赖包 (devDependencies)
"peerDependencies": {
// 指定包的对等依赖(peerDependencies)
"soy-milk": "1.2"
},
"peerDependenciesMeta": {
// 对对等依赖的配置
"soy-milk": {
"optional": true // 对等依赖可选(该项目下没有安装该包不会发出警告)
}
},
"bundleDependencies": [
// 指定指定哪些依赖项应该被捆绑到包中(bundleDependencies)
"renderized",
"super-streams"
],
"optionalDependencies": {
// 指定可选依赖项(optionalDependencies)
"@element-plus/icons-vue": "^2.0.6"
},
"overrides": {
// 覆盖版本依赖
"foo": "1.0.0"
},
"engines": {
// 指定包所依赖的运行时环境或引擎(一般为node和npm)
"node": ">=0.10.3 <15",
"npm": ">=6.0.0"
},
"os": ["linux", "!win32"], // 指定包所依赖的操作系统
"cpu": ["!arm", "x64"], // 指定包所依赖的cpu(可以使用!来表示非)
"private": false, // 设置包的私有性,如果将其设置为true,npm会拒接这个包的publish
"publishConfig": {
// 设置包发布的配置属性,只有在进行npm publish时才会生效,不会影响其他npm命令的行为
"registry": "https://registry.npmjs.org/", // 指定发布到的npm仓库的URL(默认为npm官方仓库)
"access": "public", // 指定发布的包的访问级别(默认为public,如果设置为"restricted",则只有特定的用户或团队才能访问该包)
"tag": "latest" // 指定发布的包的标签。可以设置为"latest"(最新版本,默认值)或其他自定义标签
},
"workspaces": [
// 定义工作区(workspaces)
"./packages/*"
]
}
name:name 字段遵循以下规则:
- 不多于 214 个字符;
- 作用域包的名称可以以点或下划线开头。没有作用域是不允许这样做的;
- 不可以使用大写字母,不要使用”js“或”node“来命名;
- 不能包含任何非 url 安全的字符,因为该名称最终成为 URL、命令行参数和文件夹名称的一部分;
version:当包发生变化时(再次发布包时),版本号也要随之发生变化;且它必须遵循node-semver的版本命名规范(x.y.z)。
semver:Semantic Versioning,语义化版本号。
X 为主版本号(major): 通常在涉及重大功能更新,产生了破坏性变更时会更新此版本号;
Y 为次版本号(Minor):在引入了新功能,但并未产生破坏性变更,依然向下兼容时更新此版本号;
Z 为修订号(Patch):在修复了一些问题,但未产生破坏性变更时会更新此版本号;
name 和 version 是用于确定一个唯一包的标识。
description :字符串类型,在搜索的时候可以看到该包的描述,并且该描述也可以作为搜索的关键词。
keywords:字符串数组类型,作为搜索该包的关键词。
bugs:可以是一个字符串,也可以是一个对象类型。该地址一般是 github 上的 issues 地址或者邮箱地址。
{
"url": "https://github.com/owner/project/issues",
"email": "project@hostname.com"
}
license:开源许可证,让其他开发者知道如何允许使用这个包,以及操纵这个包的具体限制。如果不想开源,可以配置:
{
"license": "UNLICENSED",
"private": true
}
people fields:包的作者或贡献者(维护者)的信息,可以是一个字符串,也可以是一个由 people 组成的数组,该数组每项格式可以有以下两种:
{
"name": "Barney Rubble",
"email": "b@rubble.com",
"url": "http://barnyrubble.tumblr.com/"
}
{
"author": "Barney Rubble <b@rubble.com> (http://barnyrubble.tumblr.com/)"
}
funding:可以是对象,字符串,数组(数组项为对象和字符串都可以)。
files:匹配文件的字符串数组,可以用来指定哪些文件需要发布到 npm 上以此来控制 npm 包的大小,如果指定的是文件夹,则对应文件夹下的所有文件都会被发布。匹配文件的字符串规则和.gitignore是一样的,也可以在项目的根目录下创建一个.npmignore文件,.npmignore的优先级高于.gitignore。
注意:如果不配置该选项,则默认值为[*],也就是会包含所有的文件。
main:其他开发者引用该包是最先进入的文件。它是一个相对于包文件夹根目录的模块,并且一般来说只会存在一个。
注意:如果不配置该选项,则默认为index.js为包的入口根文件。
bin:让包可以作为命令行工具使用。上述配置如下:
"bin": {
"element-plus": "./path/to/program"
}
scripts:在 package.json 中有很多内置的生命周期事件(key),或者我们自定义的命令,在这些事件下我们可以执行任意脚本。
"scripts": {
"prestart": "npm install", // 在脚本启动前自动执行这个命令 @1
"start": "node app.js", // 启动脚本时执行
"install" : "scripts/install.js", // 下载时执行
"postinstall": "npm run build", // 安装依赖项后自动执行
"build": "webpack",
"uninstall" : "scripts/uninstall.js", // 卸载时执行
"start-server": "node app.js", // @2
"dev": "nodemon index.js" // @2
}
@1:使用start脚本名称简化运行命令,让他人也能轻松运行入口代码文件:
npm start
@2:对于自定义的执行命令脚本名称,需要使用run来执行:
npm run start-server
npm run dev
更多生命周期讲解请见官网。
config:用于配置scripts字段里脚本运行的参数,例如配置端口号(port):
"config": {
"port": "8080"
}
执行脚本时,这个参数会变成npm_package_config_port存储到我们的环境变量中,我们可以通过:
console.log(process.env.npm_package_config_port); // 8080
访问到我们设置的端口号(port)。
dependencies:运行所需要用的依赖,在该目录下的依赖会在生产环境中。一般是使用npm install <packagename>或npm install <packagename> --save下载以来后自动插入里面的依赖,但是也可以手动在其中添加依赖,然后再通过npm install下载这些依赖。其中依赖的版本范围可以是如下几种:
版本字符串
版本字符串必须遵守 semver。
"dependencies": {
"lodash": "^4.17.21",
"@types/lodash": "^4.14.182", // @3
"elf": "~1.2.3",
"foo": "1.0.0 - 2.9999.9999",
},
@3:npm 包都有一个名字,如果该名字被占用了别人就不能使用了,但是如果后续需要提供与该包名有关的包则可以使用@。npm 官方提供了一个作用域的概念,使用@来表示一个作用域,该作用域遵循包名的通常规则(即 url 安全字符),这样就可以在我们自己的作用域中添加包名了。(一般是用来补 ts 声明哈哈)
tarball URLs
使用 tarball URL 指向的一个压缩文件包地址,tarball URL 的格式通常是一个以.tar.gz或.tar.bz2结尾的链接,指向一个可访问的.tar文件。
"dependencies": {
"asd": "http://asdf.com/asdf.tar.gz",
},
Git URLs
使用 git urls 直接从 git 仓库上拉取包。格式如下:
<protocol>://[<user>[:<password>]@]<hostname>[:<port>][:][/]<path>[#<commit-ish> | #semver:<semver>]
例如:
"dependencies": {
"asd1": "git+ssh://git@github.com:npm/cli.git#v1.0.27",
"asd2": "git+ssh://git@github.com:npm/cli#semver:^5.0",
"asd3": "git+https://isaacs@github.com/npm/cli.git",
"asd4": "git://github.com/npm/cli.git#v1.0.27",
},
GitHub URLs
可以直接指向 github 项目目录地址。
"dependencies": {
"express": "expressjs/express",
},
Local Paths
支持本地依赖地址。(一般用于测试本地依赖,要发布到 npm 上的包不应该使用该方法)
"dependencies": {
"local-module": "file:../local-module-plugin/local-module-0.0.0.tgz"
},
当前根目录的上级目录下的local-module-plugin文件夹下的local-module-0.0.0.tgz包,该包由依赖项目npm pack生成而来。
devDependencies:开发阶段时需要的依赖包,例如一些打包工具,类型检查工具等(webpack、eslint),在生产环境不会出现这个依赖包。一般使用npm install <packagename> --save-dev或者npm install <packagename> -D安装。
peerDependencies:确保包与特定版本的对等包兼容,并避免在运行时出现不兼容或错误的情况。
当当前包依赖于其他包时,通常会在 dependencies 字段中声明这些依赖关系。但是,有时该包可能需要与特定版本的其他包进行交互,而不是简单地依赖于它们。这时,可以使用 peerDependencies 字段来指定这些对等依赖。
"peerDependencies": {
"package-name": "version-range"
}
package-name是该包所依赖的对等包的名称,version-range是对等包的版本范围。
包被安装时,npm 会检查对等依赖关系,并确保对等包的正确版本已经安装。如果对等包的版本不符合要求,npm 会发出警告提示。
注意:peerDependencies 字段只是声明对等依赖关系,并不会自动安装对等软件包。用户在使用这个包时,需要手动安装对等包。因此,在文档或说明中明确指出对等依赖关系,并提供安装指南是很重要的。
bundleDependencies:发布一个软件包时,通常会将其上传到包管理器(npm)供其他人使用。默认情况下,npm 会自动下载和安装软件包的所有依赖项(package.json)。然而,有时我们项将某些依赖项捆绑到包中,以便用户在安装您的软件包时不需要下载这些依赖项。
注意:这会增加包的大小。
optionalDependencies:提供更大的灵活性和兼容性,允许包在某些依赖项无法满足或无法安装的情况下仍然正常运行。这对于与其他包进行集成或依赖于特定平台功能的软件包特别有用。
注意:optionalDependencies 字段只是声明可选依赖关系,并不会自动安装对等软件包。用户在使用这个包时,需要手动安装对等包。因此,在文档或说明中明确指出可选依赖关系,并提供安装指南是很重要的。
workspaces:允许我们在一个 package.json 中的工作区中管理多个相关的子包。它是一个文件匹配字符串的数组。例如:在./packages文件夹下的所有文件都将视作和当前 package.json 在一个工作空间下。
"workspaces": [
"./packages/*"
]
工作区功能可以帮助简化多包项目的管理和开发过程,提高效率。它适用于需要同时管理多个相关包的项目,例如 monorepo 或微服务架构。