Node.js Express Helloworld

參考《Node.js開發(fā)指南 ByVoid》 Page83

一、Express簡(jiǎn)介

Node.js 由于不需要另外的 HTTP 服務(wù)器寇漫,因此減少了一層抽象佳簸,給性能帶來不少提升狡逢,但同時(shí)也因此而提高了開發(fā)難度。舉例來說乒疏,我們要實(shí)現(xiàn)一個(gè) POST 數(shù)據(jù)的表單,例如:

<form method="post" action="http://localhost:3000/">
<input type="text" name="title" />
<textarea name="text"></textarea>
<input type="submit" />
</form>

這個(gè)表單包含兩個(gè)字段: title 和 text 饮焦,提交時(shí)以 POST 的方式將請(qǐng)求發(fā)送給http://localhost:3000/怕吴。假設(shè)我們要實(shí)現(xiàn)的功能是將這兩個(gè)字段的東西原封不動(dòng)地返回給用戶,PHP 只需寫兩行代碼县踢,儲(chǔ)存為 index.php 放在網(wǎng)站根目錄下即可:

echo $_POST['title'];
echo $_POST['text'];

在 3.5.1 節(jié)中使用了類似下面的方法(用 http 模塊):

var http = require('http');
var querystring = require('querystring');
var server = http.createServer(function(req, res) {
    var post = '';

    req.on('data', function(chunk) {
        post += chunk;
    });

    req.on('end', function() {
        post = querystring.parse(post);
        res.write(post.title);
        res.write(post.text);
        res.end();
    });
}).listen(3000);

這種差別可能會(huì)讓你大吃一驚转绷,PHP 的實(shí)現(xiàn)要比Node.js容易得多。Node.js 完成這樣一個(gè)簡(jiǎn)單任務(wù)竟然如此復(fù)雜:你需要先創(chuàng)建一個(gè) http 的實(shí)例硼啤,在其請(qǐng)求處理函數(shù)中手動(dòng)編寫req 對(duì)象的事件監(jiān)聽器议经。當(dāng)客戶端數(shù)據(jù)到達(dá)時(shí),將 POST數(shù)據(jù)暫存在閉包的變量中谴返,直到 end事件觸發(fā)煞肾,解析 POST 請(qǐng)求,處理后返回客戶端嗓袱。

其實(shí)這個(gè)比較是不公平的扯旷,PHP 之所以顯得簡(jiǎn)單并不是因?yàn)樗鼪]有做這些事,而是因?yàn)镻HP 已經(jīng)將這些工作完全封裝好了索抓,只提供了一個(gè)高層的接口钧忽,而 Node.js 的 http 模塊提供的是底層的接口,盡管使用起來復(fù)雜逼肯,卻可以讓我們對(duì) HTTP 協(xié)議的理解更加清晰耸黑。但是等等,我們并不是為了理解 HTTP 協(xié)議才來使用 Node.js 的篮幢,作為 Web 應(yīng)用開發(fā)者大刊,我們不需要知道實(shí)現(xiàn)的細(xì)節(jié),更不想與這些細(xì)節(jié)糾纏從而降低開發(fā)效率三椿。難道 Node.js 的抽象如此之差缺菌,把不該有的細(xì)節(jié)都暴露給了開發(fā)者嗎葫辐?

實(shí)際上,Node.js 雖然提供了 http 模塊伴郁,卻不是讓你直接用這個(gè)模塊進(jìn)行 Web 開發(fā)的耿战。http 模塊僅僅是一個(gè) HTTP 服務(wù)器內(nèi)核的封裝,你可以用它做任何 HTTP 服務(wù)器能做的事情焊傅,不僅僅是做一個(gè)網(wǎng)站剂陡,甚至實(shí)現(xiàn)一個(gè) HTTP代理服務(wù)器都行。你如果想用它直接開發(fā)網(wǎng)站狐胎,那么就必須手動(dòng)實(shí)現(xiàn)所有的東西了鸭栖,小到一個(gè) POST 請(qǐng)求,大到 Cookie握巢、會(huì)話的管理晕鹊。當(dāng)你用這種方式建成一個(gè)網(wǎng)站的時(shí)候,你就幾乎已經(jīng)做好了一個(gè)完整的框架了暴浦。

