天天看點

深入了解包與NPM

包與NPM

CommonJS的包規範的定義其實也十分簡單,它由包結構和包描述檔案兩個部分組成,前者用于組織包中的各種檔案,後者則用于描述包的相關資訊,以供外部讀取分析。

包結構

包實際上是一個存檔檔案,即一個目錄直接打包為 .zip 或 tar.gz 格式的檔案,安裝後解壓還原為目錄。完全符合CommonJS規範的包目錄應該包含如下檔案。

  1. package.json: 包描述檔案。
  2. bin:用于存放可執行二進制檔案的目錄。
  3. lib:用于存放JavaScript代碼的目錄。
  4. doc:用于存放文檔的目錄。
  5. test:用于存放單元測試用例的代碼。

可以看到,CommonJS包規範從文檔、測試等方面都做過考慮。當一個包完成後向外公布時,使用者可以看到單元測試和文檔時,會給人一種踏實可靠的感覺。

包描述檔案

包描述檔案用于表達非代碼相關的資訊,它是一個JSON格式的檔案——package.json,位于包的根目錄下,是包的重要組成部分。而npm的所有行為都與包描述檔案的字段息息相關。

CommonJS為package.json檔案定義一些必須的字段。

  1. name:包名。規範定義它需要小寫字元和數字的組成,可以包含. \ _和 - ,但不允許出現空格。包名必須是唯一的,以免對外公布時産生重名沖突的誤解。除此以外,NPM還建議不要在包名中附帶上node或js來重複表示它是JavaScript或Node子產品。
  2. description:包簡介。
  3. version:版本号。
  4. keywords:關鍵詞數組,NPM中主要用來做分類搜尋。一個好的關鍵詞數組有利于使用者快速找到你編寫的包。
  5. maintainers:包維護者清單,每個維護者由name、email和web這3個屬性組成。

NPM通過該屬性進行權限認證。

  1. contribuctors:貢獻者清單。
  2. bugs:一個可以回報bug的網頁位址或郵箱位址。
  3. licenses:目前包所使用的許可證清單,表示這個包在哪些許可證下使用。
  4. repositories:托管源代碼的位置清單,表明可以通過哪些方式和位址通路包的源代碼。
  5. dependencies:使用目前包所依賴的包清單。這個屬性非常重要,NPM會通過這個屬性自動加載依賴的包。

除了必選字段外,規範還定義了一部分可選的字段

  1. homepage:目前包的網站位址。
  2. os:作業系統支援的清單。這些作業系統的取值包括aix,freebsd,Linux,macos,solaris,vxworks,windows。如果設定了清單為空時,則不對作業系統做任何假設。
  3. CPU:CPU架構的支援清單。
  4. engine:支援的js引擎清單,有效的引擎清單取值包括ejs,flusspferd,gpsee,jsc,spidermonkey,narwhal,node,v8.
  5. builtin:标志目前包是否是内建在底層系統的标準元件。
  6. directories:包目錄說明。
  7. implements:實作規範的清單,标志目前包實作了CommonJS的那些規範。
  8. scripts:腳本說明對象。它主要被包管理器用來安裝,編譯,測試和解除安裝包,為其提供鈎子機制。
    //示例如下:
    "scripts":{
    	"install":"install.js",
    	"uninstall":"uninstall.js",
    	"build":"build.js",
    	"test":"test.js"
    }
               

包規範的定義可以幫助Node解決依賴包安裝的問題,而NPM正是基于該規範進行了實作。最初,NPM工具是有Issac Z.Schlueter單獨建立,提供給node服務的Node包管理器,需要單獨安裝。後來,在v6.3版本時內建進Node中作為預設包管理器,作為軟體包的一部分一起安裝。

在包描述檔案的規範中,NPM實際需要的字段主要有name,version,description,keywords,repositories,author,bin,main,scripts,engines,dependencies,devDependencies。

與包規範的差別在于多了author,bin,main和devDependencies這4個字段。

author:包作者。

bin:一些包作者希望包可以作為指令行工具使用。配置好bin字段後,通過

npm install package_name -g

指令可以将腳本添加到執行路徑中,之後可以在指令行中直接執行。通過-g指令安裝的子產品包稱為全局模式。

main:子產品引入方法require()在引入包時,會優先檢查這個字段,并将其作為包中其餘子產品的入口。如果不存在這個字段,require()方法會查找包目錄下的 index.js , index.node , index.json 檔案作為預設入口。

devDependencies:一些子產品隻在開發時需要依賴。配置這個屬性,可以提示包的後續開發者安裝依賴包。

