前端構建之NPM

NPM的由來

現代前端開發已經離不開Node了。你們都知道在安裝Node時會附贈一個命令行工具Node Package Manager,即npm。或許你已經照着教程輸入過好多遍」npm install xxx」,而且你發現npm的命令林林總總幾十條,package.json的配置項使人眼花繚亂,但不知你有沒有認真想過,咱們爲何須要npm?若是沒有它,世界會怎樣?php

個人理解,npm所作的一切都是爲了解決軟件工程界一個一直以來的追求:代碼複用。抓住這個核心,也就抓住了正確理解和使用npm的鑰匙。css

爲何要複用代碼呢?由於基於已有的成熟代碼快速開發新的應用,能夠極大地提升開發效率,正所謂「站在巨人肩膀上」「不要重複造輪子」。html

So,在Node環境下要複用JS代碼,咱們有哪些方案呢? 前端

1. 刀耕火種——copy&paste
複製粘貼代碼的思路很直接,但現在還在的這樣搞的同窗應該是從原始社會穿越來的吧。。這個方案最大的缺點倒還不是代碼冗餘,而是一旦所複製的原始代碼發生了變化,那就必須手動修改每一處複本,在稍有規模的項目里根本不可行。 vue

2. 耕牛犁地——CommonJSnode

Node實現了一個模塊系統CommonJS,實在是JSer的一大福音。藉助它,咱們沒必要再複製粘貼代碼了:假如一個做者開發了一個名爲lib1的庫,他只需代碼寫在一個名叫lib1.js的文件裏,用module.export語句導出;而使用者只需把lib1.js下載到本身工程目錄,require一下即可直接用啦!(此時lib1也被稱爲一個「依賴」)webpack

但這裏仍然存在兩個大問題:git

一,若是lib1.js自己也複用了別的代碼,好比lib2.js、lib3.js...那你在下載lib1.js的時候,必須手動把它所依賴的這些模塊文件也一併下載;可要是lib2.js還依賴lib5.js、lib6.js....呢?一棵龐大的、深不見底的依賴樹很難手工管理。程序員

2、lib1.js的做者修復了幾個bug,但沒有一個機制能讓他通知你升級舊的模塊文件。github

做爲一名職業素養良好的程序員,看到這些問題的第一反應是否是「寫個腳本」?哈哈,不用麻煩了,由於已經有人替咱們寫好了,這個腳本工具就是npm。 

3. 機械化耕做——npm
有了上面「自力更生」的原始體驗,再看看npm提供的依賴安裝、卸載、升級、發佈等一條龍服務,是否是很爽?

Npm制定了一個包規範,所謂規範就是一些格式和約定,好比約定從package.json文件裏讀取這個包的全部信息,包括它的名字、版本號、它依賴於哪些別的包等;又好比約定node_modules目錄專門用來存放第三方依賴,Node爲此提供的支持是內置的require方法默認會到這個目錄下去檢索模塊,而無需手動指定路徑。有了這些規範,一個包的開發、依賴安裝、發佈等都步驟都標準化了,省心省力。

能夠說,JavaScript從一門「玩具」語言,到現在能夠勝任大型項目開發,模塊化和npm是其進化路上的重要一步。


後記

你們都知道前端有「三板斧」,但剛纔我一直在談JS,徹底沒提到另外兩板斧。這是由於npm只是「Node模塊管理器」,Node上又沒有HTML和CSS,npm天然管不到。那麼除了能夠直接支持Node端的開發之外,npm又如何爲瀏覽器端開發提供支持呢?

答案是:npm生態圈提供了不少強大的前端開發工具,好比Webpack、Babel、ESLint等。特別是Webpack、Browserify、rollup這類構建工具,能夠接手瀏覽器端的依賴管理重任,以及不少其餘的附加功能。

什麼是 NPM

npm 之於 Node.js ,就像 pip 之於 Python, gem 之於 Ruby, pear 之於 PHP 。

npm 是 Node.js 官方提供的包管理工具,他已經成了 Node.js 包的標準發佈平臺,用於 Node.js 包的發佈、傳播、依賴控制。npm 提供了命令行工具,使你能夠方便地下載、安裝、升級、刪除包,也可讓你做爲開發者發佈並維護包。