npm 提供了大量的第三方模塊捏题,其中不乏許多 Web 框架,我們沒有必要重復(fù)發(fā)明輪子肉渴,因而選擇使用 Express 作為開發(fā)框架公荧,因?yàn)樗悄壳白罘€(wěn)定、使用最廣泛同规,而且 Node.js 官方推薦的唯一一個(gè) Web 開發(fā)框架循狰。Express 除了為 http 模塊提供了更高層的接口外,還實(shí)現(xiàn)了許多功能券勺,其中包括:

  • 路由控制绪钥;
  • 模板解析支持;
  • 動(dòng)態(tài)視圖关炼;
  • 用戶會(huì)話程腹;
  • CSRF 保護(hù);
  • 靜態(tài)文件服務(wù)儒拂;
  • 錯(cuò)誤控制器寸潦;
  • 訪問日志;
  • 緩存社痛;
  • 插件支持见转。

需要指出的是,Express 不是一個(gè)無所不包的全能框架蒜哀,像 Rails 或 Django 那樣實(shí)現(xiàn)了模板引擎甚至 ORM (Object Relation Model斩箫,對(duì)象關(guān)系模型)。它只是一個(gè)輕量級(jí)的 Web 框架,多數(shù)功能只是對(duì) HTTP協(xié)議中常用操作的封裝乘客,更多的功能需要插件或者整合其他模塊來完成狐血。下面用 Express 重新實(shí)現(xiàn)前面的例子:

var express = require('express');
var app = express.createServer();
app.use(express.bodyParser());
app.all('/', function(req, res) {
    res.send(req.body.title + req.body.text);
});
app.listen(3000);

可以看到,我們不需要手動(dòng)編寫 req 的事件監(jiān)聽器了易核,只需加載 express.bodyParser()就能直接通過 req.body 獲取 POST 的數(shù)據(jù)了匈织。

二、快速開始

1.安裝Express
首先我們要安裝 Express耸成。如果一個(gè)包是某個(gè)工程依賴,那么我們需要在工程的目錄下使用本地模式安裝這個(gè)包浴鸿,如果要通過命令行調(diào)用這個(gè)包中的命令井氢,則需要用全局模式安裝(關(guān)于本地模式和全局模式,參見 3.3.4節(jié))岳链,因此按理說我們使用本地模式安裝 Express 即可花竞。但是Express 像很多框架一樣都提供了 Quick Start(快速開始)工具,這個(gè)工具的功能通常是建立一個(gè)網(wǎng)站最小的基礎(chǔ)框架掸哑,在此基礎(chǔ)上完成開發(fā)约急。當(dāng)然你可以完全自己動(dòng)手,但我還是推薦使用這個(gè)工具更快速地建立網(wǎng)站苗分。為了使用這個(gè)工具厌蔽,我們需要用全局模式安裝Express,因?yàn)橹挥羞@樣我們才能在命令行中使用它摔癣。運(yùn)行以下命令:$ npm install -g express

image.png

但是發(fā)現(xiàn)命令行里用不了express指令奴饮,參考解決windows下npm安裝的模塊執(zhí)行報(bào)錯(cuò):無法將“cnpm”項(xiàng)識(shí)別為 cmdlet、函數(shù)择浊、腳本文件或可運(yùn)行程序的名稱,用npm list --depth=0 -global查看已經(jīng)全局安裝的模塊戴卜,也看到express的版本是4.16.3了。然后把C:\Users\ 用戶名\AppData\Roaming\npm目錄加入環(huán)境變量Path重啟琢岩,發(fā)現(xiàn)還是不行啊投剥。
又參考windows全局安裝express,無法命令行執(zhí)行担孔。,有提示說江锨,4版本需要安裝express-generatorc才能使用express命令。

這是express 4.X 版本的更新 導(dǎo)致的糕篇。參見 https://github.com/visionmedia/express/wiki/New-features-in-4.x泳桦。使用 $ npm install -g express-generator后就解決了。

2.建立工程
Express 在初始化一個(gè)項(xiàng)目的時(shí)候需要指定模板引擎娩缰,默認(rèn)支持Jade和ejs灸撰,為了降低學(xué)習(xí)難度我們推薦使用 ejs,同時(shí)暫時(shí)不添加 CSS 引擎和會(huì)話支持。

注:ejs (Embedded JavaScript) 是一個(gè)標(biāo)簽替換引擎浮毯,其語法與 ASP完疫、PHP 相似,易于學(xué)習(xí)债蓝,目前被廣泛應(yīng)用壳鹤。Express默認(rèn)提供的引擎是 jade,它顛覆了傳統(tǒng)的模板引擎饰迹,制定了一套完整的語法用來生成 HTML 的每個(gè)標(biāo)簽結(jié)構(gòu)芳誓,功能強(qiáng)大但不易學(xué)習(xí)“⊙迹可參考知乎 關(guān)于nodejs的模板引擎锹淌,如何選擇 EJS 和 Jade?赠制,簡(jiǎn)單來說EJS看起來更像HTML赂摆,可以直接拿原生的HTML頁面改成模板。

