npm开发技巧

591 阅读7分钟

今天我们学习几个npm快速开发技巧,希望这些技巧帮助你更好的认识和使用npm这个命令。

catalogue.png

基本的快捷键方式

我们从最基本的命令开始:

npm init

image.png

这个命令用于整个项目的初始化,当执行完这个命令后将会在本项目的目录下新增一个package.json的文件夹。这个命令执行完之后会进入输入配置阶段,如果不想进入,可以在这个命令加上 -y

npm init -y

npm_init_y.png

如果想知道默认的配置模板在哪里,可以执行以下的命令

npm config get userconfig

npm_get_useconfig.png 执行这个命令后,我们可以找到我们的配置文件所在的位置

home=https://www.npmjs.org 
registry=https://registry.npmjs.org/ ## 安装的源

执行下面的命令可以对.npmrc中的内容进行编辑

npm config edit

image.png

npm install , 简写:npm i

这个命令是用于安装项目的依赖包,每个项目都拥有自己的依赖包。执行这个命令后会创建一个node_module和package.json的目录或文件。

在我们上一个命令的前提下,我们继续来探寻 npm i 这个技巧

npm i

帮助检测与当前的node最匹配的npm的版本号,并匹配出相应的npm包的升级的版本号(效果同npm i -S)

npm i -S 或npm i --save

会将即将安装的包的名称及版本号放在dependecies里面

npm_i_s.png

此时的package.json

{
   "name": "no",
   "version": "1.0.0",
   "description": "",
   "main": "index.js",
   "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1"
 },
  "keywords": [],
   "author": "",
   "license": "ISC",
   "dependencies": {
     "lodash": "^4.17.21"
   }
 }

npm i -D 或npm i --save-dev

会将即将安装的包的名称及版本号放在devDependencies里面

npm_i_D.png

此时的package.json

{
  "name": "no",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {},
  "devDependencies": {
    "lodash": "^4.17.21"
  }
}

npm -O 或npm --save-optional

npm_i_O.png 此时的package.json

{
  "name": "no",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {},
  "devDependencies": {},
  "description": "",
  "optionalDependencies": {
    "lodash": "^4.17.21"
  }
}

注意:optionalDependencies会覆盖dependencies中的同名依赖包,因此不要在两个地方都写。

区别

