這篇文章主要講解了“nodejs npm的原理是什么”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“nodejs npm的原理是什么”吧!
創(chuàng)新互聯(lián)堅持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都網(wǎng)站設(shè)計、成都網(wǎng)站制作、企業(yè)官網(wǎng)、英文網(wǎng)站、手機端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時代的張掖網(wǎng)站設(shè)計、移動媒體設(shè)計的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!
npm據(jù)稱成為世界最大的包管理器?原因真的只是用戶友好?
用來初始化一個簡單的package.json文件。package.json文件用來定義一個package的描述文件。
1、npm init
的執(zhí)行的默認(rèn)行為
執(zhí)行npm init --yes
,全部使用默認(rèn)的值。
2、 自定義npm init
行為
npm init
命令的原理是:調(diào)用腳本,輸出一個初始化的package.json文件。
獲取用戶輸入使用prompt()方法。
npm的核心功能:依賴管理。執(zhí)行npm i
從package.json中dependencies和devDependencies將依賴包安裝到當(dāng)前目錄的node_modules文件夾中。
2.1、package定義
npm i
就可以安裝一個包。通常package就是我們需要安裝的包名,默認(rèn)配置下npm會從默認(rèn)的源(Registry)中查找該包名的對應(yīng)的包地址,并且下載安裝。 還可以是一個指向有效包名的http url/git url/文件夾路徑。
package的準(zhǔn)確定義,符合以下a)到g)其中一個條件,他就是一個package:
package的準(zhǔn)確定義
2.2、安裝本地包/遠程git倉庫包
共享依賴包,并非非要把包發(fā)布到npm源上才能使用。
1)、場景1:本地模塊引用
開發(fā)中避免不了模塊之間調(diào)用,開發(fā)中,我們把頻繁調(diào)用的配置模塊放在根目錄,然后如果有很多層級目錄,后來引用
const config = require(''../../../../..config)
這樣的路徑引用不利于代碼重構(gòu)。這時候我們需要考慮把這個模塊分離出來供其他模塊共享。比如config.js可以封裝成一個package放到node_modules目錄下。
不需要手動拷貝或者創(chuàng)建軟連接到node_modules目錄,npm 有自己的解決方案:
方案:
1、新增config文件夾,將config.js移入文件夾,名字修改為index.js,創(chuàng)建package.json定義config包
{ "name": "config", "main": "index.js", "version": "0.1.0" }
2、在項目的package.json新增依賴項,然后執(zhí)行npm i。
{ "dependencies": { "config":"file: ./config" } }
查看 node_modules 目錄我們會發(fā)現(xiàn)多出來一個名為 config,指向上層 config/ 文件夾的軟鏈接。這是因為 npm 識別 file: 協(xié)議的url,得知這個包需要直接從文件系統(tǒng)中獲取,會自動創(chuàng)建軟鏈接到 node_modules 中,完成“安裝”過程。
2)、場景2:私有g(shù)it共享package
團隊內(nèi)會有一些代碼/公用庫需要在團隊內(nèi)不同項目間共享,但可能由于包含了敏感內(nèi)容。
我們可以簡單的將被依賴的包托管到私有的git倉庫中,然后將git url保存到dependencies中。npm會直接調(diào)用系統(tǒng)的git命令從git倉庫拉取包的內(nèi)容到node_modules中。
npm支持的git url格式:
://[ [: ]@] [: ][:][/] [# | #semver: ]
git 路徑后可以使用 # 指定特定的 git branch/commit/tag, 也可以 #semver: 指定特定的 semver range.
比如:
git+ssh://git@github.com:npm/npm.git#v1.0.27 git+ssh://git@github.com:npm/npm#semver:^5.0 git+https://isaacs@github.com/npm/npm.git git://github.com/npm/npm.git#v1.0.27
3)、場景3:開源package問題修復(fù)
此時我們可以手動進入 node_modules 目錄下修改相應(yīng)的包內(nèi)容,也許修改了一行代碼就修復(fù)了問題。但是這種做法非常不明智!
方案:
fork原作者的git庫,在自己的repo修復(fù)問題,然后將dependencies中的相應(yīng)依賴改為自己修復(fù)后版本的git url就可以解決問題。
npm i執(zhí)行完畢,node_modules中看到所有的依賴包。開發(fā)人員無關(guān)注node_modules文件夾的結(jié)構(gòu)細(xì)節(jié),關(guān)注業(yè)務(wù)代碼中引用依賴包。
理解node_modules結(jié)構(gòu)幫助我們更好理解npm如何工作。npm2到npm5變化和改進。
3.1 npm2
npm2在安裝依賴包,采用的是簡單的遞歸安裝方法。每一個包都有自己的依賴包,每一個包的依賴都安裝在自己的node_modules中,依賴關(guān)系層層遞進,構(gòu)成整個依賴樹,這個依賴樹與文件系統(tǒng)中的文件結(jié)構(gòu)樹一一對應(yīng)。
最方便的依賴樹的方式在根目錄下執(zhí)行npm ls
。
優(yōu)點:
層級結(jié)構(gòu)明顯,便于傻瓜式管理。
缺點:
復(fù)雜工程,目錄結(jié)構(gòu)可能太深,深層的文件路徑過長觸發(fā)window文件系統(tǒng)中文件路徑不能超過260個字符長。
部分被多個包依賴的包在很多地方重復(fù)安裝,造成大量的冗余。
3.2 npm3
npm3的node_modules目錄改成更加扁平狀層級結(jié)構(gòu)。npm3在安裝的時候遍歷整個依賴樹,計算最合理的文件夾安裝方式,所有被重復(fù)依賴的包都可以去重安裝。
npm來說,同名不同版本的包是兩個獨立的包。
npm3的依賴樹結(jié)構(gòu)不再與文件夾層級一一對應(yīng)。
3.3 npm5
沿用npm3的扁平化依賴包安裝方式。最大的變化時增加package-lock.json文件。
package-lock.json作用:鎖定依賴安裝結(jié)構(gòu),發(fā)現(xiàn)node_modules目錄文件層級結(jié)構(gòu)是與json的結(jié)構(gòu)一一對應(yīng)。
npm5默認(rèn)會在執(zhí)行npm i后生成package-lock.json文件,提交到git/svn代碼庫。
要升級,不要使用 5.0版本。
注意:在 npm 5.0 中,如果已有 package-lock 文件存在,若手動在 package.json 文件新增一條依賴,再執(zhí)行 npm install, 新增的依賴并不會被安裝到 node_modules 中, package-lock.json 也不會做相應(yīng)的更新。
介紹依賴包升級管理相關(guān)知識。
4.1 語義化版本semver
npm依賴管理的一個重要特性采用語義化版本(semver)規(guī)范,作為版本管理方案。
語義化版本號必須包含三個數(shù)字,格式:major.minor.patch。意思是:主版本號.小版本號.修改版本號。
我們需要在dependencies中使用semver約定的指定所需依賴包的版本號或者范圍。
常用的規(guī)則如下圖:
semver語義化版本
1、任意兩條規(guī)則,用空格連接起來,表示“與”邏輯,即為兩個規(guī)則的交集。
如 >=2.3.1 <=2.8.0 可以解讀為: >=2.3.1 且 <=2.8.0
可以匹配 2.3.1, 2.4.5, 2.8.0
但不匹配 1.0.0, 2.3.0, 2.8.1, 3.0.0
2、任意兩條規(guī)則,用||連接起來,表示“或”邏輯,即為兩條規(guī)則的并集。
如 ^2 >=2.3.1 || ^3 >3.2
可以匹配 2.3.1, 2,8.1, 3.3.1
但不匹配 1.0.0, 2.2.0, 3.1.0, 4.0.0
3、更直觀的表示版本號范圍的寫法
或 x 匹配所有主版本
1 或 1.x 匹配 主版本號為 1 的所有版本
1.2 或 1.2.x 匹配 版本號為 1.2 開頭的所有版本
4、在 MAJOR.MINOR.PATCH 后追加 - 后跟點號分隔的標(biāo)簽,作為預(yù)發(fā)布版本標(biāo)簽 通常被視為不穩(wěn)定、不建議生產(chǎn)使用的版本。
1.0.0-alpha
1.0.0-beta.1
1.0.0-rc.3
4.2 依賴版本升級
在安裝完一個依賴包之后有新的版本發(fā)布了,如何使用npm進行版本升級呢?
npm i或者npm update,但是不同的npm版本,不同的package.json和package-lock.json文件,安裝和升級表現(xiàn)是不同的。
使用npm3的結(jié)論:
如果本地 node_modules 已安裝,再次執(zhí)行 install 不會更新包版本, 執(zhí)行 update 才會更新; 而如果本地 node_modules 為空時,執(zhí)行 install/update 都會直接安裝更新包。
npm update 總是會把包更新到符合 package.json 中指定的 semver 的最新版本號——本例中符合 ^1.8.0 的最新版本為 1.15.0
一旦給定 package.json, 無論后面執(zhí)行 npm install 還是 update, package.json 中的 webpack 版本一直頑固地保持 一開始的 ^1.8.0 巋然不動
使用npm5的結(jié)論:
無論何時執(zhí)行 install, npm 都會優(yōu)先按照 package-lock 中指定的版本來安裝 webpack; 避免了 npm 3 表中情形 b) 的狀況;
無論何時完成安裝/更新, package-lock 文件總會跟著 node_modules 更新 —— (因此可以視 package-lock 文件為 node_modules 的 JSON 表述)
已安裝 node_modules 后若執(zhí)行 npm update,package.json 中的版本號也會隨之更改為 ^1.15.0
4.3 最佳實踐
我常用的node是8.11.x,npm是5.6.0。
使用npm >= 5.1 版本,保持package-lock.json文件默認(rèn)開啟配置。
初始化,npm i 安裝依賴包,默認(rèn)保存^X.Y.Z,項目提交package.json和package-lock.json。
不要手動修改package-lock.json
升級依賴包:
升級小版本,執(zhí)行npm update升級到新的小版本。
升級大版本,執(zhí)行npm install @ 升級到新的大版本。
手動修改package.json中的版本號,然后npm i。
本地驗證升級新版本后沒有問題,提交新的package.json和package-lock.json文件。
降級依賴包:
正確:npm i @驗證沒有問題后,提交package.json和package-lock.json文件。
錯誤:修改package.json中的版本號,執(zhí)行npm i不會生效。因為package-lock.json鎖定了版本。
刪除依賴包:
A計劃:npm uninstall 。提交package.json和package-lock.json。
B計劃:在package.json中刪除對應(yīng)的包,然后執(zhí)行npm i,提交package.json和package-lock.json。
5.1 基本使用
npm scripts是npm的一個重要的特性。在package.json中scripts字段定義一個腳本。
比如:
{ "scripts": { "echo": "echo HELLO WORLD" } }
我們可以通過npm run echo 命令執(zhí)行這段腳本,就像shell中執(zhí)行echo HELLO WOLRD,終端是可以看到輸出的。
總結(jié)如下:
npm run 命令執(zhí)行時,會把./node_modules/.bin目錄添加到執(zhí)行環(huán)境的PATH變量中。全局的沒有安裝的包,在node_modules中安裝了,通過npm run 可以調(diào)用該命令。
執(zhí)行npm 腳本時要傳入?yún)?shù),需要在命令后加 -- 表明,比如 npm run test -- --grep="pattern" 可以將--grep="pattern"參數(shù)傳給test命令。
npm 還提供了pre和post兩種鉤子的機制,可以定義某個腳本前后的執(zhí)行腳本。
運行時變量:npm run 的腳本執(zhí)行環(huán)境內(nèi),可以通過環(huán)境變量的方式獲取更多的運行相關(guān)的信息??梢酝ㄟ^process.env對象訪問獲得:
npm_lifecycle_event:正在運行的腳本名稱
npm_package_:獲取當(dāng)前package.json中某一個字段的匹配值:如包名npm_package_name
npm_package__:package中的嵌套字段。
5.2 node_modules/.bin目錄
保存了依賴目錄中所安裝的可供調(diào)用的命令行包。本質(zhì)是一個可執(zhí)行文件到指定文件源的映射。
例如 webpack 就屬于一個命令行包。如果我們在安裝 webpack 時添加 --global 參數(shù),就可以在終端直接輸入 webpack 進行調(diào)用。
上一節(jié)所說,npm run 命令在執(zhí)行時會把 ./node_modules/.bin 加入到 PATH 中,使我們可直接調(diào)用所有提供了命令行調(diào)用接口的依賴包。所以這里就引出了一個最佳實踐:
?將項目依賴的命令行工具安裝到項目依賴文件夾中,然后通過 npm scripts 調(diào)用;而非全局安裝
于是 npm 從5.2 開始自帶了一個新的工具 npx.
5.3 npx
npx 的使用很簡單,就是執(zhí)行 npx 即可,這里的 默認(rèn)就是 ./node_modules 目錄中安裝的可執(zhí)行腳本名。例如上面本地安裝好的 webpack 包,我們可以直接使用 npx webpack 執(zhí)行即可。
5.4 用法
1、傳入?yún)?shù)
"scripts": { "serve": "vue-cli-service serve", "serve1": "vue-cli-service --serve1", "serve2": "vue-cli-service -serve2", "serve3": "vue-cli-service serve --mode=dev --mobile -config build/example.js" }
除了第一個可執(zhí)行的命令,以空格分割的任何字符串都是參數(shù),并且都能通過process.argv屬性訪問。
比如執(zhí)行npm run serve3命令,process.argv的具體內(nèi)容為:
[ '/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' ]
2、多命令運行 在啟動時可能需要同時執(zhí)行多個任務(wù),多個任務(wù)的執(zhí)行順序決定了項目的表現(xiàn)。
1)串行執(zhí)行
串行執(zhí)行,要求前一個任務(wù)執(zhí)行成功之后才能執(zhí)行下一個任務(wù)。使用 && 服務(wù)來連接。
npm run scipt1 && npm run script2
串行執(zhí)行命令,只要一個命令執(zhí)行失敗,整個腳本會中止的。
2)并行執(zhí)行
并行執(zhí)行,就是多個命令同時平行執(zhí)行,使用 & 符號來連接。
npm run script1 & npm run script2
3、env 環(huán)境變量 在執(zhí)行npm run腳本時,npm會設(shè)置一些特殊的env環(huán)境變量。其中package.json中的所有字段,都會被設(shè)置為以npm_package_ 開頭的環(huán)境變量。
4、指令鉤子 在執(zhí)行npm scripts命令(無論是自定義還是內(nèi)置)時,都經(jīng)歷了pre和post兩個鉤子,在這兩個鉤子中可以定義某個命令執(zhí)行前后的命令。比如在執(zhí)行npm run serve命令時,會依次執(zhí)行npm run preserve、npm run serve、npm run postserve,所以可以在這兩個鉤子中自定義一些動作:
"scripts": { "preserve": "xxxxx", "serve": "cross-env NODE_ENV=production webpack", "postserve": "xxxxxx" }
5、常用腳本示例
// 刪除目錄 "clean": "rimraf dist/*", // 本地搭建一個http服務(wù) "server": "http-server -p 9090 dist/", // 打開瀏覽器 "open:dev": "opener http://localhost:9090", // 實時刷新 "livereload": "live-reload --port 9091 dist/", // 構(gòu)建 HTML 文件 "build:html": "jade index.jade > dist/index.html", // 只要 CSS 文件有變動,就重新執(zhí)行構(gòu)建 "watch:css": "watch 'npm run build:css' assets/styles/", // 只要 HTML 文件有變動,就重新執(zhí)行構(gòu)建 "watch:html": "watch 'npm run build:html' assets/html", // 部署到 Amazon S3 "deploy:prod": "s3-cli sync ./dist/ s3://example-com/prod-site/", // 構(gòu)建 favicon "build:favicon": "node scripts/favicon.js",
6.1 npm config
通過npm config ls -l 可查看npm 的所有配置,包括默認(rèn)配置。
通過npm config set ,常見配置:
proxy,https-proxy:指定npm使用的代理
registry:指定npm下載安裝包時的源,默認(rèn)是https://registry.npmjs.org??梢灾付ㄋ接械膔egistry源。
package-lock.json:指定是否默認(rèn)生成package-lock.json,建議保持默認(rèn)true。
save :true/false指定是否在npm i之后保存包為dependencies,npm5開始默認(rèn)為true。
通過npm config delete 刪除指定的配置項。
6.2 npmrc文件
可以通過刪除npm config命令修改配置,還可以通過npmrc文件直接修改配置。
npmrc文件優(yōu)先級由高到低,包括:
工程內(nèi)配置文件:項目根目錄下的.npmrc文件
用戶級配置文件:用戶配置里
全局配置文件
npm內(nèi)置配置文件 我們可以在自己的團隊中在根目錄下創(chuàng)建一個.npmrc文件來共享需要在團隊中共享的npm運行相關(guān)配置。
比如:我們在公司內(nèi)網(wǎng)下需要代理才能訪問默認(rèn)源:https://registry.npmjs.org源;或者訪問內(nèi)網(wǎng)的registry,就可以在工作項目下新增.npmrc文件并提交代碼庫。
示例配置:
proxy = http://proxy.example.com/ https-proxy = http://proxy.example.com/ registry = http://registry.example.com/
這種在工程內(nèi)配置文件的優(yōu)先級最高,作用域在這個項目下,可以很好的隔離公司項目和學(xué)習(xí)研究的項目兩種不同環(huán)境。
將這個功能與 ~/.npm-init.js 配置相結(jié)合,可以將特定配置的 .npmrc 跟 .gitignore, README 之類文件一起做到 npm init 腳手架中,進一步減少手動配置。
6.3 node版本約束
一個團隊中共享了相同的代碼,但是每個人開發(fā)機器不一致,使用的node版本也不一致,服務(wù)端可能與開發(fā)環(huán)境不一致。
這就帶來了不一致的因素----方案:聲明式約束+腳本限制。
聲明:通過package.json的engines屬性聲明應(yīng)用運行所需的版本要求。例如我呢項目中使用了async,await特性,得知node查閱兼容表格[1]得知最低支持版本是7.6.0.因此指定engines配置為:
{ "engines": {"node": ">=7.6.0"} }
強約束(可選):需要添加強約束,需要自己寫腳本鉤子,讀取并解析engines字段的semver range并與運行環(huán)境做比對校驗并適當(dāng)提醒。
感謝各位的閱讀,以上就是“nodejs npm的原理是什么”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對nodejs npm的原理是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!