爲何要使用 NPM

npm 是隨同 Node.js 一塊兒安裝的包管理工具,能解決 Node.js 代碼部署上的不少問題,常見的場景有如下幾種:

容許用戶從 npm 服務器下載別人編寫的第三方包到本地使用。 容許用戶從 npm 服務器下載並安裝別人編寫的命令行程序到本地使用。 容許用戶將本身編寫的包或命令行程序上傳到 npm 服務器供別人使用。

npm 的背後,是基於 couchdb 的一個數據庫,詳細記錄了每一個包的信息,包括做者、版本、依賴、受權信息等。它的一個很重要的做用就是:將開發者從繁瑣的包管理工做(版本、依賴等)中解放出來,更加專一於功能的開發。

如何使用 NPM

安裝

npm 不須要單獨安裝。在安裝 Node 的時候,會連帶一塊兒安裝 npm 。可是,Node 附帶的 npm 可能不是最新版本,最後用下面的命令,更新到最新版本。

1
$ sudo npm install npm@latest -g

若是是 Window 系統使用如下命令便可:

1
npm install npm -g

也就是使用 npm 安裝本身。之因此能夠這樣,是由於 npm 自己與 Node 的其餘模塊沒有區別。 而後,運行下面的命令,查看各類信息。

1
2
3
4
5
6
7
8
9
10
11
# 查看 npm 命令列表
$ npm help
 
# 查看各個命令的簡單用法
$ npm -l
 
# 查看 npm 的版本
$ npm -v
 
# 查看 npm 的配置
$ npm config list -l

使用

npm init

npm init 用來初始化生成一個新的 package.json 文件。它會向用戶提問一系列問題,若是你以爲不用修改默認配置,一路回車就能夠了。
若是使用了 -f(表明force)、-y(表明yes),則跳過提問階段,直接生成一個新的 package.json 文件。

1
$ npm init -y

npm set

npm set 用來設置環境變量

1
2
3
4
$ npm set  init-author- name  'Your name'
$ npm set  init-author-email 'Your email'
$ npm set  init-author-url 'https://yourdomain.com'
$ npm set  init-license 'MIT'

上面命令等於爲 npm init 設置了默認值,之後執行 npm init 的時候,package.json 的做者姓名、郵件、主頁、許可證字段就會自動寫入預設的值。這些信息會存放在用戶主目錄的 ~/.npmrc文件,使得用戶不用每一個項目都輸入。若是某個項目有不一樣的設置,能夠針對該項目運行 npm config。

npm info

npm info 命令能夠查看每一個模塊的具體信息。好比,查看 underscore 模塊的信息。

1
$ npm info underscore

上面命令返回一個 JavaScript 對象,包含了 underscore 模塊的詳細信息。這個對象的每一個成員,均可以直接從 info 命令查詢。

1
2
3
4
5
$ npm info underscore description
 
$ npm info underscore homepage
 
$ npm info underscore version

npm search

npm search 命令用於搜索 npm 倉庫,它後面能夠跟字符串,也能夠跟正則表達式。

1
$ npm search <搜索詞>

npm list

npm list 命令以樹形結構列出當前項目安裝的全部模塊,以及它們依賴的模塊。

1
2
3
4
5
6
7
$ npm list
 
# 加上 global  參數,會列出全局安裝的模塊
$ npm list - global
 
# npm list 命令也能夠列出單個模塊
$ npm list underscore

npm install

使用 npm 安裝包的命令格式爲:
npm [install/i] [package_name]

本地模式和全局模式

npm 在默認狀況下會從 https://npmjs.org 搜索或下載包,將包安裝到當前目錄的 node_modules 子目錄下。

若是你熟悉 Ruby 的 gem 或者 Python 的 pip,你會發現 npm 與它們的行爲不一樣,gem 或 pip 老是以全局模式安裝,使包能夠供全部的程序使用,而 npm 默認會把包安裝到當前目錄下。這反映了 npm 不一樣的設計哲學。若是把包安裝到全局,能夠提供程序的重複利用程度,避免一樣的內容的多分副本,但壞處是難以處理不一樣的版本依賴。若是把包安裝到當前目錄,或者說本地,則不會有不一樣程序依賴不一樣版本的包的衝突問題,同時還減輕了包做者的 API 兼容性壓力,但缺陷則是同一個包可能會被安裝許屢次。

