題目1: 如何全局安裝一個 node 應(yīng)用?
Node
模塊采用npm install
命令安裝。
每個模塊可以“全局安裝”,也可以“本地安裝”。“全局安裝”指的是將一個模塊安裝到系統(tǒng)目錄中,各個項(xiàng)目都可以調(diào)用。一般來說,全局安裝只適用于工具模塊,比如eslint
和gulp
。“本地安裝”指的是將一個模塊下載到當(dāng)前項(xiàng)目的node_modules
子目錄,然后只有在項(xiàng)目目錄之中,才能調(diào)用這個模塊。
# 本地安裝
npm install <package name>
# 全局安裝
npm install -g <package name>
# npm更新最新版本
cnpm install npm@lastest -g
如果下載速度比較慢,可以使用淘寶的鏡像
//先執(zhí)行下面命令
npm install -g cnpm --registry=https://registry.npm.taobao.org
//以后安裝就用 cnpm 代替 npm
cnpm install -g xxx
install 可以縮寫成 i
題目2: package.json 有什么作用?
命令行npm init
可以初始化生成一個package.json
package.json
是一個 json
格式的文件,用來記錄當(dāng)前的npm
包的相關(guān)信息,如
name
:包的名字
version
:版本號
description
:描述
main
:包的入口文件
script
: 運(yùn)行腳本命令的npm命令行縮寫
author
: 作者
license
: 版權(quán)信息
dependencies
:項(xiàng)目運(yùn)行依賴,發(fā)布的時候,不需要發(fā)布依賴的包,只要發(fā)布其名字,別人下載的時候,會自動下載依賴的包。
devDependencies
:開發(fā)依賴,只有自己本地開發(fā)時候用的依賴包,發(fā)布以后別人不能用。
{
"name": "packageName",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "cg",
"license": "ISC",
"dependencies": {},
"devDependencies": {}
}
題目3: npm install --save app 與 npm install --save-dev app有什么區(qū)別?
命令行參數(shù)
當(dāng)你為你的模塊安裝一個依賴模塊時,正常情況下你得先安裝他們,在模塊根目錄下npm install module-name
,然后連同版本號手動將他們添加到模塊配置文件package.json
中的依賴?yán)?code>(dependencies)。
-save
和save-dev
可以省掉你手動修改package.json
文件的步驟。
npm install module-name -save
自動把模塊和版本號添加到dependencies
部分。
npm install module-name -save-dev
自動把模塊和版本號添加到devdependencies
部分。
配置文件
package.json
提供了三種依賴關(guān)系定義:
- dependencies
- peerDependencies
- devDependencies
devDependencies
是開發(fā)時依賴,比如你模塊用了mocha
測試框架,那么你的模塊的開發(fā)就依賴mocha
,如果別人想為你的模塊貢獻(xiàn)代碼,他就需要安裝mocha
。但是只是使用你的模塊的人,就不需要mocha
。
peerDependencies
是為插件準(zhǔn)備的。比如grunt
的插件,里面沒有require(“grunt”)
,所以用dependencies
會有問題。所以需要單獨(dú)列出。
題目4: node_modules的查找路徑是怎樣的?
- 先從當(dāng)前目錄下查找node_modules文件夾,如果沒有,則到父級文件夾查找node_modules文件夾,直至查找到根目錄。
題目5: npm3與 npm2相比有什么改進(jìn)?yarn和 npm 相比有什么優(yōu)勢? (選做題目)
模塊和包
很多時候我們并不是很區(qū)分模塊和包,因?yàn)槲覀円话闶褂脠鼍熬褪?npm install xxx
,然后在文件里直接require('xxx')
。
但是考慮到很多時候我們也可以隨意 require
一個本地自己寫的 JS 文件,只要其按照CommonJS
規(guī)范 export
即可,因此這里需要嚴(yán)格區(qū)分下:
- 模塊:符合
CommonJS
規(guī)范的文件 - 包:一個包含
package.json
以及入口文件的文件夾
這里也不是絕對這樣定義的,不過最終表現(xiàn)出來的就是這些,而 npm
的所有的管理的對象都必須包含 package.json
文件,用于模塊確定依賴關(guān)系。
說了這么多,只是為了強(qiáng)調(diào):npm
是包管理器。
npm2 問題
npm2 安裝依賴的時候比較簡單直接,直接按照包依賴的樹形結(jié)構(gòu)下載填充本地目錄結(jié)構(gòu)。
因?yàn)?npm 設(shè)計(jì)的初衷就是考慮到了包依賴的版本錯綜復(fù)雜的關(guān)系,同一個包因?yàn)楸灰蕾嚨年P(guān)系原因會出現(xiàn)多個版本,簡單地填充結(jié)構(gòu)保證了無論是安裝還是刪除都會有統(tǒng)一的行為和結(jié)構(gòu)。
比如一個 App 里模塊 A 和 C 都依賴 B,無論被依賴的 B 是否是同一個版本,都會生成對應(yīng)結(jié)構(gòu):
于是缺陷就凸顯出來了,太深的目錄樹結(jié)構(gòu)會嚴(yán)重影響效率,甚至在 Windows 下可能會超出系統(tǒng)路徑限制的長度。另外,在 Windows 有刪 node_modules 目錄經(jīng)歷的可能都經(jīng)歷過漫長的等待。
npm3 解決方式
針對 npm2
的問題,npm3
加了點(diǎn)算法,直白的解釋就是:npm install
時會按照 package.json
里依賴的順序依次解析,遇到新的包就把它放在第一級目錄,后面如果遇到一級目錄已經(jīng)存在的包,會先判斷版本,如果版本一樣則忽略,否則會按照npm2
的方式依次掛在依賴包目錄下
還是剛剛的栗子,可以看下npm2
和 npm3
生成的結(jié)構(gòu)對比:
試想,在包版本差異化不太嚴(yán)重的情況下,這種構(gòu)建方式會幾乎把所有包放在一級目錄下,很大程度上提升了效率以及節(jié)省了部分磁盤空間。
其實(shí),npm3 這種方式在理論上其實(shí)會趨于一種平穩(wěn)的狀態(tài),因?yàn)槟憧赡軙f,
npm3
在極端情況下也可能退化為 npm2
的行為,不過這種情況在一般情況下是可以忽略的。npm3
還有個優(yōu)點(diǎn),就是在動態(tài)安裝更新包的時候,是可以進(jìn)一步調(diào)整目錄結(jié)構(gòu)的,比如某種依賴已經(jīng)如下:具體依賴細(xì)節(jié)我們不用追究,假設(shè) E_v1.0 模塊是依賴 B_v1.0 的,此時我們更新 E 到 v2.0,假設(shè)此時依賴 B_v2.0 了,那么最終生成的結(jié)構(gòu)會是如下:
是不是覺得很多冗余?其實(shí)只需執(zhí)行下
npm dedupe
就會變成如下結(jié)構(gòu):
這已經(jīng)很接近我們理想的使用場景了!
npm3 新的問題
你以為就這么完了嗎?注意到上面提到的npm3
會按照package.json
的順序解析目錄樹,試著看下下面的場景:
對應(yīng)的
dependencies
為:
"dependencies": { "mod-a": "^1.0.0", "mod-c": "^1.0.0", "mod-d": "^1.0.0", "mod-e": "^1.0.0"}
如果恰好 A_v1.0 依賴 B_v1.0,然后我們本地升級了 A 到 v2.0,假設(shè)此時依賴 B_v2.0,那么此時目錄結(jié)構(gòu)會變成:
而此時部署到測試平臺呢?因?yàn)?mod-a 在第一個,所以會優(yōu)先解析,也就是 B_v2.0 會優(yōu)先占據(jù)一級目錄,最終可能目錄結(jié)構(gòu)為:
開發(fā)環(huán)境和測試環(huán)境的
node_modules
目錄結(jié)構(gòu)不一樣了!!這個問題很可能會導(dǎo)致一些很微妙的問題,而且很難調(diào)試。如何解決呢?就是本地每次安裝或者升級包后,完整刪除node_modules
目錄然后再install
一次……(感覺比npm2
還粗暴)
新的工具 yarn
除了上面的問題,還有個嚴(yán)重的問題。npm
在使用的時候大多是用語義化版本號管理包依賴的,比如 ~1.0.0
表示只更新補(bǔ)丁,但是世界辣么大,什么人沒有?說不定哪個開發(fā)者就在 patch version
上就搞了major
的升級,即使你本地使用固定版本號也無濟(jì)于事。
當(dāng)然,后面 npm
也有 shrinkwrap 機(jī)制來保證這種一致性,不過說實(shí)話 npm-shrinkwrap.json
略難看,基本屬于給 npm
打補(bǔ)丁,讓我在一個項(xiàng)目引入這個幾乎無法 review
的文件肯定會不開心的。
太多因素導(dǎo)致了 npm
已經(jīng)步履維艱了,估計(jì) Facebook 也累了吧,于是前不久搞了yarn
用來替代npm
了。
我覺得yarn
革命性的更改在于其改變了構(gòu)建的步驟,其它有點(diǎn)都是新構(gòu)建方式的副產(chǎn)物,yarn
構(gòu)建步驟如下:
- Resolution: 向倉庫請求依賴關(guān)系
- Fetching: 看看本地緩存了沒有,否則把包拉到緩存里
- Linking: 直接全部從緩存里構(gòu)建好目錄樹放到 node_modules 里
這里的緩存機(jī)制很像 mvn
之類的,而且其還引入了lockfile
用于鎖定版本號,這很類似shrinkwrap
,不過格式比npm-shrinkwrap.json
更好 review
。除了這些特別明顯的改進(jìn),還有很多體驗(yàn)上的提升,具體可以看官方博客。
題目6: webpack是什么?和其他同類型工具比有什么優(yōu)勢?
WebPack
可以看做是模塊打包機(jī):它做的事情是,分析你的項(xiàng)目結(jié)構(gòu),找到JavaScript
模塊以及其它的一些瀏覽器不能直接運(yùn)行的拓展語言(Scss
,TypeScript
等),并將其打包為合適的格式以供瀏覽器使用。
WebPack和Grunt以及Gulp相比有什么特性
其實(shí) Webpack 和另外兩個并沒有太多的可比性,Gulp/Grunt 是一種能夠優(yōu)化前端的開發(fā)流程的工具,而WebPack 是一種模塊化的解決方案,不過Webpack 的優(yōu)點(diǎn)使得 Webpack 可以替代 Gulp/Grunt 類的工具。
Grunt 和 Gulp 的工作方式是:在一個配置文件中,指明對某些文件進(jìn)行類似編譯,組合,壓縮等任務(wù)的具體步驟,這個工具之后可以自動替你完成這些任務(wù)。
** Grunt 和 Gulp** 的工作流程
Webpack的工作方式是:把你的項(xiàng)目當(dāng)做一個整體,通過一個給定的主文件(如:index.js),Webpack將從這個文件開始找到你的項(xiàng)目的所有依賴文件,使用loaders處理它們,最后打包為一個瀏覽器可識別的JavaScript文件。
如果實(shí)在要把二者進(jìn)行比較,Webpack的處理速度更快更直接,能打包更多不同類型的文件。
題目7:npm script是什么?如何使用?
npm
允許在package.json
文件里面,使用scripts
字段定義腳本命令。
{
// ...
"scripts": {
"build": "node build.js"
}
}
上面代碼是package.json
文件的一個片段,里面的scripts
字段是一個對象。它的每一個屬性,對應(yīng)一段腳本。比如,build
命令對應(yīng)的腳本是node build.js
。
命令行下使用npm run
命令,就可以執(zhí)行這段腳本。
$ npm run build
等同于執(zhí)行
$ node build.js
這些定義在package.json
里面的腳本,就稱為 npm
腳本。它的優(yōu)點(diǎn)很多。
- 項(xiàng)目的相關(guān)腳本,可以集中在一個地方。
- 不同項(xiàng)目的腳本命令,只要功能相同,就可以有同樣的對外接口。用戶不需要知道怎么測試你的項(xiàng)目,只要運(yùn)行npm run test即可。
- 可以利用 npm 提供的很多輔助功能。
查看當(dāng)前項(xiàng)目的所有 npm 腳本命令,可以使用不帶任何參數(shù)的npm run命令。
$ npm run
題目8: 使用 webpack 替換 入門-任務(wù)15中模塊化使用的 requriejs
github代碼
題目9:gulp是什么?使用 gulp 實(shí)現(xiàn)圖片壓縮、CSS 壓縮合并、JS 壓縮合并
簡介:
gulp 是前端開發(fā)過程中對代碼進(jìn)行構(gòu)建的工具,是自動化項(xiàng)目的構(gòu)建利器;她不僅能對網(wǎng)站資源進(jìn)行優(yōu)化,而且在開發(fā)過程中很多重復(fù)的任務(wù)能夠使用正確的工具自動完成;使用她,我們不僅可以很愉快的編寫代碼,而且大大提高我們的工作效率。
gulp中文網(wǎng)
入門指南
- 全局安裝 gulp:
$ npm install --global gulp
- 作為項(xiàng)目的開發(fā)依賴(devDependencies)安裝:
$ npm install --save-dev gulp
- 在項(xiàng)目根目錄下創(chuàng)建一個名為 gulpfile.js 的文件:
var gulp = require('gulp');
gulp.task('default', function() {
// 將你的默認(rèn)的任務(wù)代碼放在這
});
- 運(yùn)行 gulp:
$ gulp
默認(rèn)的名為 default 的任務(wù)(task)將會被運(yùn)行,在這里,這個任務(wù)并未做任何事情。
想要單獨(dú)執(zhí)行特定的任務(wù)(task),請輸入 gulp <task> <othertask>。
gulp的常用插件
var gulp=require('gulp');
var del=require('del'); // 刪除文件
var minify=require('gulp-minify-css'); //壓縮CSS
var uglify=require('gulp-uglify'); // 壓縮JS
var concat=require('gulp-concat'); // 合并文件
var imagemin=require('gulp-imagemin'); // 縮小圖片
gulp.task('css', function() {
gulp.src('css/*.css')
.pipe(concat('merge.css'))
.pipe(minify())
.pipe(gulp.dest('dist2/css'));
})
gulp.task('js', function() {
gulp.src('js/**/*.js')
.pipe(concat('merge.js'))
.pipe(uglify())
.pipe(gulp.dest('dist2/js'));
})
gulp.task('pic', function() {
gulp.src('images/*')
.pipe(imagemin())
.pipe(gulp.dest('dist2/images'));
})
gulp.task('clean', function() {
del([
'dist2'
])
})
gulp.task('default',['css','js','pic'])