[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/'目錄下創建一個軟鏈接。
~~~

* node_moduels/.bin

>[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

* 這個路徑下能用的指令以我本機為例

* 看一下這個腳本內部(實際執行全局node_modules 下bin文件eslint 指令)

>[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 腳本

* 運行一個node 文件

>[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命令路徑,第二個為執行文件路徑,后面的值為用空格隔開的其他參數,
~~~

* 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 -- 案例
- 待續