Day3: npm補(bǔ)充 和 Express

寫(xiě)在前面


昨天說(shuō)的npm看了之后, 覺(jué)得有兩個(gè)指的記錄, 再就是寫(xiě)下Express

npm補(bǔ)充


npm2和npm3的區(qū)別


就本地安裝的包來(lái)說(shuō), npm2npm3有一個(gè)很大的區(qū)別, 就是組織包的結(jié)構(gòu). npm2組織依賴的包是按照樹(shù)形組織的. npm3將其改進(jìn)為扁平結(jié)構(gòu).

npm2會(huì)將所依賴的包存放到當(dāng)前目錄的./node_modules/目錄下. 而被安裝的包又會(huì)依賴其他的包的話, 則會(huì)存放到該包的./node_modules下. 所以, 當(dāng)依賴結(jié)構(gòu)很復(fù)雜的時(shí)候, 目錄結(jié)構(gòu)會(huì)非常深. 不管是性能還是操作上, 體驗(yàn)都不怎么好.

your_project/
  node_modules/
    module_a@1.0/
      node_modules/
        module_b@1.0/
    module_c@1.0/
      node_modules/
        module_b@1.0
    module_d@1.0/
      node_modules/
        module_b@2.0/

而在npm3中, 采用扁平的目錄結(jié)構(gòu), 二級(jí)依賴會(huì)放到當(dāng)前目錄的node_modules的里, 與一級(jí)包在同一目錄.

your_project/
  node_modules/
    module_a@1.0/
    module_b@1.0
    module_c@1.0/
    module_d@1.0/
      node_modules/
        module_b@2.0/

盡量都安裝到項(xiàng)目目錄下的./node_modules中. 但是, 這樣會(huì)遇到幾個(gè)問(wèn)題.

由于安裝順序不同, ./node_modules的目錄結(jié)構(gòu)也可能不同, 這樣導(dǎo)致即使是相同的依賴關(guān)系, 目錄結(jié)構(gòu)也不一定一樣. 例如在npm3例子中, 如果先安裝module_d的話, module_b@2.0則會(huì)和module_d同級(jí), 這樣module_amodule_c所依賴的module_b@1.0則會(huì)在各自的./node_modules目錄下, 即使是重復(fù)的. 像下面這樣:

your_project/
  node_modules/
    module_a@1.0/
      node_modules/
        module_b@1.0
    module_c@1.0/
      node_modules/
        module_b@1.0
    module_d@1.0/
    module_b@2.0/

雖然一般情況下, 這個(gè)并不影響項(xiàng)目運(yùn)行, 但是如果要達(dá)成目錄結(jié)構(gòu)一致的話, 解決方法是, 刪除./node_modules目錄下的所有文件, 重新安裝. 因?yàn)?code>npm會(huì)按照依賴包的字符表順序排序后, 按照這個(gè)順序進(jìn)行安裝.

npm安裝全局包遇到權(quán)限問(wèn)題


還有一點(diǎn), 就是在Linux下安裝npm全局包的話, 會(huì)遇到權(quán)限問(wèn)題. 我以前一般是用sudo添加權(quán)限. 而又兩個(gè)方法更好:

1, 用chown來(lái)改變目錄的所有者, 全局包安裝在npm config get prefix目錄下, 將這個(gè)目錄的所有者改成運(yùn)行項(xiàng)目的人就行.

sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share}

2, 利用npm config set prefix <new_dir>設(shè)置到當(dāng)前用戶擁有讀寫(xiě)權(quán)限的目錄下. 再講這個(gè)路徑加到PATH中.

mkdir ~/.npm-global
npm config set prefix '~/.npm-global'
export PATH=~/.npm-global/bin:$PATH
source ~/.profile

如果不想更改PATH系統(tǒng)變量, 可以這樣. 由于npm最后會(huì)在/usr/local/bin目錄下創(chuàng)建鏈接文件, 指向包的入口文件, 所以這樣也是OK的.

NPM_CONFIG_PREFIX=~/.npm-global npm install -g jshint

Express


Express中, 采用的是一種中間件的方式對(duì)請(qǐng)求進(jìn)行處理.

       +--------+--------+--------+--------+
req -> |   MW1  |   MW2  |   MW3  |   MW4  |
       | -----> | -----> | -----> | ---    |
       |    \   |    \   |    \   |    \   |
res <- |  <-/   |  <-/   |  <-/   |  <-/   |
       +--------+--------+--------+--------+

