不知從何時開始,網上很是流行面試類的技術文章,講述某次失敗或者成功的面試過程以及面試中被問到的題目,這些文章中的題目大部分都是鬆散零碎,毫無關聯的。可能這些文章會幫助你瞭解到你未曾掌握的點,但僅僅就是了解,真的掌握僅僅只靠面試題是不夠,就比如平時學習不努力,靠考前作幾套名校試卷,或者模擬套題是不夠的。
雖然本身也未能免俗,收藏了一堆面試文章,可是仍是更願意看一些更有技術針對性的文章,甚至能本身寫一篇。這是第一次寫文,原因是工做中的一個需求,多個項目須要共用一些組件,那麼比較方便的作法就是將這些組件封成一個包,發佈到 NPM 上。可是因爲這些組件是公司自用,和公司業務緊密關聯,不便於發佈成公共包,雖然 NPM 如今也提供了私有包服務,可是因爲某些不可抗拒的網絡因素,即便付費可能也享受不到好的服務,因此考慮內部搭建一個 NPM 私有倉庫。
這篇文章就是此次搭建私有倉庫涉及到的相關知識,總結以後發現是一個比較完整知識鏈,因此分享給你們,水平不高,能力有限但願你們多擔待,本文參考了一些內容,也儘可能保證都是本身親自驗證過的,若是有錯誤或疏漏歡迎你們指正。javascript
當咱們如今用 JavaScript 大型單頁應用以及服務端程序時,誰又能想到 JavaScript 這門語言在誕生之初目的只是爲了替服務端完成一些輸入驗證操做。功能的複雜意味着代碼量的提高,而模塊化正是爲了解決所以帶來的維護困難,結構混亂,代碼重複冗餘等問題。很不幸的是在 ES6 以前,JavaScript 並不自然支持模塊化編程。
不過雖然 JavaScript 不支持模塊化編程,可是咱們能夠經過對象,命名空間,當即執行函數等方法實現"模塊"的效果,具體的一些方法能夠參考阮一峯的:Javascript 模塊化編程(一):模塊的寫法。html
這種狀況直到 nodejs 出現,並參照 CommonJS 實現了模塊功能才獲得改善。
在 nodejs 中咱們能夠很方便的導出和引入模塊:前端
// module.js
const name = 'wang';
const age = 18;
function showName() {
console.log(wang);
}
function showAge() {
console.log(age);
}
module.exports = {
showName,
showAge
};
// page.js
const module = require('./module.js');
module.showName(); // wang
module.showAge(); //18
複製代碼
可是 CommonJS 規範不適用於瀏覽器環境,不說瀏覽器沒有 require 方法,服務端文件 require 一個包只是讀取本地的一個文件,而客戶端則是網絡加載,網絡加載速度和硬盤讀寫速度差距可不是一星半點,這種寫法總不能讓客戶端 require 時假死在那裏,前端處理這種問題的方法首先想到應該就是經過異步加載回調來處理。
爲了讓瀏覽器支持模塊化開發,因而出現了基於AMD規範的RequireJS和基於CMD規範的SeaJS
它們的區別主要是(參考知乎上 SeaJs 開發者玉伯的回答):java
- 對於依賴的模塊,AMD 是提早執行,CMD 是延遲執行。不過 RequireJS 從 2.0 開始,也改爲能夠延遲執行(根據寫法不一樣,處理方式不一樣)。CMD 推崇 as lazy as possible。
- CMD 推崇依賴就近,AMD 推崇依賴前置
寫法以下:node
// CMD
define(function(require, exports, module) {
var a = require('./a');
a.doSomething();
var b = require('./b'); // 依賴能夠就近書寫
b.doSomething();
});
// AMD 默認推薦的是
define(['./a', './b'], function(a, b) {
// 依賴必須一開始就寫好
a.doSomething();
b.doSomething();
});
複製代碼
JavaScript 多年之後終於在 ES6 時提出了本身的 modules 規範,寫法以下webpack
//module.js
function showName() {
console.log('wang');
}
function showAge() {
console.log(18);
}
export { showName, showAge };
//page.js
import { showName, showAge } from 'module.js';
showName(); //wang
showAge(); //18
複製代碼
在 chrome61 以後能夠經過給 script 標籤加 type = 'module' 來使用此功能,代碼以下:git
<script type="module" src="./module.js"></script>
<script type="module"> import { showName } from './module.js'; showName(); //wang </script>
複製代碼
其中有一些要注意的點是,不管是被引入方仍是被引入方都要設置 type='module',並且對路徑也有一些要求,具體能夠參考這篇文章瀏覽器中的 ES6 module 實現。github
而在 node 中要使用 es module 要配合命令行參數--experimental-modules 和 mjs 文件後綴名。這個具體能夠參考 nodejs 官方的相關文檔 。
因爲是官方規範,因此普及的很是快,除了直接在 node 中使用不太方便,如今前端開發基本都參照此寫法風格,事實說明一個道理,官方發力,碾壓一切。web
有人會問 webpack 和它們是什麼關係,這裏總結一下。 CommonJS,AMD,CMD,ES Modules 都是規範,RequireJs,SeaJS 是分別基於 AMD 和 CMD 的前端模塊化具體實現,是一種在線模塊編譯方案,引入這兩個庫後,就能夠按照規範進行模塊化開發。而 webpack 是一個打包工具,它是一種預編譯模塊方案,無論是上面哪一種規範它都可以識別,並編譯打包成瀏覽器認識的 js 文件,以實現模塊化開。可能還有人聽過 UMD,UMD 是 AMD 和 CommonJS 的糅合,解決跨平臺的問題,具體就是它會判斷是否支持 Node.js 的模塊(exports 是否存在),存在則使用 Node.js 模塊模式。 再判斷是否支持 AMD(define 是否存在),存在則使用 AMD 方式加載模塊,這種規範 webpack 也是可以識別的。面試
之前咱們想要引入一個第三方包,通常是要將包文件下載下來放入到咱們的項目中,而後在 html 中經過 script 標籤引入,或者這個包有 CDN 服務,那麼能夠直接在 script 中引入這個包的 CDN 網絡地址。這個過程是繁瑣且低效的,那麼有沒有什麼工具可以讓咱們方便的引入第三方包,那就是 npm。
npm 能夠理解爲一個包的倉庫,市場,人們能夠將本身的代碼在 npm 上發佈,讓別人能夠下載分享,npm 原本是做爲 nodejs 的包管理工具隨同 nodejs 一塊兒安裝的,如今基本已經成爲了整個前端標配的包管理工具,經過 npm 咱們能夠很方便的引入第三方包。由於很經常使用,就很少說了。
npm 是咱們最經常使用的包管理工具,可是在早期版本中存在一些缺陷:
目錄結構大概是這個樣子:
├── node_modules
│ └── moduleA
│ └── node_modules
│ └──moduleC
│ └── moduleB
│ └── node_modules
│ └──moduleC
└── package.json
複製代碼
cnpm
是淘寶 NPM 鏡像,官方的說法是
這是一個完整 npmjs.org 鏡像,你能夠用此代替官方版本(只讀),同步頻率目前爲 10 分鐘 一次以保證儘可能與官方服務同步。
它的出現解決了前三條問題,將全部的依賴置於 node_modules 下層,並添加軟連接(快捷方式)。這也就是爲何經過 cnpm 安裝你會在 node_modules 下發現不少文件夾快捷方式。並且因爲 cnpm 的服務器是在國內,因此安裝速度很是快,可是依然沒有解決第四條問題。 目錄結構大概是這個樣子:
├── node_modules
│ ├── _moduleA@1.0.0
│ │ └── node_modules
│ │ └──moduleC
│ ├── _moduleB@1.0.0
│ │ └── node_modules
│ │ └──moduleC
│ │── _moduleC@1.0.0
│ │── moduleA //軟連接(快捷方式)moduleA@1.0.0
│ │── moduleB //軟連接(快捷方式)moduleB@1.0.0
│ └── moduleC //軟鏈接(快捷方式)moduleC@1.0.0
└── package.json
複製代碼
yarn 是一個很是牛逼的項目,曾經有一段時間它將 npm 按在地上摩擦,做爲一個可替代 npm 的包管理器,它解決了 npm 的大部分痛點,又加入了一些本身的功能。
這些優勢讓許多人紛紛投向 yarn 的懷抱(包括我),可是仍是那句話官方發力,碾壓一切。
npm3 以後:
npm5 以後:
總之如今沒有什麼太多的理由讓咱們還能捨棄官方的包管理器而選擇第三方。
(此節參考了文章爲何我從 npm 到 yarn 再到 npm?)
現代前端項目的根目錄下面通常都會有一個 package.json 文件,它是在初始化項目的時候,經過 npm init 命令自動生成的,包含了這個項目所需的依賴和配置信息。
//package.json
{
"name": "demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"npmv-test": "^2.1.0"
}
}
複製代碼
如上面文件展現,項目名稱,做者,描述等不細說,主要來講一下 dependencies,dependencies 是項目依賴(還有 devDependencies 等,不展開細說),能夠看到這個項目依賴了一個名爲 npmv-test 的包,後面的^2.1.0,是描述版本範圍,下面是關於這個版本號的相關的總結。
由於 package.json 中描述依賴包的版本都是範圍,這就形成了一些不肯定性,沒法確保每次安裝依賴的版本都一致,也沒法在出問題時肯定依賴包的版本,而 package-lock.json 就是的出現就是爲了解決這個問題。這個文件詳細的描述了依賴關係和依賴版本,能夠說是 node_modules 文件夾結構和信息的一個快照,每次 node_modules 的變更都會致使 package-lock.json 的更新。
這是上面 package.json 對應的 package-lock.json 文件,咱們能夠看下區別:
{
"name": "demo",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
},
"npmv-test": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/npmv-test/-/npmv-test-2.1.0.tgz",
"integrity": "sha512-tNUwr+sdUek+lyJFmGT2H6Jox50NwA5EmNKAZTL3N5fYU1W7Aucfw+rNVsDinnQnhOF1hNvdU5RCUOvgcRWzng==",
"requires": {
"ms": "^2.1.1"
}
}
}
}
複製代碼
這裏有個問題就是,因爲 package-lock.json 頻繁變更,有些人會將 package-lock.json 文件排除在源碼倉庫的追蹤以外。根據官方文檔說法,是建議將該文件提交到源碼倉庫的,一個是爲了項目每次安裝的依賴版本一致,二是因爲咱們通常將 node_modules 排除在倉庫以外,因此咱們須要在出了問題時可以還原當時的 node_modules 狀況。
衆所周知 npm i 加包名是安裝一個包並添加到 package.json 的依賴中,若是 npm i 不加包名,則是安裝 package.json 依賴中的全部包,而 npm update 對應的則是更新。可是有的時候可能會有一些疑惑,在執行 npm i 命令時好像也更新了包,有的時候 package.json 中的版本明明偏低可是執行 npm update 卻沒有更新,這些問題但願經過下面兩張圖能夠幫助你們找打答案,這個兩張圖是隻是爲了幫助加理解,真實的執行過程順序並不必定一致,有興趣的朋友能夠去看一下源碼的實現。
npm i
npm update
多個項目中重複使用的相同代碼封裝打包成爲一個通用組件庫,既避免了重複造輪子,也利於後期維護和管理,那麼這個東西要怎麼實現有這麼一些方法。 首先既然是庫,確定是要將這些組件單獨拎出來放到一個源碼倉庫裏維護,若是你將這些組件直接打包發佈到 npm 上,那麼這就是一個 npm 公共包,誰均可如下載使用。可是若是不想這樣,那麼有下面這些方法(源碼倉庫爲 git):
此次咱們選擇的方案就是經過搭建私有倉庫來實現,NPM 私有倉庫的工做原理,大概就是將 NPM 命令註冊地址指向咱們的私有倉庫,當咱們經過 npm 命令進行安裝時,若是在私有倉庫中存在則從私有倉庫中獲取,若是私有倉庫中沒有則去配置的 CNPM 或者 NPM 官方倉庫中獲取。
目前市面上比較常見的私有倉庫搭建方法爲:
它們具體的搭建方法都有相應的文檔,上面的連接就指向文檔地址,這裏就不細說了,這三種方法我都跑過都是可行的,最後選擇了 cpm,並且因爲目前 cpm 用到人還很少,能夠和開發者快速交流及時反饋問題,這裏就打個廣告,推薦一下項目地址。
這篇文章的目的不是教你們怎麼搭建一個私有倉庫(這個是文檔乾的事),而是經過搭建一個私有倉庫引出相關的內容並串聯起來幫助總體理解,能展開的儘可能展開,該點到爲止的就點到爲止。第一次寫文章,發現比想象中的要累,但卻頗有成就感,歡迎你們多多批評指正。也感謝各個社區分享知識的做者,但願能向他們學習,分享更多的東西和你們討論學習。
(沒有公衆號二維碼,也沒有github要你們點贊,都散了吧)。