通過命令express --view=ejs microblog在當(dāng)前目錄中創(chuàng)建子目錄microblog(因版本問題钟些,原書是express -t ejs microblog烟号,最新的使用方式根據(jù)npm庫上面的說明來操作),然后執(zhí)行npm install,所有package.json中記錄的插件都會(huì)安裝到node_modules文件夾內(nèi)政恍。

//package.json:
{
  "name": "microblog",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "cookie-parser": "~1.4.3",
    "debug": "~2.6.9",
    "ejs": "~2.5.7",
    "express": "~4.16.0",
    "http-errors": "~1.6.2",
    "morgan": "~1.9.0"
  }
}

然后在命令行執(zhí)行npm start汪拥,這個(gè)命令會(huì)查找package.json中的scripts。查看bin目錄下的www文件:

var app = require('../app');
var debug = require('debug')('microblog:server');
var http = require('http');

/**
 * Get port from environment and store in Express.
 */

var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);

/**
 * Create HTTP server.
 */

var server = http.createServer(app);

/**
 * Listen on provided port, on all network interfaces.
 */

server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

此時(shí)訪問localhost:3000即可訪問示例頁面篙耗。
3.在www中看到引用了app.js

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/users', usersRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

這里把public,routes,views三個(gè)文件夾都引入進(jìn)來了喷楣,下面逐個(gè)分析一下
4.訪問localhost:3000的流程

var indexRouter = require('./routes/index');
app.use('/', indexRouter);
...
//index.js
var express = require('express');
var router = express.Router();

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

module.exports = router;

將“ / ”路徑映射到 exports.index函數(shù)下。然后調(diào)用模板解析引擎鹤树,翻譯名為 index 的模板铣焊,并傳入一個(gè)對(duì)象作為參數(shù),這個(gè)對(duì)象只有一個(gè)屬性罕伯,即 title: 'Express'曲伊。看一下index.ejs

<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <h1><%= title %></h1>
    <p>Welcome to <%= title %></p>
  </body>
</html>

替換內(nèi)容后追他,瀏覽器發(fā)現(xiàn)要獲取 /stylesheets/style.css坟募,因此會(huì)再次向服務(wù)器發(fā)起請(qǐng)求。app.js 中并沒有一個(gè)路由規(guī)則指派到 /stylesheets/style.css邑狸,但 app 通過app.use(express.static(__dirname + '/public')) 配置了靜態(tài)文件服務(wù)器懈糯,因此/stylesheets/style.css 會(huì)定向到 app.js 所在目錄的子目錄中的文件public/stylesheets/style.css
同樣道理,可以看看http://localhost:3000/users是如何路由的:在users.js中并沒有使用模板单雾,而是直接返回一個(gè)字符串

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

/* GET users listing. */
router.get('/', function(req, res, next) {
  res.send('respond with a resource');
});

module.exports = router;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末赚哗,一起剝皮案震驚了整個(gè)濱河市她紫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌屿储,老刑警劉巖贿讹,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異够掠,居然都是意外死亡民褂,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門疯潭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赊堪,“玉大人,你說我怎么就攤上這事竖哩】蘖” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵期丰,是天一觀的道長(zhǎng)群叶。 經(jīng)常有香客問我吃挑,道長(zhǎng)钝荡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任舶衬,我火速辦了婚禮埠通,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘逛犹。我一直安慰自己端辱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開白布虽画。 她就那樣靜靜地躺著舞蔽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪码撰。 梳的紋絲不亂的頭發(fā)上渗柿,一...
    開封第一講書人閱讀 49,071評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音脖岛,去河邊找鬼朵栖。 笑死,一個(gè)胖子當(dāng)著我的面吹牛柴梆,可吹牛的內(nèi)容都是我干的陨溅。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼绍在,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼门扇!你這毒婦竟也來了雹有?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤悯嗓,失蹤者是張志新(化名)和其女友劉穎件舵,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脯厨,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡铅祸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了合武。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片临梗。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖稼跳,靈堂內(nèi)的尸體忽然破棺而出盟庞,到底是詐尸還是另有隱情,我是刑警寧澤汤善,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布什猖,位于F島的核電站,受9級(jí)特大地震影響红淡,放射性物質(zhì)發(fā)生泄漏不狮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一在旱、第九天 我趴在偏房一處隱蔽的房頂上張望摇零。 院中可真熱鬧,春花似錦桶蝎、人聲如沸驻仅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽噪服。三九已至,卻和暖如春胜茧,著一層夾襖步出監(jiān)牢的瞬間粘优,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工竹揍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留敬飒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓芬位,卻偏偏與公主長(zhǎng)得像无拗,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子昧碉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

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