HTTP請(qǐng)求會(huì)依次經(jīng)過(guò)每個(gè)中間件處理, 每個(gè)中間件可以選擇處理該請(qǐng)求, 返回, 或者交給下一個(gè)中間件繼續(xù)處理. 類似于Java Web的filter. 而末尾的中間件, 可以看做成action.

寫(xiě)Express應(yīng)用一般是這樣.

var express = require('express');
var app = express();

// respond with "hello world" when a GET request is made to the homepage
app.get('/', function(req, res) {
  res.send('hello world');
});

第一行加載Express模塊, 第二行創(chuàng)建Express對(duì)象. app.get就是設(shè)置一個(gè)中間件, 這個(gè)中間件只處理GET請(qǐng)求. app.get第一個(gè)參數(shù)是當(dāng)HTTP請(qǐng)求的URL匹配這個(gè)參數(shù)的時(shí)候, 運(yùn)行第二個(gè)參數(shù)傳入函數(shù).

每個(gè)中間件就是一個(gè)接受三個(gè)參數(shù)的函數(shù).

function middleware(req, res, next){
  // your code
}

Express加載中間件的方式也有很多

app.use(function(req, res, next){
  // your code.
});
app.use('url_pattern_string', function(req, res, next){
  // your code.
});

使用app對(duì)象有很多方法, 其中, use表示所有HTTP請(qǐng)求都需要經(jīng)過(guò)這個(gè)中間件, 還有幾個(gè)針對(duì)HTTP請(qǐng)求類型的, 比如get, post, put, delete等等. 這些只處理相應(yīng)類型的HTTP請(qǐng)求, 不處理除此之外其他類型的請(qǐng)求.

更好的組織代碼的方式是利用Router. 在router對(duì)象上設(shè)置各種處理, 然后將這個(gè)router作為中間件使用.

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/index', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

module.exports = router;

然后再添加到app

var express = require('express');

var routes = require('./routes/index');

app.use('/routes', routes);

這樣, 訪問(wèn)/routes/index會(huì)首先匹配到route對(duì)象, 然后在這個(gè)對(duì)象進(jìn)行下一步的匹配, 匹配到/index, 就執(zhí)行這里的方法, 渲染index模板并返回給瀏覽器.

總體看來(lái), 可以認(rèn)為Express是對(duì)HTTP請(qǐng)求的URL和中間件進(jìn)行匹配的. 匹配過(guò)程中會(huì)找到一些符合的中間件, 執(zhí)行這些中間件. 中間件有很多種, 有針對(duì)某種HTTP請(qǐng)求類型的中間件, 也有不管HTTP請(qǐng)求類型, 都要執(zhí)行的中間件. 這都是通過(guò)在設(shè)置中間件時(shí), 使用哪種方法設(shè)置來(lái)區(qū)別的.

app.use() // HTTP 所有類型的請(qǐng)求
app.get() // HTTP GET請(qǐng)求
app.post() // HTTP POST請(qǐng)求
...

Express支持很多請(qǐng)求類型.

最重要的還有中間件接受的參數(shù), req, res,和next.

Request


Request用來(lái)封裝HTTP請(qǐng)求的信息.

路由匹配過(guò)程中可以匹配參數(shù).

app.get('/user/:id', function(req, res){
  res.end('' + req.params.id);
});

get參數(shù)可以在req.query中訪問(wèn)

// GET /shoes?order=desc&shoe[color]=blue&shoe[type]=converse

req.query.order
// => "desc"

req.query.shoe.color
// => "blue"

req.query.shoe.type
// => "converse"

訪問(wèn)HTTP請(qǐng)求頭

req.get('Content-Type');
// => "text/plain"

req.get('content-type');
// => "text/plain"

req.get('Something');
// => undefined

其他的方法可以查Express 4.x Request

Response


res.locals針對(duì)當(dāng)前請(qǐng)求, 保存一些信息, 可以被模板引擎直接訪問(wèn), 也可以在其他的中間件中訪問(wèn)到. 不同請(qǐng)求, locals是新的對(duì)象, 請(qǐng)求之間是隔離的.

res.locals.title = 'Express';
res.render('index'); // 在模板引擎中可以直接訪問(wèn)title變量, 值為上面指定的`Express

app.use(function(req, res, next){
  res.locals.user = req.user;
  res.locals.authenticated = ! req.user.anonymous;
  next(); // 下一個(gè)中間件可以訪問(wèn)這兩個(gè)變量
});