咱們在使用 supervisor 的時候使用了 npm install -g supervisor 命令,就是以全局模式安裝 supervisor 。

這裏注意一點的就是,supervisor 必須安裝到全局,若是你不安裝到全局,錯誤命令會提示你安裝到全局。若是不想安裝到默認的全局,也能夠本身修改全局路徑到當前路徑 npm config set prefix "路徑" 安裝完之後就能夠用 supervisor 來啓動服務了。

supervisor 能夠幫助你實現這個功能,它會監視你對代碼的驅動,並自動重啓 Node.js 。

通常來講,全局安裝只適用於工具模塊,好比 eslint 和 gulp 。關於使用全局模式,多數時候並非由於許多程序都有可能用到了它,爲了減小多重副本而使用全局模式,而是由於本地模式不會註冊 PATH 環境變量

「本地安裝」指的是將一個模塊下載到當前項目的 node_modules 子目錄,而後只有在項目目錄之中,才能調用這個模塊。

本地模式和全局模式的特色以下:

模式 可經過 require 使用 註冊 PATH
本地模式
全局模式
1
2
3
4
5
6
# 本地安裝
$ npm install <package name = "" >
 
# 全局安裝
$ sudo npm install - global  <package name = "" >
$ sudo npm install -g <package name = "" ></package></package></package>

npm install 也支持直接輸入 Github 代碼庫地址。

安裝以前,npm install 會先檢查,node_modules 目錄之中是否已經存在指定模塊。若是存在,就再也不從新安裝了,即便遠程倉庫已經有了一個新版本,也是如此。

若是你但願,一個模塊無論是否安裝過, npm 都要強制從新安裝,可使用 -f 或 –force 參數。

1
$ npm install <packagename> --force</packagename>

安裝不一樣版本

install 命令老是安裝模塊的最新版本,若是要安裝模塊的特定版本,能夠在模塊名後面加上 @ 和版本號。

1
2
3
$ npm install sax@latest
$ npm install sax@0.1.1
$ npm install sax@ ">=0.1.0 <0.2.0"

install 命令可使用不一樣參數,指定所安裝的模塊屬於哪種性質的依賴關係,即出如今 packages.json 文件的哪一項中。

–save:模塊名將被添加到 dependencies,能夠簡化爲參數-S。
–save-dev:模塊名將被添加到 devDependencies,能夠簡化爲參數-D。

1
2
3
4
5
$ npm install sax --save
$ npm install node-tap --save-dev
# 或者
$ npm install sax -S
$ npm install node-tap -D

dependencies 依賴

這個能夠說是咱們 npm 核心一項內容,依賴管理,這個對象裏面的內容就是咱們這個項目所依賴的 js 模塊包。下面這段代碼表示咱們依賴了 markdown-it 這個包,版本是 ^8.1.0 ,表明最小依賴版本是 8.1.0 ,若是這個包有更新,那麼當咱們使用 npm install 命令的時候,npm 會幫咱們下載最新的包。當別人引用咱們這個包的時候,包內的依賴包也會被下載下來。

1
2
3
"dependencies" : {
     "markdown-it" : "^8.1.0"
}

devDependencies 開發依賴

在咱們開發的時候會用到的一些包,只是在開發環境中須要用到,可是在別人引用咱們包的時候,不會用到這些內容,放在 devDependencies 的包,在別人引用的時候不會被 npm 下載。

1
2
3
4
5
6
7
8
9
10
11
"devDependencies" : {
     "autoprefixer" : "^6.4.0" ,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"
}

當你有了一個完整的 package.json 文件的時候,就可讓人一眼看出來,這個模塊的基本信息,和這個模塊所須要依賴的包。咱們能夠經過 npm install 就能夠很方便的下載好這個模塊所須要的包。

