[TOC] >[success] # npm package.json 1. 当执行'**npm init**' 时候会生成'**package.json**' 文件,能够帮我们列出项目所依赖的包,可以指定项目可以使用的包版本等等 2. **必须遵守 JSON 格式**,否则,尝试以编程的方式访问其属性的程序则无法读取它**package.json常见字段如下** 2.1. '**version**' -- 表明了当前的版本。 2.2. '**name**' -- 设置了应用程序/软件包的名称。 2.3. '**description**' -- 是应用程序/软件包的简短描述。 2.4. '**main**' -- 设置了应用程序的入口点。 2.5. '**private**' -- 如果设置为 true,则可以防止应用程序/软件包被意外地发布到 npm。 2.6. '**scripts**' -- 定义了一组可以运行的 node 脚本。 2.7. '**dependencies**' -- 设置了作为依赖安装的 npm 软件包的列表。 2.8. '**devDependencies**' -- 设置了作为开发依赖安装的 npm 软件包的列表。 2.9. '**engines**' -- 设置了此软件包/应用程序在哪个版本的 Node.js 上运行。 2.10. '**browserslist**' -- 用于告知要支持哪些浏览器(及其版本)。 2.11. **author**-- 是作者相关信息(发布时用到) 2.12. **license** -- 是开源协议(发布时用到) >[info] ## main -- 主入口文件 ~~~ 1.main配置项的值是一个js文件的路径,它将作为程序的主入口文件。也就是说当别人引 用了这个包时import testNpm from 'testNpm',其实引入的就是testNpm/index.js文件所 export出的模块。你也可以自己的入口文件 ~~~ >[danger] ##### 先看一下找包机制 * 指定一个具体路径的时候 ~~~ require('./find.js'); require('./find'); 1.require方法根据模块路径查找模块,如果是完整路径,直接引入模块。 1.1.模块后缀省略,先找同名JS文件再找同名JS文件夹 1.2.找到了同名文件夹,找文件夹中的index.js 1.3.如果文件夹中没有index.js就会去当前文件夹中的package.json文件中查找 main选项中的入口文件(具体路径也可以是指定到node_modules)所以找package.json 也是一种情况 1.4.如果找指定的入口文件不存在或者没有指定入口文件就会报错,模块没有被找到 ~~~ * 没有指定一个具体路径 ~~~ require('find'); 1.Node.js会假设它是系统模块,Node.js会去node_modules文件夹中 1.1.首先看是否有该名字的JS文件 1.2.再看是否有该名字的文件夹 1.3.如果是文件夹看里面是否有index.js 1.4.如果没有index.js查看该文件夹中的package.json中的main选项确定模块入口文件 否则找不到报错 ~~~ >[danger] ##### 好文 [package.json 中 你还不清楚的 browser,module,main 字段优先级 - SegmentFault 思否](https://segmentfault.com/a/1190000019438150) >[info] ## bin 指定脚本 ~~~ 很多模块有一个或多个需要配置到PATH路径下的可执行模块,npm让这个工作变得十分 简单(实际上npm本身也是通过bin属性安装为一个可执行命令的) 如果要用npm的这个功能,在package.json里边配置一个'bin'属性。bin属性是一个已命令 名称为key,本地文件名称为value的map如下: ~~~ ~~~ { "bin" : { "myapp" : "./cli.js" } } ~~~ ~~~ 模块安装的时候,若是全局安装,则'npm'会为'bin'中配置的文件在bin目录下创建一个软连 接(对于windows系统,默认会在'C:\\Users\\username\\AppData\\Roaming\\npm'目录 下),若是局部安装,则会在项目内的./node\_modules/.bin/目录下创建一个软链接。 因此,按上面的例子,当你安装'myapp'的时候,npm就会为cli.js在'/usr/local/bin/myapp'路 径创建一个软链接。 如果你的模块只有一个可执行文件,并且它的命令名称和模块名称一样,你可以只写一个 字符串来代替上面那种配置,例如: ~~~ ~~~ { "name": "my-program" , "version": "1.2.5" , "bin": "./path/to/program" } ~~~ 作用和如下写法相同: ~~~ { "name": "my-program" , "version": "1.2.5" , "bin" : { "my-program" : "./path/to/program" } } ~~~ >[danger] ##### 什么是软连接 ~~~ 1.软链接(符号链接)是一类特殊的可执行文件, 其包含有一条以绝对路径或者相对路 径的形式指向其它文件或者目录的引用 ~~~ >[danger] ##### 举个例子 ~~~ 1.'cli-service'的package.json 的bin 告诉了cli 执行脚本位置,在'node_moduels/.bin' 接下来要做的就是在项目的'./node\_modules/.bin/'目录下创建一个软链接。 ~~~ ![](https://img.kancloud.cn/3b/69/3b69fa8db614c8b8e64a28d3c4905684_918x462.png) * node_moduels/.bin ![](https://img.kancloud.cn/4f/81/4f81c6193dad3ddc0437260473506bff_1001x282.png) >[info] ## scripts -- 指令集合 1. **scripts属性用于配置一些脚本命令,以键值对的形式存在** ,配置后我们可以通过 **npm run 命令的key来执行这个命令**,对于常用的 **start、 test、stop、restart可以省略掉run直接通过 npm start等方式运行** 2. 上面的指令是 **`npm run-script <command> [--silent] [-- <args>...]`缩写** 3. **以vuecli为例常见在'scripts'配置的指令**: ~~~ "scripts": { "serve": "vue-cli-service serve", ... } ~~~ >[danger] ##### 查看当前所有可执行脚本列表 ~~~ 1. npm run ~~~ >[danger] ##### 工作原理 ~~~ 1.本质上执行的是'Shell(一般是 Bash)可以运行的命令',shell是依赖于平台的。 默认情况下, Unix-like 操作系统是'/bin/sh'指令, Windows 操作系统是'cmd.exe'。 实际的被'/bin/sh'引用的shell也依赖于平台。'npm@5.1.0'你可以使用'script-shell'自定义你的shell配置。 ~~~ >[warning] ### 可以运行脚本类型 ~~~ 1.查看执行'shell' 脚本位置 使用指令'npm config get shell',执行完后我的本机配置环境 输出结果为'C:\Windows\system32\cmd.exe',就说明实际是'window'系统的'cmd'命令行工 具。 ~~~ >[danger] ##### 第一种内部自带指令 ~~~ 1.在win 实际运行在'cmd'中执行的命令,因此系统cmd的内部命令,不需要安装额外的插 件,就可以直接执行,在'npm'的'scripts'中都可以执行,举个例子: "scripts":{ /*系统命令*/ "ip":"ipconfig" } ~~~ >[danger] ##### 第二种执行外部命令 ~~~ 1.我们如果安装了node,git等客户端,可以直接在cmd窗口执行(需配置了系统的环境变量) 举个例子当安装了 node 后,我们可以直接在控制台输入'node -v' 来查看node 版本信息 因此也可以执行下面列子 "scripts":{ /*全局外部命令*/ "git":"git --version", "node":"node -v", } ~~~ >[danger] ##### 第三种执行项目内部 ~~~ 1.当我们安装类似'vuecli'或者'webpack','eslint' 这些项目内部使用的,每当执行npm run 时,会自动新建一个Shell,这个 Shell会将当前项目的'node_modules/.bin'的绝对路径加 入到环境变量PATH中,执行结束后,再将环境变量PATH恢复原样。 可以理解为前目录的’node_modules/.bin'子目录里面的所有脚本,都可以直接用脚本名 调用,而不必加上路径。比如,当前项目的依赖里面有 Mocha,只要直接写'mocha test' 就可以了。 {"test": "mocha test"} 等同于 {"test": "./node_modules/.bin/mocha test"} ~~~ >[danger] ##### 疑问全局安装的某些包和非全局安装时候执行 * 非全局安装的时候 ~~~ 1.非全局安装的时候,当想执行eslint 时候需要我们在scripts 标签配置好脚本,举个例子 scripts:{"eslint-version":"eslint --version"} ,只需要执行'npm run eslint-version',具体为什么会执行 参考上面章节中"第三种执行项目内部"讲的内容 2.如果不想配置'scripts' 其他的执行方法" .\node_modules\.bin\eslint.cmd --version" 直接指定运行 "node_modules\.bin" 文件下的脚本 或者 'node .\node_modules\eslint\bin\eslint.js --version' 直接 具体到运行的脚本目录 ~~~ * 全局安装时 ~~~ 1.全局安装一些包例如'eslint' 直接执行'eslint' 就可以在全局运行,这是因为你在全局 安装时候会在node 的文件所在目录自动添加一个执行shell 脚本,并且node 路径在 系统path 中因此可以直接调用 2.什么是'PATH'环境变量,是告诉系统,当要求系统运行一个程序而没有告诉它程序所在 的完整路径时,系统除了在当前目录下面寻找此程序外,还应到哪些目录下去寻找。 ~~~ * 全局的path ![](https://img.kancloud.cn/9f/97/9f976f46c7484a090eef5ebc485b47d3_702x94.png) * 这个路径下能用的指令以我本机为例 ![](https://img.kancloud.cn/58/b4/58b4cf70626d8ad2519c1953ec6396d3_261x580.png) * 看一下这个脚本内部(实际执行全局node_modules 下bin文件eslint 指令) ![](https://img.kancloud.cn/0c/98/0c987c770eee74880ee113df229ea566_685x416.png) >[warning] ### 通配符 ~~~ 1.由于 npm 脚本就是 Shell 脚本,因为可以使用 Shell 通配符。 我们在写脚本命令的时候,常常要匹配文件,这就要用到路径的通配符。 总的来说*表示任意字符串,在目录中表示1级目录,**表示0级或多级目录,例如: 1.1.src/*:src目录下的任意文件,匹配 src/a.js; src/b.json;不匹配src/aa/a.js 1.2.src/*.js:src目录下任何js文件,匹配 src/a.js; 不匹配 src/b.json;src/aa/a.js 1.3.src/*/*.js:src目录下一级的任意js文件,匹配 src/aa/a.js; 不匹配src/a.js;src/a/aa/a.js 1.4.src/**/*.js:src目录下的任意js文件,匹配 src/a.js; src/a/a.js; src/a/aa/a.js ~~~ >[warning] ### 传参 ~~~ 1.举个例子比如我们使用的'vuecli' 时候,在当前项目的'node_moduels/.bin' 去查看 一下执行脚本如图,在这里一定要明白'npm run '在执行对应的'scripts' 是对应的执行 窗口调用响应命令而非npm 去调用,是npm 去将具体调用shell指令给到了执行窗口 因此其实'vue-cli-service' 执行时候其实是node 执行,'node' 调用时候可以通过 'process.argv' 获取一个返回的数组 2.关于'process.argv',这个数组包含了启动node进程时的命令行参数。第一个元素为启 动node 进程的可执行文件的绝对路径名process.execPath,第二个元素为当前执行的 JavaScript文件路径。剩余的元素为其他命令行参数。如下图 3.来分析一组指令 "scripts": { "serve": "vue-cli-service serve --mode=dev --mobile -config build/example.js" } 当执行'npm run serve' 实际执行的是 'node "node_modules/@vue\cli-service\bin\vue-cli-service.js --mode=dev --mobile -config build/example.js" ' vue-cli-service.js 这个js 程序理论上只要执行'process.argv' 即可获取传递参数 可以获取的打印结果如下: [ '/usr/local/Cellar/node/7.7.1_1/bin/node', '/Users/mac/Vue-projects/hao-cli/node_modules/.bin/vue-cli-service', 'serve', '--mode=dev', '--mobile', '-config', 'build/example.js'] 实际很多命令行包之所以这么写,都是依赖了 minimist 或者 yargs 等参数解析工具来对 命令行参数进行解析。 其中以 'vue-cli-service ' 为例内部使用'minimist' 进行了解析 ~~~ * vue-cli-service 脚本 ![](https://img.kancloud.cn/1e/99/1e997a97c0ebdb329bed662ec3b6948c_708x203.png) * 运行一个node 文件 ![](https://img.kancloud.cn/dd/0b/dd0becd80d8ca821da71fb5e0ac0eeff_797x398.png) >[danger] ##### 总结 ~~~ 1.npm 其实本身也是通过node 来运行的关于运行脚本阶段参数的传递实际都是利用 'process'这个参数来做的,下图的脚本被配置在'spricts' 中当运行的时候可以发现npm 创建了很多'npm_package_'前缀,加入到'process.env'的变量中,如果你看了下面想了解 更多的文章也可以发现其实通过vuecli 传递的参数也是会加到''process.env.某个自定义' 变量来储存的 1.1.'npm内部变量' 当我们在执行npm命令的时候,就会把package.json的参数加上npm_package_前缀, 加入到process.env的变量中,所以在上面的node代码可以通过 process.env.npm_package_name获取到package.json里面配置的name属性。 1.2.'npm 命令参数'(npm 自己api 提供的命令参数) 当我们在运行npm命令时,带上以双横线为后缀的参数:npm 命令 --xx=xx,npm就会 把xx加上npm_config_前缀,加入到process.env变量中,如果原来有同名的,命令参数 的优先级最高,会覆盖掉原来的,所以在上面的node代码可以通过 process.env.npm_config_env获取到npm run node --env=npmEnv命令里的参数env的 值,如果参数没有赋值:npm run node --env,则默认值为true 1.3.'脚本参数' 这个其实要根据脚本的内容来看,比如我们上面的脚本是node index.js --env=node,这其 实是纯粹的node命令了,可以通过process.argv来获取node的命令参数,这是个数组, 第一个为node命令路径,第二个为执行文件路径,后面的值为用空格隔开的其他参数, ~~~ ![](https://img.kancloud.cn/62/b0/62b0d1733de64fca625a51a2b25ae22b_588x253.png) * npm run 时候 env ~~~ 1.在执行npm run脚本时,npm会设置一些特殊的env环境变量。其中package.json中的所 有字段,都会被设置为以npm_package_ 开头的环境变量,同时,`npm`相关的所有配置 也会被设置为以`npm_config_`开头的环境变量 看个例子 { "name": "sh", "version": "1.1.1", "description": "shenhao", "main": "index.js", "repository": { "type": "git", "url": "git+ssh://git@gitlab.com/xxxx/sh.git" } } 可以通过process.env.npm_package_name 可以获取到package.json中name字段的值 sh,也可以通过process.env.npm_package_repository_type获取到嵌套属性type的值git ~~~ >[warning] ## 多命令执行 ~~~ 当然命令不仅仅可以执行一条也可以多条按照某种顺序来执行 1.串行执行,要求前一个任务执行成功以后才能执行下一个任务,使用&&符号来连接。 npm run script1 && npm run script2 2.并行执行,就是多个命令可以同时的平行执行,使用&符号来连接。 npm run script1 & npm run script2 ~~~ >[warning] ### 钩子 ~~~ 1.npm脚本有pre,post两类钩子,一个是执行前,一个是执行后。举个例子例如我想 给我在scripts 中定义的server 脚本增加钩子,分别在原有指令前增加了'pre' 和'post' 前缀 "scripts": { "preserve": "xxxxx", "serve": "vue-cli-service serve", "postserve": "xxxxxx" } 在执行npm run serve命令时,会依次执行npm run preserve、npm run serve、 npm run postserve 2.通常时候可以发现我们并没有去指定,会默默的跳过。如果想要指定钩子, 必须严格按照'pre'和'pos't前缀来添加 3.'process.env.npm_lifecycle_event'可以配合钩子来一起使用 const event = process.env.npm_lifecycle_event if (event === 'preserve') { console.log('Running the preserve task!') } else if (_event === 'serve') { console.log('Running the serve task!') } ~~~ >[info] ## peerDependencies属性 1. 还有一种项目依赖关系是对等依赖**,也就是你依赖的一个包,它必须是以另外一个宿主包为前提的**,例如当使用**element-plus是依赖于vue3的** ~~~ "peerDependencies": { "vue": "^3.2.0" }, ~~~ >[info] ## engines属性 1. engines属性用于指定Node和NPM的版本号; 2. 在安装的过程中,会先检查对应的引擎版本,如果不符合就会报错; 3. 事实上也可以指定所在的操作系统 "os" : \[ "darwin", "linux" \],只是很少用到; >[info] ## 参考文章 [npm-run-script - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/66793538) [npm基本用法及原理(10000+) - 漫漫字节|漫漫编程 (mmbyte.com)](https://www.mmbyte.com/article/185966.html) [npm scripts 使用指南 - 阮一峰的网络日志 (ruanyifeng.com)](http://www.ruanyifeng.com/blog/2016/10/npm_scripts.html) [置之process.env](https://www.jianshu.com/p/19d199f93045) [我想对vuecli 了解更多](https://blog.csdn.net/k464746/article/details/109066209) [npm package.json属性详解](https://www.cnblogs.com/tzyy/p/5193811.html#_h1_5) [关于版本](https://www.npmjs.cn/misc/semver/) [package.json 指南 ](http://nodejs.cn/learn/the-package-json-guide) [前端工程化 - 剖析npm的包管理机制 (juejin.cn)](https://juejin.cn/post/6844904022080667661#heading-24) [前端工程化(5):你所需要的npm知识储备都在这了](https://juejin.cn/post/6844903870578032647#heading-11)