什么是 NPM
npm之于Node,就像pip之于Python,gem之于Ruby,composer之于PHP。
npm是Node官方提供的包管理工具,他已經(jīng)成了Node包的標(biāo)準(zhǔn)發(fā)布平臺(tái),用于Node包的發(fā)布、傳播、依賴(lài)控制。npm提供了命令行工具,使你可以方便地下載、安裝、升級(jí)、刪除包,也可以讓你作為開(kāi)發(fā)者發(fā)布并維護(hù)包。
為什么要使用 NPM
npm是隨同Node一起安裝的包管理工具,能解決Node代碼部署上的很多問(wèn)題,常見(jiàn)的場(chǎng)景有以下幾種:
允許用戶(hù)從npm服務(wù)器下載別人編寫(xiě)的第三方包到本地使用。
允許用戶(hù)從npm服務(wù)器下載并安裝別人編寫(xiě)的命令行程序到本地使用。
允許用戶(hù)將自己編寫(xiě)的包或命令行程序上傳到npm服務(wù)器供別人使用。
npm的背后,是基于CouchDB的一個(gè)數(shù)據(jù)庫(kù),詳細(xì)記錄了每個(gè)包的信息,包括作者、版本、依賴(lài)、授權(quán)信息等。它的一個(gè)很重要的作用就是:將開(kāi)發(fā)者從繁瑣的包管理工作(版本、依賴(lài)等)中解放出來(lái),更加專(zhuān)注于功能的開(kāi)發(fā)。
如何使用 NPM
安裝
npm不需要單獨(dú)安裝。在安裝Node的時(shí)候,會(huì)連帶一起安裝npm。但是,Node附帶的npm可能不是最新版本,最后用下面的命令,更新到最新版本。
$ sudo npm install npm@latest -g
如果是 Window 系統(tǒng)使用以下命令即可:
npm install npm -g
也就是使用npm安裝自己。之所以可以這樣,是因?yàn)閚pm本身與Node的其他模塊沒(méi)有區(qū)別。
然后,運(yùn)行下面的命令,查看各種信息。
#查看 npm 命令列表$ npmhelp#查看各個(gè)命令的簡(jiǎn)單用法$ npm -l#查看 npm 的版本$ npm -v#查看 npm 的配置$ npm config list -l
使用
npm init
npm init用來(lái)初始化生成一個(gè)新的package.json文件。它會(huì)向用戶(hù)提問(wèn)一系列問(wèn)題,如果你覺(jué)得不用修改默認(rèn)配置,一路回車(chē)就可以了。 如果使用了-f(代表force)、-y(代表yes),則跳過(guò)提問(wèn)階段,直接生成一個(gè)新的package.json文件。
$ npm init -y
npm set
npm set用來(lái)設(shè)置環(huán)境變量
$ npmsetinit-author-name'Your name'$ npmsetinit-author-email'Your email'$ npmsetinit-author-url'http://yourdomain.com'$ npmsetinit-license'MIT'
上面命令等于為npm init設(shè)置了默認(rèn)值,以后執(zhí)行npm init的時(shí)候,package.json的作者姓名、郵件、主頁(yè)、許可證字段就會(huì)自動(dòng)寫(xiě)入預(yù)設(shè)的值。這些信息會(huì)存放在用戶(hù)主目錄的~/.npmrc文件,使得用戶(hù)不用每個(gè)項(xiàng)目都輸入。如果某個(gè)項(xiàng)目有不同的設(shè)置,可以針對(duì)該項(xiàng)目運(yùn)行npm config。
npm info
npm info命令可以查看每個(gè)模塊的具體信息。比如,查看underscore模塊的信息。
$ npm info underscore
上面命令返回一個(gè)JavaScript對(duì)象,包含了underscore模塊的詳細(xì)信息。這個(gè)對(duì)象的每個(gè)成員,都可以直接從info命令查詢(xún)。
$ npm info underscore description
$ npm info underscore homepage
$ npm info underscore version
npm search
npm search命令用于搜索npm倉(cāng)庫(kù),它后面可以跟字符串,也可以跟正則表達(dá)式。
$ npm search<搜索詞>
npm list
npm list命令以樹(shù)形結(jié)構(gòu)列出當(dāng)前項(xiàng)目安裝的所有模塊,以及它們依賴(lài)的模塊。
$ npm list#加上 global 參數(shù),會(huì)列出全局安裝的模塊$ npm list -global#npm list 命令也可以列出單個(gè)模塊$ npm list underscore
npm install
使用npm安裝包的命令格式為:npm [install/i] [package_name]
本地模式和全局模式
npm在默認(rèn)情況下會(huì)從NPM搜索或下載包,將包安裝到當(dāng)前目錄的node_modules子目錄下。
如果你熟悉Ruby的gem或者Python的pip,你會(huì)發(fā)現(xiàn)npm與它們的行為不同,gem或pip總是以全局模式安裝,使包可以供所有的程序使用,而npm默認(rèn)會(huì)把包安裝到當(dāng)前目錄下。這反映了npm不同的設(shè)計(jì)哲學(xué)。如果把包安裝到全局,可以提供程序的重復(fù)利用程度,避免同樣的內(nèi)容的多分副本,但壞處是難以處理不同的版本依賴(lài)。如果把包安裝到當(dāng)前目錄,或者說(shuō)本地,則不會(huì)有不同程序依賴(lài)不同版本的包的沖突問(wèn)題,同時(shí)還減輕了包作者的API兼容性壓力,但缺陷則是同一個(gè)包可能會(huì)被安裝許多次。
我們?cè)谑褂胹upervisor的時(shí)候使用了npm install -g supervisor命令,就是以全局模式安裝supervisor。
這里注意一點(diǎn)的就是,supervisor必須安裝到全局,如果你不安裝到全局,錯(cuò)誤命令會(huì)提示你安裝到全局。如果不想安裝到默認(rèn)的全局,也可以自己修改全局路徑到當(dāng)前路徑npm config set prefix "路徑"安裝完以后就可以用supervisor來(lái)啟動(dòng)服務(wù)了。supervisor可以幫助你實(shí)現(xiàn)這個(gè)功能,它會(huì)監(jiān)視你對(duì)代碼的驅(qū)動(dòng),并自動(dòng)重啟Node。
一般來(lái)說(shuō),全局安裝只適用于工具模塊,比如eslint和gulp。關(guān)于使用全局模式,多數(shù)時(shí)候并不是因?yàn)樵S多程序都有可能用到了它,為了減少多重副本而使用全局模式,而是因?yàn)?b>本地模式不會(huì)注冊(cè)PATH環(huán)境變量。 “本地安裝”指的是將一個(gè)模塊下載到當(dāng)前項(xiàng)目的node_modules子目錄,然后只有在項(xiàng)目目錄之中,才能調(diào)用這個(gè)模塊。
本地模式和全局模式的特點(diǎn)如下:
模式可通過(guò) require 使用注冊(cè) PATH
本地模式是否
全局模式否是
#本地安裝$ npm install#全局安裝$ sudo npm install -global$ sudo npm install -g
npm install也支持直接輸入Github代碼庫(kù)地址。
$ npm install git://github.com/package/path.git
$ npm install git://github.com/package/path.git#0.1.0
安裝之前,npm install會(huì)先檢查,node_modules目錄之中是否已經(jīng)存在指定模塊。如果存在,就不再重新安裝了,即使遠(yuǎn)程倉(cāng)庫(kù)已經(jīng)有了一個(gè)新版本,也是如此。
如果你希望,一個(gè)模塊不管是否安裝過(guò),npm都要強(qiáng)制重新安裝,可以使用-f或--force參數(shù)。
$ npm install--force
安裝不同版本
install命令總是安裝模塊的最新版本,如果要安裝模塊的特定版本,可以在模塊名后面加上@和版本號(hào)。
$ npm install sax@latest$ npm install sax@0.1.1$ npm install sax@">=0.1.0 <0.2.0"
install命令可以使用不同參數(shù),指定所安裝的模塊屬于哪一種性質(zhì)的依賴(lài)關(guān)系,即出現(xiàn)在packages.json文件的哪一項(xiàng)中。
--save:模塊名將被添加到 dependencies,可以簡(jiǎn)化為參數(shù)-S。 --save-dev:模塊名將被添加到 devDependencies,可以簡(jiǎn)化為參數(shù)-D。
$ npm install sax --save$ npm install node-tap --save-dev#或者$ npm install sax -S$ npm install node-tap -D
dependencies 依賴(lài)
這個(gè)可以說(shuō)是我們npm核心一項(xiàng)內(nèi)容,依賴(lài)管理,這個(gè)對(duì)象里面的內(nèi)容就是我們這個(gè)項(xiàng)目所依賴(lài)的js模塊包。下面這段代碼表示我們依賴(lài)了markdown-it這個(gè)包,版本是^8.1.0,代表最小依賴(lài)版本是8.1.0,如果這個(gè)包有更新,那么當(dāng)我們使用npm install命令的時(shí)候,npm會(huì)幫我們下載最新的包。當(dāng)別人引用我們這個(gè)包的時(shí)候,包內(nèi)的依賴(lài)包也會(huì)被下載下來(lái)。
"dependencies":{"markdown-it":"^8.1.0"}
devDependencies 開(kāi)發(fā)依賴(lài)
在我們開(kāi)發(fā)的時(shí)候會(huì)用到的一些包,只是在開(kāi)發(fā)環(huán)境中需要用到,但是在別人引用我們包的時(shí)候,不會(huì)用到這些內(nèi)容,放在devDependencies的包,在別人引用的時(shí)候不會(huì)被npm下載。
"devDependencies":{"autoprefixer":"^6.4.0","babel-preset-es2015":"^6.0.0","babel-preset-stage-2":"^6.0.0","babel-register":"^6.0.0","webpack":"^1.13.2","webpack-dev-middleware":"^1.8.3","webpack-hot-middleware":"^2.12.2","webpack-merge":"^0.14.1","highlightjs":"^9.8.0"}
當(dāng)你有了一個(gè)完整的package.json文件的時(shí)候,就可以讓人一眼看出來(lái),這個(gè)模塊的基本信息,和這個(gè)模塊所需要依賴(lài)的包。我們可以通過(guò)npm install就可以很方便的下載好這個(gè)模塊所需要的包。
npm install默認(rèn)會(huì)安裝dependencies字段和devDependencies字段中的所有模塊,如果使用--production參數(shù),可以只安裝dependencies字段的模塊。
$ npm install --production#或者$ NODE_ENV=production npm install
一旦安裝了某個(gè)模塊,就可以在代碼中用require命令加載這個(gè)模塊。
varbackbone=require('backbone')console.log(backbone.VERSION)
npm run
npm不僅可以用于模塊管理,還可以用于執(zhí)行腳本。package.json文件有一個(gè)scripts字段,可以用于指定腳本命令,供npm直接調(diào)用。package.json文件內(nèi)容:
{"name":"myproject","devDependencies":{"jshint":"latest","browserify":"latest","mocha":"latest"},"scripts":{"lint":"jshint **.js","test":"mocha test/"}}
scripts 腳本
顧名思義,就是一些腳本代碼,可以通過(guò)npm run script-key來(lái)調(diào)用,例如在這個(gè)package.json的文件夾下使用npm run dev就相當(dāng)于運(yùn)行了node build/dev-server.js這一段代碼。使用scripts的目的就是為了把一些要執(zhí)行的代碼合并到一起,使用 npm run 來(lái)快速的運(yùn)行,方便省事。npm run是npm run-script的縮寫(xiě),一般都使用前者,但是后者可以更好的反應(yīng)這個(gè)命令的本質(zhì)。
//腳本"scripts":{"dev":"node build/dev-server.js","build":"node build/build.js","docs":"node build/docs.js","build-docs":"npm run docs & git checkout gh-pages & xcopy /sy dist\\* . & git add . & git commit -m 'auto-pages' & git push & git checkout master","build-publish":"rmdir /S /Q lib & npm run build &git add . & git commit -m auto-build & npm version patch & npm publish & git push","lint":"eslint --ext .js,.vue src"}
npm run如果不加任何參數(shù),直接運(yùn)行,會(huì)列出package.json里面所有可以執(zhí)行的腳本命令。npm內(nèi)置了兩個(gè)命令簡(jiǎn)寫(xiě),npm test等同于執(zhí)行npm run test,npm start等同于執(zhí)行npm run start。
"build":"npm run build-js && npm run build-css"
上面的寫(xiě)法是先運(yùn)行npm run build-js,然后再運(yùn)行npm run build-css,兩個(gè)命令中間用&&連接。如果希望兩個(gè)命令同時(shí)平行執(zhí)行,它們中間可以用&連接。
寫(xiě)在scripts屬性中的命令,也可以在node_modules/.bin目錄中直接寫(xiě)成bash腳本。下面是一個(gè)bash腳本。
#!/bin/bashcdsite/mainbrowserify browser/main.js|uglifyjs -mc>static/bundle.js
假定上面的腳本文件名為build.sh,并且權(quán)限為可執(zhí)行,就可以在scripts屬性中引用該文件。
"build-js":"bin/build.sh"
pre- 和 post- 腳本
npm run為每條命令提供了pre-和post-兩個(gè)鉤子(hook)。以npm run lint為例,執(zhí)行這條命令之前,npm會(huì)先查看有沒(méi)有定義prelint和postlint兩個(gè)鉤子,如果有的話,就會(huì)先執(zhí)行npm run prelint,然后執(zhí)行npm run lint,最后執(zhí)行npm run postlint。
{"name":"myproject","devDependencies":{"eslint":"latest""karma":"latest"},"scripts":{"lint":"eslint --cache --ext .js --ext .jsx src","test":"karma start --log-leve=error karma.config.js --single-run=true","pretest":"npm run lint","posttest":"echo 'Finished running tests'"}}
上面代碼是一個(gè)package.json文件的例子。如果執(zhí)行npm test,會(huì)按下面的順序執(zhí)行相應(yīng)的命令。
pretest
test
posttest
如果執(zhí)行過(guò)程出錯(cuò),就不會(huì)執(zhí)行排在后面的腳本,即如果prelint腳本執(zhí)行出錯(cuò),就不會(huì)接著執(zhí)行l(wèi)int和postlint腳本。
npm bin
npm bin命令顯示相對(duì)于當(dāng)前目錄的,Node模塊的可執(zhí)行腳本所在的目錄(即.bin目錄)。
#項(xiàng)目根目錄下執(zhí)行$ npm bin./node_modules/.bin
創(chuàng)建全局鏈接
npm提供了一個(gè)有趣的命令npm link,它的功能是在本地包和全局包之間創(chuàng)建符號(hào)鏈接。我們說(shuō)過(guò)使用全局模式安裝的包不能直接通過(guò)require使用。但通過(guò)npm link命令可以打破這一限制。舉個(gè)例子,我們已經(jīng)通過(guò)npm install -g express安裝了express,這時(shí)在工程的目錄下運(yùn)行命令:npm link express ./node_modules/express -> /user/local/lib/node_modules/express我們可以在node_modules子目錄中發(fā)現(xiàn)一個(gè)指向安裝到全局的包的符號(hào)鏈接。通過(guò)這種方法,我們就可以把全局包當(dāng)做本地包來(lái)使用了。 除了將全局的包鏈接到本地以外,使用npm link命令還可以將本地的包鏈接到全局。使用方法是在包目錄(package.json所在目錄)中運(yùn)行npm link命令。如果我們要開(kāi)發(fā)一個(gè)包,利用這種方法可以非常方便地在不同的工程間進(jìn)行測(cè)試。
創(chuàng)建包
包是在模塊基礎(chǔ)上更深一步的抽象,Node的包類(lèi)似于C/C++的函數(shù)庫(kù)或者Java、.Net的類(lèi)庫(kù)。它將某個(gè)獨(dú)立的功能封裝起來(lái),用于發(fā)布、更新、依賴(lài)管理和版本控制。Node根據(jù)CommonJS規(guī)范實(shí)現(xiàn)了包機(jī)制,開(kāi)發(fā)了npm來(lái)解決包的發(fā)布和獲取需求。Node的包是一個(gè)目錄,其中包含了一個(gè)JSON格式的包說(shuō)明文件package.json。嚴(yán)格符合CommonJS規(guī)范的包應(yīng)該具備以下特征:
package.json必須在包的頂層目錄下;
二進(jìn)制文件應(yīng)該在bin目錄下;
JavaScript代碼應(yīng)該在lib目錄下;
文檔應(yīng)該在doc目錄下;
單元測(cè)試應(yīng)該在test目錄下。
Node對(duì)包的要求并沒(méi)有這么嚴(yán)格,只要頂層目錄下有package.json,并符合一些規(guī)范即可。當(dāng)然為了提高兼容性,我們還是建議你在制作包的時(shí)候,嚴(yán)格遵守CommonJS規(guī)范。
我們也可以把文件夾封裝為一個(gè)模塊,即所謂的包。包通常是一些模塊的集合,在模塊的基礎(chǔ)上提供了更高層的抽象,相當(dāng)于提供了一些固定接口的函數(shù)庫(kù)。通過(guò)定制package.json,我們可以創(chuàng)建更復(fù)雜,更完善,更符合規(guī)范的包用于發(fā)布。
Node在調(diào)用某個(gè)包時(shí),會(huì)首先檢查包中packgage.json文件的main字段,將其作為包的接口模塊,如果package.json或main字段不存在,會(huì)嘗試尋找 index.js 或 index.node 作為包的接口。
package.json是CommonJS規(guī)定的用來(lái)描述包的文件,完全符合規(guī)范的package.json文件應(yīng)該含有以下字段: name: 包的名字,必須是唯一的,由小寫(xiě)英文字母、數(shù)字和下劃線組成,不能包含空格。 description: 包的簡(jiǎn)要說(shuō)明。 version: 符合語(yǔ)義化版本識(shí)別規(guī)范的版本字符串。 keywords: 關(guān)鍵字?jǐn)?shù)組,通常用于搜索。 maintainers: 維護(hù)者數(shù)組,每個(gè)元素要包含name、email(可選)、web(可選)字段。 contributors: 貢獻(xiàn)者數(shù)組,格式與maintainers相同。包的作者應(yīng)該是貢獻(xiàn)者數(shù)組的第一個(gè)元素。 bugs: 提交bug的地址,可以是網(wǎng)址或者電子郵件地址。 licenses: 許可證數(shù)組,每個(gè)元素要包含type(許可證的名稱(chēng))和 url(鏈接到許可證文本的地址)字段。 repositories: 倉(cāng)庫(kù)托管地址數(shù)組,每個(gè)元素要包含type(倉(cāng)庫(kù)的類(lèi)型,如 git)、URL(倉(cāng)庫(kù)的地址)和 path(相對(duì)于倉(cāng)庫(kù)的路徑,可選)字段。 dependencies: 包的依賴(lài),一個(gè)關(guān)聯(lián)數(shù)組,由包名稱(chēng)和版本號(hào)組成。
包的發(fā)布
通過(guò)使用npm init可以根據(jù)交互式回答產(chǎn)生一個(gè)符合標(biāo)準(zhǔn)的package.json。創(chuàng)建一個(gè)index.js作為包的接口,一個(gè)簡(jiǎn)單的包就制作完成了。 在發(fā)布前,我們還需要獲得一個(gè)賬號(hào)用于今后維護(hù)自己的包,使用npm adduser根據(jù)提示完成賬號(hào)的創(chuàng)建 完成后可以使用npm whoami檢測(cè)是否已經(jīng)取得了賬號(hào)。 接下來(lái),在package.json所在目錄下運(yùn)行npm publish,稍等片刻就可以完成發(fā)布了,打開(kāi)瀏覽器,訪問(wèn)NPM搜索就可以找到自己剛剛發(fā)布的包了?,F(xiàn)在我們可以在世界的任意一臺(tái)計(jì)算機(jī)上使用npm install neveryumodule命令來(lái)安裝它。 如果你的包將來(lái)有更新,只需要在package.json文件中修改version字段,然后重新使用npm publish命令就行了。 如果你對(duì)已發(fā)布的包不滿(mǎn)意,可以使用npm unpublish命令來(lái)取消發(fā)布。
*需要說(shuō)明的是: `json` 文件不能有注釋*