npm install 默認會安裝 dependencies 字段和 devDependencies 字段中的全部模塊,若是使用 --production 參數,能夠只安裝 dependencies 字段的模塊。

1
2
3
$ npm install --production
# 或者
$ NODE_ENV=production npm install

一旦安裝了某個模塊,就能夠在代碼中用 require 命令加載這個模塊。

1
2
var backbone = require( 'backbone' )
console.log(backbone.VERSION)

npm run

npm 不只能夠用於模塊管理,還能夠用於執行腳本。package.json 文件有一個 scripts 字段,能夠用於指定腳本命令,供 npm 直接調用。
package.json

1
2
3
4
5
6
7
8
9
10
11
12
{
   "name" : "myproject" ,
   "devDependencies" : {
     "jshint" : "latest" ,
     "browserify" : "latest" ,
     "mocha" : "latest"
   },
   "scripts" : {
     "lint" : "jshint **.js" ,
     "test" : "mocha test/"
   }
}

scripts 腳本

顧名思義,就是一些腳本代碼,能夠經過 npm run script-key 來調用,例如在這個 package.json 的文件夾下使用 npm run dev 就至關於運行了 node build/dev-server.js 這一段代碼。使用 scripts 的目的就是爲了把一些要執行的代碼合併到一塊兒,使用 npm run 來快速的運行,方便省事。
npm run 是 npm run-script 的縮寫,通常都使用前者,可是後者能夠更好的反應這個命令的本質。

1
2
3
4
5
6
7
8
9
// 腳本
"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 若是不加任何參數,直接運行,會列出 package.json 裏面全部能夠執行的腳本命令。
npm 內置了兩個命令簡寫, npm test 等同於執行 npm run test,npm start 等同於執行 npm run start。

1
"build" : "npm run build-js && npm run build-css"

上面的寫法是先運行 npm run build-js ,而後再運行 npm run build-css ,兩個命令中間用 && 鏈接。若是但願兩個命令同時平行執行,它們中間能夠用 & 鏈接。

寫在 scripts 屬性中的命令,也能夠在 node_modules/.bin 目錄中直接寫成 bash 腳本。下面是一個 bash 腳本。

1
2
3
4
#!/bin/bash
 
cd site/main
browserify browser/main.js | uglifyjs -mc > static /bundle.js

假定上面的腳本文件名爲 build.sh ,而且權限爲可執行,就能夠在 scripts 屬性中引用該文件。

1
"build-js" : "bin/build.sh"

pre- 和 post- 腳本

npm run 爲每條命令提供了 pre- 和 post- 兩個鉤子(hook)。以 npm run lint 爲例,執行這條命令以前,npm 會先查看有沒有定義 prelint 和 postlint 兩個鉤子,若是有的話,就會先執行 npm run prelint,而後執行 npm run lint,最後執行 npm run postlint。

1
2
3
4
5
6
7
8
9
10
11
12
13
{
   "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'"
   }
}

上面代碼是一個 package.json 文件的例子。若是執行 npm test,會按下面的順序執行相應的命令。
1. pretest
2. test
3. posttest

若是執行過程出錯,就不會執行排在後面的腳本,即若是 prelint 腳本執行出錯,就不會接着執行 lint 和 postlint 腳本。

npm bin

npm bin 命令顯示相對於當前目錄的,Node 模塊的可執行腳本所在的目錄(即 .bin 目錄)。

1
2
3
# 項目根目錄下執行
$ npm bin
./node_modules/.bin

建立全局連接

npm 提供了一個有趣的命令 npm link,它的功能是在本地包和全局包之間建立符號連接。咱們說過使用全局模式安裝的包不能直接經過 require 使用。但經過 npm link 命令能夠打破這一限制。舉個例子,咱們已經經過 npm install -g express 安裝了 express,這時在工程的目錄下運行命令:

1
npm link express ./node_modules/express -> / user / local /lib/node_modules/express

咱們能夠在 node_modules 子目錄中發現一個指向安裝到全局的包的符號連接。經過這種方法,咱們就能夠把全局包當作本地包來使用了。