通過(guò)res可以設(shè)置cookie

// res.cookie(name, value [, options])
res.cookie('name', 'tobi', { domain: '.example.com', path: '/admin', secure: true });
res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });

響應(yīng)有很多種

  • res.json([body])返回JSON
  • res.jsonp([body])返回JSONP
  • res.download(path [, filename] [, fn]), 返回文件, 使瀏覽器下載
  • res.redirect([status,] path)返回重定向
  • res.render(view [, locals] [, callback])進(jìn)行模板渲染, 然后傳輸給瀏覽器

進(jìn)一步的參考Express 4.x Response

最后


Koa出來(lái)之前, Express很腫脹, 到了4.x的時(shí)候, Express將很多東西都作為中間件單獨(dú)提供, 清爽很多, 而且官網(wǎng)首頁(yè)的一句話介紹也改了. 有過(guò)一段時(shí)間, 想學(xué)學(xué)connect. 但是放棄了, 覺(jué)得Express更方便一些.

對(duì)于Koa, 使用generator function的確很好, 但是函數(shù)的上下文有點(diǎn)混亂, 等Express的文檔看完了, 有空好好學(xué)學(xué)ES6`

對(duì)于看ES6的內(nèi)容來(lái)說(shuō), 大概看了下, 覺(jué)得阮一峰的<ECMAScript 6 入門>寫(xiě)的非常好. JavaScript語(yǔ)言有很多不符合直覺(jué)的地方, 很別扭, 比如:

// Chrome 版本 47.0.2526.111 m (64-bit)
[] + []
// => ""
{} + []
// => 0
[] + {}
// => "[object Object]"
{} + {}
// => NaN
NaN === NaN
// => false

// 還有很多...

JavaScript也有很多不好的地方. 比如不同瀏覽器支持的程度不一樣, 新的標(biāo)準(zhǔn)出來(lái)了, 瀏覽器要跟上也得過(guò)段時(shí)間; 不同瀏覽器又會(huì)實(shí)驗(yàn)性地拓展內(nèi)容; 為了使用瀏覽器未實(shí)現(xiàn)的語(yǔ)言標(biāo)準(zhǔn), 出現(xiàn)了中間層來(lái)轉(zhuǎn)換(babel). 為了磨平瀏覽器對(duì)于語(yǔ)言支持的區(qū)別, 也出現(xiàn)了shim / polyfill; ES6還沒(méi)學(xué)會(huì)呢, ES7就快來(lái)了(已經(jīng)有不少草案了);

雖然JavaScript作為服務(wù)器端腳本, 相對(duì)其他的腳本語(yǔ)言有所欠缺, 但是我等相信, 未來(lái)更好.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末族壳,一起剝皮案震驚了整個(gè)濱河市遗锣,隨后出現(xiàn)的幾起案子速妖,更是在濱河造成了極大的恐慌,老刑警劉巖触趴,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昨稼,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)抛人,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)脐瑰,“玉大人妖枚,你說(shuō)我怎么就攤上這事〔栽冢” “怎么了绝页?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)寂恬。 經(jīng)常有香客問(wèn)我续誉,道長(zhǎng),這世上最難降的妖魔是什么初肉? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任酷鸦,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘井佑。我一直安慰自己属铁,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布躬翁。 她就那樣靜靜地躺著焦蘑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪盒发。 梳的紋絲不亂的頭發(fā)上例嘱,一...
    開(kāi)封第一講書(shū)人閱讀 51,727評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音宁舰,去河邊找鬼拼卵。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蛮艰,可吹牛的內(nèi)容都是我干的腋腮。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼壤蚜,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼即寡!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起袜刷,我...
    開(kāi)封第一講書(shū)人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤聪富,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后著蟹,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體墩蔓,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年萧豆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了奸披。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡炕横,死狀恐怖源内,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情份殿,我是刑警寧澤膜钓,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站卿嘲,受9級(jí)特大地震影響颂斜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拾枣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一沃疮、第九天 我趴在偏房一處隱蔽的房頂上張望盒让。 院中可真熱鬧,春花似錦司蔬、人聲如沸邑茄。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)肺缕。三九已至,卻和暖如春授帕,著一層夾襖步出監(jiān)牢的瞬間同木,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工跛十, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留彤路,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓芥映,卻偏偏與公主長(zhǎng)得像洲尊,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子屏轰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容