[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)
- 工程化 -- Node
- vscode -- 插件
- vscode -- 代码片段
- 前端学会调试
- 谷歌浏览器调试技巧
- 权限验证
- 包管理工具 -- npm
- 常见的 npm ci 指令
- npm -- npm install安装包
- npm -- package.json
- npm -- 查看包版本信息
- npm - package-lock.json
- npm -- node_modules 层级
- npm -- 依赖包规则
- npm -- install 安装流程
- npx
- npm -- 发布自己的包
- 包管理工具 -- pnpm
- 模拟数据 -- Mock
- 页面渲染
- 渲染分析
- core.js && babel
- core.js -- 到底是什么
- 编译器那些术语
- 词法解析 -- tokenize
- 语法解析 -- ast
- 遍历节点 -- traverser
- 转换阶段、生成阶段略
- babel
- babel -- 初步上手之了解
- babel -- 初步上手之各种配置(preset-env)
- babel -- 初步上手之各种配置@babel/helpers
- babel -- 初步上手之各种配置@babel/runtime
- babel -- 初步上手之各种配置@babel/plugin-transform-runtime
- babel -- 初步上手之各种配置(babel-polyfills )(未来)
- babel -- 初步上手之各种配置 polyfill-service
- babel -- 初步上手之各种配置(@babel/polyfill )(过去式)
- babel -- 总结
- 各种工具
- 前端 -- 工程化
- 了解 -- Yeoman
- 使用 -- Yeoman
- 了解 -- Plop
- node cli -- 开发自己的脚手架工具
- 自动化构建工具
- Gulp
- 模块化打包工具为什么出现
- 模块化打包工具(新) -- webpack
- 简单使用 -- webpack
- 了解配置 -- webpack.config.js
- webpack -- loader 浅解
- loader -- 配置css模块解析
- loader -- 图片和字体(4.x)
- loader -- 图片和字体(5.x)
- loader -- 图片优化loader
- loader -- 配置解析js/ts
- webpack -- plugins 浅解
- eslit
- plugins -- CleanWebpackPlugin(4.x)
- plugins -- CleanWebpackPlugin(5.x)
- plugin -- HtmlWebpackPlugin
- plugin -- DefinePlugin 注入全局成员
- webapck -- 模块解析配置
- webpack -- 文件指纹了解
- webpack -- 开发环境运行构建
- webpack -- 项目环境划分
- 模块化打包工具 -- webpack
- webpack -- 打包文件是个啥
- webpack -- 基础配置项用法
- webpack4.x系列学习
- webpack -- 常见loader加载器
- webpack -- 移动端px转rem处理
- 开发一个自己loader
- webpack -- plugin插件
- webpack -- 文件指纹
- webpack -- 压缩css和html构建
- webpack -- 清里构建包
- webpack -- 复制静态文件
- webpack -- 自定义插件
- wepack -- 关于静态资源内联
- webpack -- source map 对照包
- webpack -- 环境划分构建
- webpack -- 项目构建控制台输出
- webpack -- 项目分析
- webpack -- 编译提速优护体积
- 提速 -- 编译阶段
- webpack -- 项目优化
- webpack -- DefinePlugin 注入全局成员
- webpack -- 代码分割
- webpack -- 页面资源提取
- webpack -- import按需引入
- webpack -- 摇树
- webpack -- 多页面打包
- webpack -- eslint
- webpack -- srr打包后续看
- webpack -- 构建一个自己的配置后续看
- webpack -- 打包组件和基础库
- webpack -- 源码
- webpack -- 启动都做了什么
- webpack -- cli做了什么
- webpack - 5
- 模块化打包工具 -- Rollup
- 工程化搭建代码规范
- 规范化标准--Eslint
- eslint -- 扩展配置
- eslint -- 指令
- eslint -- vscode
- eslint -- 原理
- Prettier -- 格式化代码工具
- EditorConfig -- 编辑器编码风格
- 检查提交代码是否符合检查配置
- 整体流程总结
- 微前端
- single-spa
- 简单上手 -- single-spa
- 快速理解systemjs
- single-sap 不使用systemjs
- monorepo -- 工程
- Vue -- 响应式了解
- Vue2.x -- 源码分析
- 发布订阅和观察者模式
- 简单 -- 了解响应式模型(一)
- 简单 -- 了解响应式模型(二)
- 简单 --了解虚拟DOM(一)
- 简单 --了解虚拟DOM(二)
- 简单 --了解diff算法
- 简单 --了解nextick
- Snabbdom -- 理解虚拟dom和diff算法
- Snabbdom -- h函数
- Snabbdom - Vnode 函数
- Snabbdom -- init 函数
- Snabbdom -- patch 函数
- 手写 -- 虚拟dom渲染
- Vue -- minVue
- vue3.x -- 源码分析
- 分析 -- reactivity
- 好文
- grpc -- 浏览器使用gRPC
- grcp-web -- 案例
- 待续