package.json文件里面的devDependenciesdependenciesoptionalDependencies`对象有什么区别呢?

  • devDependencies里面的依赖用于于开发环境,不用于生产环境
  • dependencies是需要发布到生产环境的 待处理
  • optionalDependencies是可选依赖包,此种依赖不是程序运行所必须的,但是安装后可能会有新功能,例如一个图片解码库,安装了 optionalDependencies 后会支持更多的格式(webp,png,jpeg等)

dependencies 是项目正常运行所需要的依赖,devDependencies则是开发阶段整个项目所需的依赖(如会有一些测试依赖之类的),optionalDependencies是为了更好的扩充原有项目的功能所需要的依赖。

npm i xx@

这个命令可以精确化安装某个版本

npm 版本修饰前缀与依赖含义

在依赖声明列表中,我们看到各包的后面还会带着版本号,版本号前则有不同的前缀修饰。npm 正是通过这种前缀修饰决定版本下载方式。 当安装一个包并添加了 –save 等参数要求保存到 package.json 中时,默认使用的是 ^。通过修改 save-prefix 配置,可以修改这个默认的设置。如:

npm config set save-prefix="~" 常见的版本号表达式示例:

表达式版本范围
>=1.2.7大于等于1.2.7
>=1.2.7 <1.3.01.2.7,1.2.8,1.2.9
1.2.3 – 2.3.4>=1.2.3 <=2.3.4
1.2 – 2.3.4>=1.2.0 <=2.3.4
1.2.3 – 2.3>=1.2.3 <2.4.0
1.2.3 – 2>=1.2.3 <3.0.0
*>=0.0.0
1.x(等价于1.X)>=1.0.0 <2.0.0
1.2.x>=1.2.0 <1.3.0
“”(等价于*)>=0.0.0
1(等价于1.x.x)>=1.0.0 <2.0.0
1.2(等价于1.2.x)>=1.2.0 <1.3.0
~1.2.3(>=1.2.3 <1.(2+1).0)>=1.2.3 <1.3.0
~1.2(>=1.2.0 <1.(2+1).0)>=1.2.0 <1.3.0
~1(>=1.0.0 <(1+1).0.0)>=1.0.0 <2.0.0
~0.2.3(>=0.2.3 <0.(2+1).0)>=0.2.3 <0.3.0
~0.2(>=0.2.0 <0.(2+1).0)>=0.2.0 <0.3.0
~0(>=0.0.0 <(0+1).0.0)>=0.0.0 <1.0.0
~1.2.3-beta.2>=1.2.3-beta.2 <1.3.0
^1.2.3>=1.2.3 <2.0.0
^0.2.3>=0.2.3 <0.3.0
^0.0.3>=0.0.3 <0.0.4
^1.2.3-beta.2>=1.2.3-beta.2 <2.0.0
^0.0.3-beta>=0.0.3-beta <0.0.4
^1.2.x>=1.2.0 <2.0.0
^0.0.x>=0.0.0 <0.1.0
^0.0>=0.0.0 <0.1.0
^1.x>=1.0.0 <2.0.0
^0.x>=0.0.0 <1.0.0

让脚本跨平台兼容

任何在命令行上运行的代码都有兼容性问题的风险,特别是在Windows和基于unix的系统(包括Mac和Linux)之间。如果你只处理特定的项目,但是在许多情况下,跨平台兼容性很有必要的:任何开放源码或协作项目,以及示例和教程项目,都应该能够工作,而不管操作系统是什么。

有几个选项可供选择,但效果最好的是cross-env,执行

npm i -D cross-env

将其作为开发依赖项安装。然后在任何环境变量之前包括关键字cross-env,就像这样:

{
  "scripts": {
    "build": "cross-env NODE_ENV=production webpack --config build/wepack.config.js"
  }
}

cross-env是 实现跨平台兼容性的最无缝的方法,但还有其他两个流行的工具,它们可以帮助实现跨平台兼容性:

  • rimraf 可以安装在全球运行跨平台脚本
  • ShellJS 是Unix shell命令在Node.js API上的可移植实现

在不同的目录中运行脚本

有时,在同一个文件夹里可能存在多个不同的项目,但此时我们想在一个项目里访问另外项目的脚本命令,我们该如何操作?有两种方法可以执行此操作:

第一种方式

第一种是手动 cd 并运行对应的命令:

cd common && npm start && cd ..

第二种方式

但还有一个更优雅的解决方案,即使用--prefix标志指定路径:

npm  run [command] --prefix 相对路径

备注:除了npm start 之外的命令都需要在对应的命令前加run(npm run [command])

举个栗子:

fileHolder.png 目前有两个目录下都有对应个package.json文件夹

我们需要在 no这个目录下执行另一个package.json中的脚本

proxy_commond.png

--prefix后的路径是 相对路径

列举已安装的package

为什么我们需要花费我们的时间打开 package.json 文件去检查我们安装了哪些 package 依赖? 只需要简单的运行 npm ls --depth=0 就可以得到我们需要了解的信息 数字0待研究

npm ls --depth=0 或 npm ls --depth 0

npm_ls_depth.png 通过执行这个命令,我们可以查看到我们在common项目目录下安装的依赖。除此之外,如果想要知道全局安装了哪一些依赖,可以执行

npm ls --depth 0 -g

注意:–depth 表示深度,我们使用的模块会有依赖,深度为 0 的时候,不会显示依赖模块

列出并选择可用脚本

列出packgage.json文件中可用的脚本很简单:

在项目的根目录并在终端中执行以下命令:

npm run

npm_run.png

控制应用程序版本

与手动更改应用程序的版本相比,npm 提供了一些有用的快捷方式来完成这一点。

版本号解释

major: 主版本号 
premajor: 预备主版本 
minor: 次版本号 
preminor: 预备次版本 
patch: 修订号 
prepatch: 预备修订版 
prerelease: 预发布版本

案例

假设初始版本为0.1.0
➜  xxx git:(master) npm version preminor
v0.1.0-0
➜  xxx git:(master) npm version minor
v0.1.0
➜  xxx git:(master) npm version prepatch
v0.1.1-0
➜  xxx git:(master) npm version patch   
v0.1.1
➜  xxx git:(master) npm version prerelease
v0.1.2-0
➜  xxx git:(master) npm version premajor
v1.0.0-0
➜  xxx git:(master) npm version major   
v1.0.0

自定义npm init脚本

在项目开发过程中,我们可以自定义我们自己的npm init 脚本来初始化我们的项目。

首先在我们的项目目录中创建一个.npm-init.js文件

npm_init.png

为了确保npm init被指向正确的文件,可以运行:

npm config set init-module ./.npm-init.js

在集成git之前,这里有一个简单的.npm-init.js文件,它模拟了默认npm init的问题。

module.exports = {
    name: prompt('package name', basename || package.name),
    version: prompt('version', '0.0.0'),
    decription: prompt('description', '?'),
    main: prompt('entry point', 'index.js'),
    repository: prompt('git repository', 'https://github.com/xjingmo'),
    keywords: prompt(function (s) { return s.split(/\s+/) }),
    author: prompt('author', 'Gmo'),
    license: prompt('license', 'ISC')
  }

现在执行

npm init

image.png

此时每个命令都会依据我们提供的.npm-init.js文件中代码执行

每个问题都遵循nameInPackage模式:prompt('nameInPrompt','defaultValue'),要在缺省情况下设置值而不带问题,只需删除prompt方法。如果要返回默认设置,只需删除.npm-init.js。

使用自定义npm init脚本将你的第一个 Commit 提交到 GitHub

为了将git命令合并到.npm-init.js文件中,需要一种方法来控制命令行,我们可以使用child_process 模块

在文件的顶部引入它,因为我们只需要execSync函数,所以可以使用析构赋值语法自己获取它:

const { execSync } = require('child_process');

创建了一个helper函数,它将函数的结果打印到控制台:


function run(func) {
  console.log(execSync(func).toString())
}

最后,将提示输入GitHub存储库URL,如果提供,我们将生README.md文件,并启动我们的第一次提交。

repository: prompt('github repository url', '', function (url) {
  if (url) {
    run('touch README.md');
    run('git init');
    run('git add README.md');
    run('git commit -m "first commit"');
    run(`git remote add origin ${url}`);
    run('git push -u origin master');
  }
  return url;
})

.npm-init.js文件大概如下:


const { execSync } = require('child_process');

function run(func) {
  console.log(execSync(func).toString())
}

module.exports = {
    name: prompt('package name', basename || package.name),
    version: prompt('version', '0.0.0'),
    decription: prompt('description', '?'),
    main: prompt('entry point', 'index.js'),
    repository: prompt('github repository url', '', function (url) {
      if (url) {
        run('touch README.md');
        run('git init');
        run('git add README.md');
        run('git commit -m "first commit"');
        run(`git remote add origin ${url}`);
        run('git push -u origin master');
      }
      return url;
    }),
    keywords: prompt(function (s) { return s.split(/\s+/) }),
    author: prompt('author', 'Gmo'),
    license: prompt('license', 'ISC')
  }

命令执行:

package.json文件:


{
  "name": "no",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+ssh://git@github.com/xjingmo/JavaScript.git"
  },
  "author": "",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/xjingmo/JavaScript/issues"
  },
  "homepage": "https://github.com/xjingmo/JavaScript#readme"
}

更多npm命令,请查看npm命令