除了將全局的包連接到本地之外,使用 npm link 命令還能夠將本地的包連接到全局。使用方法是在包目錄(package.json 所在目錄)中運行 npm link 命令。若是咱們要開發一個包,利用這種方法能夠很是方便地在不一樣的工程間進行測試。

建立包

包是在模塊基礎上更深一步的抽象,Node.js 的包相似於 C/C++ 的函數庫或者 Java、.Net 的類庫。它將某個獨立的功能封裝起來,用於發佈、更新、依賴管理和版本控制。Node.js 根據 CommonJS 規範實現了包機制,開發了 npm 來解決包的發佈和獲取需求。
Node.js 的包是一個目錄,其中包含了一個 JSON 格式的包說明文件 package.json。嚴格符合 CommonJS 規範的包應該具有如下特徵:
。package.json 必須在包的頂層目錄下;
。二進制文件應該在 bin 目錄下;
JavaScript 代碼應該在 lib 目錄下;
。文檔應該在 doc 目錄下;
。單元測試應該在 test 目錄下。

Node.js 對包的要求並無這麼嚴格,只要頂層目錄下有 package.json,並符合一些規範便可。固然爲了提升兼容性,咱們仍是建議你在製做包的時候,嚴格遵照 CommonJS 規範。

咱們也能夠把文件夾封裝爲一個模塊,即所謂的包。包一般是一些模塊的集合,在模塊的基礎上提供了更高層的抽象,至關於提供了一些固定接口的函數庫。經過定製 package.json,咱們能夠建立更復雜,更完善,更符合規範的包用於發佈。

Node.js 在調用某個包時,會首先檢查包中 packgage.json 文件的 main 字段,將其做爲包的接口模塊,若是 package.json 或 main 字段不存在,會嘗試尋找 index.js 或 index.node 做爲包的接口。

package.json 是 CommonJS 規定的用來描述包的文件,徹底符合規範的 package.json 文件應該含有如下字段:

name: 包的名字,必須是惟一的,由小寫英文字母、數字和下劃線組成,不能包含空格。
description: 包的簡要說明。
version: 符合語義化版本識別規範的版本字符串。
keywords: 關鍵字數組,一般用於搜索。
maintainers: 維護者數組,每一個元素要包含 name 、email(可選)、web(可選)字段。
contributors: 貢獻者數組,格式與 maintainers 相同。包的做者應該是貢獻者數組的第一個元素。
bugs: 提交 bug 的地址,能夠是網址或者電子郵件地址。
licenses: 許可證數組,每一個元素要包含 type(許可證的名稱)和 url(連接到許可證文本的地址)字段。
repositories: 倉庫託管地址數組,每一個元素要包含 type(倉庫的類型,如 git)、URL(倉庫的地址)和 path(相對於倉庫的路徑,可選)字段。
dependencies: 包的依賴,一個關聯數組,由包名稱和版本號組成。

包的發佈

官方文檔https://www.npmjs.cn/getting-started/publishing-npm-packages/

經過使用 npm init 能夠根據交互式回答產生一個符合標準的 package.json。建立一個 index.js 做爲包的接口,一個簡單的包就製做完成了。

修改package.js的name屬性做爲發佈包的名字。

在發佈前,咱們還須要得到一個帳號用於從此維護本身的包,使用 npm adduser 根據提示完成帳號的建立,若是你是在npm網站上註冊的帳號在終端上使用npm login根據提示登錄。

完成後可使用 npm whoami 檢測是否已經取得了帳號,若是登錄已經登錄會顯示你的用戶名。

若是你的包未來有更新,只須要在 package.json 文件中修改 version 字段,而後從新使用 npm publish 命令就好了。
若是你對已發佈的包不滿意,可使用 npm unpublish 命令來取消發佈。

須要說明的是:json 文件不能有註釋。

下載包本身的包方式和下載其它包時同樣,使用npm install packagename就能夠了。

 

最後在說一下package.js中的"devDependencies"和"dependencies",開始覺得這兩個字段和webpack打包相關,其實沒有一點關係,webpack中依賴的包不管安裝到哪裏均可以使用到。

 

原文:http://www.cnblogs.com/leegent/p/7244660.html

相關文章
相關標籤/搜索