傳送門: to My Blogjavascript
這個話題的目的實際上是想深度的挖一挖 package.json 這個文件都有哪些屬性、均可覺得咱們提供哪些解決問題的方法。目前前端工程化最離不開的就是 package.json 這個文件了,以這個文件爲出發點,構造了龐大的前端摩天大樓。這篇文章的前半段大都是枚舉 package.json 的各類屬性,請擇食。後面有類最佳實踐的內容,最後是你們關心的 package.json 的進化體 -- package-lock.json。css
1. 想要完全瞭解 package.json 首先須要枚舉它的屬性。html
2. 以後,咱們會了解這個文件在工程化中起到了多麼重要的做用。前端
3. 在深度認識這個文件以後,咱們要考慮的是如何用好它,爲咱們的工程化鋪平道路。vue
4. Let's Go !java
5. 以後瞭解一下最關鍵的 scripts 能夠進行什麼騷操做。node
6. Package.json 是怎麼變成 package-lock.json 和 yarn.lock 的。linux
任何理性的知識整理都是根據時間線索從它的源頭開始的,咱們第一次接觸到 package.json 這個文件的時候應該是從NPM這個包管理工具開始的。NPM 表面來看它可能只是一個包管理工具,其實本質上是前端工程化的一個統一的解決方案。NPM 通過幾回大版本的迭代和中途 yarn 的衝擊,已經變成了一個及其成熟的系統了。目前NPM的包數量在193K個,僅次於Go。git
那麼問題來了,NPM是如何提供解決方案的呢?固然是提供了良好的包管理方案了,上古時期的前端模塊劃分是經過js/css/html/img進行文件分類的,這樣的按文件劃分方案的不足可想而知,沒有清晰的功能模塊概念,複用困難。github
NPM 是如何管理模塊的呢?NPM的推出,將具備統一功能的一些js/html等放到一個Package裏,這些Package統一的執行,達到這個Package開發者開發時候的功能,積極面向對象編程。
Package愈來愈多怎麼辦?隨着開發者創造力的提高,Package會愈來愈多,NPM同時提供了一個NPM源爲世界上的開發者同事提供下載安裝使用的服務。
單個Package在沒有安裝下載以前不知道其功能的怎麼辦?單個包功能不清,NPM就提供了應對策略 -- Package.json 文件,進行規範的包內容介紹,規範的依賴關係,規範的內置腳本執行,許可規範等。README文件,爲這個包提供功能顯示。NPM也默認讀取項目根目錄下的 README.md 進行展現。
所以 package.json 實際上是NPM對於包管理和項目管理的一種規範。也是爲開發者減小沒必要要的理解負擔,經過package.json 的豐富屬性能夠很好的解決並度過前端工程規範的初級階段。
經過 package.json 的起源瞭解到,package.json 的屬性都是NPM對於工程化的規範的產物,既然規則是NPM指定的,那麼咱們想要獲得NPM對於包管理的便利條件,也就一樣須要遵照這樣的規範。這些規範的細節提現就在package.json 這個文件提供的屬性上面。
NPM 官方 package.json文檔: docs.npmjs.com/files/packa… 在官方文檔的幫助下能夠快速的瞭解其豐富屬性。
package.json 會受到NPM配置的影響
若是你的工程想要公佈在NPM網站上供全世界訪問和使用,name和version這個字段能夠說是兩個P0級別的屬性且是必須的,由於這兩個屬性是搜索的必須條件。若是是本身開發的項目沒有Publish的計劃,這兩個屬性是可選的,咱們能夠用這兩個屬性去標識咱們的代碼和項目。
• 既然NPM是規範的制定者,固然也爲這個屬性增長了一些規範,須要瞭解:
1. 名稱必須小於或等於214個字符。
2. 名稱不能以點或下劃線開頭。
3. 名稱不能含有大寫字母。
4. 該名稱最終成爲URL的一部分,命令行上的參數和文件夾名稱。所以,名稱不能包含任何非URL安全字符。
• 一些官方建議:
請勿使用與核心節點模塊相同的名稱。
不要在名稱中加上「js」或「node」
該名稱可能會做爲參數傳遞給require(),所以它應該是簡短的,但也是合理描述的。
起名的時候能夠先查看 npmjs.org 是否有命名重複
• 類最佳實踐Tips:
使用 @ 開頭: 例如: @vue/cli,
使用短線鏈接 a-b-c
不適用 js/node。若是想要區分引擎,在對應的屬性裏面限制便可。
命名可使用npm-scope命名做用域做爲開頭。
綜上: name屬性是在publish 的時候很關鍵的屬性。能夠有效區分咱們的項目。
和name同樣,若是想要publish你的package,version 和 name 會一塊兒組裝造成這個代碼包的npm平臺識別標識,npm官方但願更新內容應該隨着版本一塊兒提供。不發包的狀況下,version是可選的。對於version版本號具體的規範,不在本話題討論範圍內。
• 一些官方建議:
• Tips:
2. node-semver:對於version 官方提供了一個npm插件解決這個問題。有一點的學習成本。
這個屬性做爲一個程序包的說明使用,其值是一個字符串,用來描述這個項目。對於publish的項目來講,這個屬性,影響用戶在npmjs.org的搜索。 npm search
例如: vue.js 的description:
// Vue 的package.json 的描述。
{
"description": "Reactive, component-oriented view layer for modern web interfaces.",
}
複製代碼
這個屬性就比較日常了,就像你在寫一篇文檔的時候,須要提取一些關鍵詞同樣,這個屬性是一個字符串數組。僅此而已。
項目主頁的URL地址。 字符串。
對於這個屬性的值,最好是項目地址的文檔目錄。例如GitHub的ReadMe
{
"homepage": "https://github.com/owner/project#readme"
}
複製代碼
bugs是一個對象,表示使用者遇到問題時候及時獲取幫助。若是隻想提供一個地址URL,能夠是一個字符串。有兩個屬性:
1. url
2. email
"bugs": {
"url": "https://github.com/owner/project/issues",
"email": "project@hostname.com"
}
複製代碼
若是是單許可證,是一個字符串。
若是是多許可證,是一個對象數組。
最多見的許可證是: MIT 。
對於許可證的相關姿式,詳見: 推薦閱讀 做爲前端,你須要瞭解的開源協議知識
做者和貢獻者,單獨開發者使用 author, 一羣人的共同開發請使用 contributors 貢獻者。有name, email, url 三個屬性可選。
{
"name": "Barney Rubble",
"email": "b@rubble.com",
"url" : "http://barnyrubble.tumblr.com/"
}
複製代碼
例如: "Barney Rubble <b@rubble.com> (barnyrubble.tumblr.com/)"
• files 屬性的值是一個數組
• 內容是模塊下的文件名和文件夾名,這些被列舉的文件會被包含在npm模塊中。
• 可使用 .npmignore 文件進行文件排除,這個配置的優先級大於 files 屬性的值。
• .npmignore 會存在於項目的根目錄中。
• 如下的內容會被自動永久默認的包含在files中: ◦ package.json ◦ README ◦ CHANGES / CHANGELOG / HISTORY ◦ LICENSE / LICENCE ◦ NOTICE ◦ main 屬性中的文件
• 某些文件會被NPM自動忽略: ◦ .git ◦ CVS ◦ .svn ◦ .hg ◦ .lock-wscript ◦ .wafpickle-N ◦ ..swp ◦ .DS_Store ◦ ._ ◦ npm-debug.log ◦ .npmrc ◦ node_modules ◦ config.gypi ◦ *.orig ◦ package-lock.json (use shrinkwrap instead)
• main 字段的值是一個字符串
• main 的內容是這個模塊的主入口文件相對於package.json的文件路徑。
• 當其餘項目使用 import/require 等進行引入的時候,其實就是引用 main 的文件的 export 內容。
• 其實在main的對應文件中,你只須要提供導出的主要代碼,不須要長篇大論的實現。
若是您的模塊要在客戶端使用,則應使用瀏覽器字段而不是主字段。這有助於提示用戶它可能依賴於Node.js模塊中不可用的基元。(例如window)
• bin 屬性的值是一個對象。
• bin 適用於在代碼中有可執行文件的時候。意思是若是你的模塊有獨立的命令須要執行的時候,可使用這個屬性添加自定義的腳本。
• 如下代碼中, test.js 是一個可執行的腳本,在這個文件的開頭須要使用 #!/usr/bin/env node
// package.json
{
"bin":{
"test" : "./test.js"
}
}
複製代碼
• 在安裝帶有bin屬性的模塊時,若是是全局安裝,可執行文件會被註冊到 xxx/bin。
• 若是是普通安裝,全部的可執行文件都會註冊到 ./node_modules/.bin/下。
• man 屬性的值是一個字符串或者是一個數組。
• man 的用法很相似與 shell 命令的 man
• man 也是爲了給咱們的模塊提供一個可供理解的doc。
• direcotries 的值是一個對象。
• direcotries 的配置目前沒有功能性,只具有解釋性。
• direcotries 目前用來解釋各個文件在哪裏
• 如 : direcotries.lib 指定lib目錄位置
• 除了 lib 以外,npm的direcotries還分別配置瞭如下位置:
• repository 是一個對象。
• repository 指定代碼的提交目錄,對想提PR的人有幫助。
• scripts 是一個對象。
• scripts 用來編寫模塊中使用的腳本。
• npm 在一些階段提供了對 scripts 的鉤子,能夠在其中使用。
• config 是一個對象。
• config 是模塊中常駐的配置項。
• npm 提供了使用和修改config的方法。
• dependencies 的值是一個對象。
• dependencies 用於配置模塊依賴的模塊列表。key是模塊名稱,value是版本範圍
• dependencies 是生產環境的依賴,不要把測試工具或transpilers寫到dependencies中。
• dependencies 的版本範圍有不少種寫法。
• URL 做爲依賴
◦ 將在安裝時下載並安裝在您的軟件包本地。
• Git URL
◦ 規則: <protocol>://[<user>[:<password>]@]<hostname>[:<port>][:][/]<path>[#<commit-ish> | #semver:<semver>]
◦ demo:
git+ssh://git@github.com:npm/cli.git#v1.0.27
git+ssh://git@github.com:npm/cli#semver:^5.0
git+https://isaacs@github.com/npm/cli.git
git://github.com/npm/cli.git#v1.0.27
• GitHub URLs
◦ 前面和Git Urls 同樣,後面能夠加 commit 後綴。
• Local Paths
◦ "bar": "file:../foo/bar"可使用本地的代碼路徑。
• 用戶下載安裝你發佈的模塊,你的某些依賴不但願被引用。或者這些只是在你開發的時候給予你便利使用
• 在業務項目中,這部份內容只用於dev開發中,不在生產環境使用。
• 在部分的引用依賴須要使用npm link或者npm install進行安裝。
有時候作一些插件開發,好比grunt等工具的插件,它們每每是在grunt的某個版本的基礎上開發的,而在他們的代碼中並不會出現require("grunt")這樣的依賴,dependencies配置裏邊也不會寫上grunt的依賴,爲了說明此模塊只能做爲插件跑在宿主的某個版本範圍下,能夠配置peerDependencies:
{
"name": "tea-latte",
"version": "1.3.5",
"peerDependencies": {
"tea": "2.x"
}
}
複製代碼
上面這個配置確保再npm install的時候tea-latte會和2.x版本的tea一塊兒安裝,並且它們兩個的依賴關係是同級的:
├── tea-latte@1.3.5
└── tea@2.2.0
這個配置的目的是讓npm知道,若是要使用此插件模塊,請確保安裝了兼容版本的宿主模塊。
• 是個數組。
• 捆綁須要一塊兒安裝的內容。
若是可使用依賴項,可是若是沒法找到或沒法安裝,您但願npm繼續,那麼您能夠將它放在optionalDependencies 對象中。這是包名稱到版本或URL的映射,就像 dependencies對象同樣。不一樣之處在於構建失敗不會致使安裝失敗。
• 指定您的東西所使用的node的版本。
• 版本範圍和依賴安裝保持一致。
• node版本: { "engines" : { "node" : ">=0.10.3 <0.12" } }
• npm 版本: { "engines" : { "npm" : "~1.0.20" } }
• 模塊設置了engine-strict配置標誌,會產生警告。
• 此功能已在npm 3.0.0中刪除
• 在npm 3.0.0以前,此功能用於處理此程序包,就像用戶已設置同樣engine-strict。它已再也不使用。
• 指定操做系統
• "os" : [ "darwin", "linux" ]
• "os" : [ "!win32" ]
• 原理是使用node 的 process.platform來判斷。
• 若是您的代碼僅在某些cpu體系結構上運行,則能夠指定哪些代碼。
• "cpu" : [ "x64", "ia32" ]
• 與os選項同樣,您也能夠將架構列入黑名單:
• "cpu" : [ "!arm", "!mips" ]
• 主機架構由肯定 process.arch
• npm 只會發佈 "private": false的包。
• 能夠指定爲 true 來進行標記。防止誤發佈。
這是一組將在發佈時使用的配置值。若是要設置標記,註冊表或訪問權限,則特別方便,以便確保給定的包未標記爲「latest」,發佈到全局公共註冊表或默認狀況下做用域模塊是私有的。
能夠覆蓋任何配置值,但只有「tag」,「registry」和「access」可能對於發佈而言很重要。
請參閱npm-config以查看能夠覆蓋的配置選項列表。
例如: "scripts": {"start": "node server.js"}
例如: "scripts":{"install": "node-gyp rebuild"}
例如: "contributors": [...]
若是AUTHORS包的根目錄中有文件,則npm會將每一行視爲一種Name (url)格式,其中email和url是可選的。以a #或空白開頭的行將被忽略。
這個話題得從一個使人失望的事情提及,在一段時間之前咱們使用npm安裝依賴的時候,會出現這樣一個現象。 咱們的項目只依賴於A,A依賴於B,B依賴於C。會出現下面的依賴樹
-- A @0.1.0 -- B @0.0.1 -- C @0.0.1
這個時候B發生了版本更新,就會致使B的版本發生變化,也就是說咱們的整個依賴樹都發生了變化。這種現象有的時候會致使不少不可預知的問題。因此引入了一個臨時過分的方案 -- npm shrinkwrap
咱們打開npm的package code,發現生成 package-lock.json 和 生成 shrinkwrap.json 的代碼處於同一處,查看npm的config的時候能夠更加肯定, package-lock是shrinkwrap.json 的替代品,這一方法在 npm5 以上獲得的更新。這一個更新靈感可能來自於 yarn.lock 。
咱們繼續看 config 中和這兩個文件有關係的屬性內容。
config.shrinkwrap
config.package-lock
從以上doc中能夠得出的一個簡單的結論: shrinkwrap 和 package-lock 是同一個東西,這將幫助咱們理解 NPM 是如何生成Package-lock.json 的。
在npm的自身module中,生成 package-lock 和 shrinkwrap 的代碼同處於: npm/lib/shrinkwrap.js 中。在這個代碼module中,使用了ES6 的module,對外exports了兩個方法
treeToShrinkwrap:
function treeToShrinkwrap (tree) {
validate('O', arguments)
var pkginfo = {}
if (tree.package.name) pkginfo.name = tree.package.name
if (tree.package.version) pkginfo.version = tree.package.version
if (tree.children.length) {
pkginfo.requires = true
shrinkwrapDeps(pkginfo.dependencies = {}, tree, tree)
}
return pkginfo
}
複製代碼
createShrinkwrap:
function createShrinkwrap (tree, opts, cb) {
opts = opts || {}
lifecycle(tree.package, 'preshrinkwrap', tree.path, function () {
const pkginfo = treeToShrinkwrap(tree)
chain([
[lifecycle, tree.package, 'shrinkwrap', tree.path],
[shrinkwrap_, tree.path, pkginfo, opts],
[lifecycle, tree.package, 'postshrinkwrap', tree.path]
], iferr(cb, function (data) {
cb(null, pkginfo)
}))
})
}
複製代碼
package-lock.json 每次 npm install / uninstall / update 都會去更新。
pacakge-lock 是npm 自動觸發的。 shrinkwrap 是須要手動 npm shrinkwrap 的。
package-lock 是生成 node_modules 的依據,也是肯定每次的 node_modules 相同的保證。
它描述了生成的確切樹,以便後續安裝可以生成相同的樹。
package-lock.json和npm-shrinkwrap.json同時存在於包的根,package-lock.json將被徹底忽略。
package-lock 和 shrinKwrap 本質上應屬於統一個文件,他們擁有相同的文件格式。
文件格式:
▪ 解釋: 包的名稱,這是一個包鎖。這必須與之相符 package.json。
▪ srouce:
▪ 來自於 treeToShrinkwrap 方法的 if (tree.package.name) pkginfo.name = tree.package.name。
▪ tree.package.name的package 的賦值在 npm/lib/install 的 readPackageJson 方法。
▪ 綜上: 這裏的name 是從 package.json 的 name 字段獲取的。
▪ 這個包的版本。也是來自於 package.json。
▪ if (tree.package.version) pkginfo.version = tree.package.version
▪ 一個整數版本從1開始。
▪ 來自於 pkginfo.lockfileVersion = PKGLOCK_VERSION
▪ PKGLOCK_VERSION 是 npm/lib/npm.js中定義的 從 1開始。
◦ pkginfo.lockfileVersion 除了再npm.js 中定義以外還在 lib/install/read-shrinkwrap.js中 進行過比對。
if (parsed && parsed.lockfileVersion !== PKGLOCK_VERSION) {
log.warn('read-shrinkwrap', `This version of npm is compatible with lockfileVersion@${PKGLOCK_VERSION}, but ${name} was generated for lockfileVersion@${parsed.lockfileVersion || 0}. I'll try to do my best with it!`)
}
複製代碼
▪ 聽說是驗證完整性的。
▪ 表示安裝已在NODE_PRESERVE_SYMLINKS啓用環境變量的狀況下完成 。安裝程序應該堅持認爲此屬性的值與該環境變量匹配。
▪ version
▪ 安裝這個包的版本。
▪ 這是此資源的標準子資源完整性。
▪ 資源的URL路徑
▪ 根據package.json 中依賴項version來源的不一樣而有區別
▪ bundled
▪ 若是爲true,則這是捆綁的依賴項,將由父模塊安裝。安裝時,此模塊將在提取階段從父模塊中提取,而不是做爲單獨的依賴項安裝。
▪ 若是爲true,則此依賴關係僅是頂級模塊的開發依賴關係或者是一個的傳遞依賴關係。對於依賴性而言,這是錯誤的,這些依賴性既是頂級的開發依賴性,也是頂級的非開發依賴性的傳遞依賴性。
▪ 若是爲true,那麼此依賴關係或者只是頂級模塊的可選依賴項或者是一個的傳遞依賴項。對於依賴項來講這是錯誤的,這些依賴項既是頂級的可選依賴項,也是頂級的非可選依賴項的傳遞依賴項。
▪ 這是模塊名稱到版本的映射。這是該模塊所需的全部內容的列表,不管它將在何處安裝。版本應該經過正常的匹配規則匹配咱們dependencies或高於咱們的級別的依賴 。
▪ 此依賴項的依賴項,與頂層徹底相同。
不少時候咱們無心識的將package-lock.json放入.gitignore 中,相信讀過這篇文章以後,你應該把他解禁了。
only 修改你的pacakge.json 並不會當即同步觸發package-lock.json ,須要手動 npm install 一次,若是你忽略掉了這個步驟,你的package-lock就不會達到你想要的結果。
想同步修改package.json和package-lock.json 請使用 npm install xxx --save 命令。
不少同窗是使用NVM安裝node.js 的,這樣的好處是能夠快速的切換node 版本,避免由於node版本帶來的問題。
• node路徑: /Users/xxx/.nvm/versions/node/v10.14.1/bin/node
• npm路徑: /Users/xxx/.nvm/versions/node/v10.14.1/bin/npm
• 以上路徑沒有找到的狀況下: 使用 where node來查找