//以下是node比較有名的架構express項目的package.json檔案
{
  "name": "express",
  "description": "Fast, unopinionated, minimalist web framework",
  "version": "4.17.1",
  "author": "TJ Holowaychuk <[email protected]>",
  "contributors": [
    "Aaron Heckmann <[email protected]>",
    "Ciaran Jessup <[email protected]>",
    "Douglas Christopher Wilson <[email protected]>",
    "Guillermo Rauch <[email protected]>",
    "Jonathan Ong <[email protected]>",
    "Roman Shtylman <[email protected]>",
    "Young Jae Sim <[email protected]>"
  ],
  "license": "MIT",
  "repository": "expressjs/express",
  "homepage": "http://expressjs.com/",
  "keywords": [
    "express",
    "framework",
    "sinatra",
    "web",
    "rest",
    "restful",
    "router",
    "app",
    "api"
  ],
  "dependencies": {
    "accepts": "~1.3.7",
    "array-flatten": "1.1.1",
    "body-parser": "1.19.0",
    "content-disposition": "0.5.3",
    "content-type": "~1.0.4",
    "cookie": "0.4.0",
    "cookie-signature": "1.0.6",
    "debug": "2.6.9",
    "depd": "~1.1.2",
    "encodeurl": "~1.0.2",
    "escape-html": "~1.0.3",
    "etag": "~1.8.1",
    "finalhandler": "~1.1.2",
    "fresh": "0.5.2",
    "merge-descriptors": "1.0.1",
    "methods": "~1.1.2",
    "on-finished": "~2.3.0",
    "parseurl": "~1.3.3",
    "path-to-regexp": "0.1.7",
    "proxy-addr": "~2.0.5",
    "qs": "6.7.0",
    "range-parser": "~1.2.1",
    "safe-buffer": "5.1.2",
    "send": "0.17.1",
    "serve-static": "1.14.1",
    "setprototypeof": "1.1.1",
    "statuses": "~1.5.0",
    "type-is": "~1.6.18",
    "utils-merge": "1.0.1",
    "vary": "~1.1.2"
  },
  "devDependencies": {
    "after": "0.8.2",
    "connect-redis": "3.4.1",
    "cookie-parser": "~1.4.4",
    "cookie-session": "1.3.3",
    "ejs": "2.6.1",
    "eslint": "2.13.1",
    "express-session": "1.16.1",
    "hbs": "4.0.4",
    "istanbul": "0.4.5",
    "marked": "0.6.2",
    "method-override": "3.0.0",
    "mocha": "5.2.0",
    "morgan": "1.9.1",
    "multiparty": "4.2.1",
    "pbkdf2-password": "1.2.1",
    "should": "13.2.3",
    "supertest": "3.3.0",
    "vhost": "~3.0.2"
  },
  "engines": {
    "node": ">= 0.10.0"
  },
  "files": [
    "LICENSE",
    "History.md",
    "Readme.md",
    "index.js",
    "lib/"
  ],
  "scripts": {
    "lint": "eslint .",
    "test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/",
    "test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/",
    "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
    "test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/"
  },
  "__npminstall_done": "Sun Feb 16 2020 11:22:54 GMT+0800 (GMT+08:00)",
  "_from": "[email protected]",
  "_resolved": "https://registry.npm.taobao.org/express/download/express-4.17.1.tgz"
}
           

NPM常用功能

CommonJS包規範是理論,NPM是其中的一種實踐。對于Node而言,NPM幫助完成了第三方子產品的釋出,安裝和依賴等。借助NPM,Node與第三方子產品之間形成了很好地一個生态系統。

安裝依賴包

安裝依賴包是NPM最常見的用法,它的執行語句是npm install express.。執行該指令後,NPM會在目前目錄下建立node_module目錄,然後在node_module目錄下建立express目錄,接着将包解壓到這個目錄下。

安裝好依賴包後,直接在代碼中調用require(‘express’),即可引入該包,require()方法在做路徑分析時會通過,子產品路徑查找到express所在的位置。子產品引入和包的安裝是相輔相成的。

全局模式安裝

如果包含有指令行工具,那麼需要執行

npm install express -g

指令進行全局安裝。需要注意的是,全局模式并不是将一個子產品包安裝為一個全局包的意思,他并不意味着可以從任何地方通過require()來引用到它。

全局模式這個稱謂其實并不精确,存在諸多誤導。實際上,-g是将一個包安裝為全局可用的可執行指令。他根據包描述檔案中的bin字段配置,将實際腳本連結到與Node可執行檔案相同的路徑下:

"bin":{
	"express":"./bin/express"
}
           

事實上,通過全局模式安裝的所有子產品都被安裝進一個統一的目錄下,這個目錄可以通過如下方式運算出來:

path.resolve(process.execPath, ‘…’ , ‘lib’ , ‘node_modules’);

如果Node可執行檔案的位置是/user/local/bin/node,那麼子產品目錄就是/user/local/lib/node_modules。最後,通過軟連結的方式将bin字段配置的可執行檔案連結到Node的可執行目錄下。

從本地安裝

對于一些沒有釋出到NPM上的包,或是因為網絡原因導緻無法直接安裝的包,可通過将包下載下傳到本地,然後本地安裝。本地安裝隻需要為NPM指定package.json檔案所在的位置即可:它可以是一個包含package.json的存檔檔案,也可以是一個URL位址,也可以是一個目錄下有package.json檔案的目錄位置。具體參數如下:

npm install <tarbar file>
npm install <tarbar url>
npm install <folder>
           

從非官方源安裝

如果不能通過官方源安裝,可通過鏡像安裝。在執行指令時,添加

--registry=http://registry.url

即可,執行個體:

npm insatll underscore --registry=http://registry.yrl
           

如果使用過程幾乎使用鏡像源安裝,可執行指令指定預設源

npm config set registry http://registry.url
           

NPM潛在問題

作為為子產品和包服務的工具,NPM十分便捷。它實質上已經是一個包共享平台,所有人都可以貢獻并将其打包分享到這個平台上,也可以在許可證的允許下免費使用他們

繼續閱讀