前情提要
在昨天大致說明了express的程式碼和資料夾架構,路由藍圖、引用檔案和輸出的檔案放在哪裡。今天要來說明要怎麼怎麼看這些檔案,怎麼知道程式進入點在哪裡,因為最後我們要利用webpack重構所有伺服器的檔案所以這邊需要了解所以檔案互相之間的關聯和程式邏輯流程。
中篇:express進入點與流程路線
昨天我們已經對資料夾和伺服器架構做出了說明,所以今天就需要來看看這個伺服器是從哪個檔案開始一步一步串接起來的。要知道一個專案的起始點也非常容易,通常只要看看"package.json"就能夠知道了。而express有快速幫我們建立了這個檔案,簡單看一下應該能看到一個很熟悉的指令
{
略
"scripts": {
"start": "node ./bin/www"
},
略
}
簡單說明"scripts"就是說腳本,通常會包含這個專案常用的所有指令。像是打包產品、開始開發或其他專案需要用到的指令,這邊理解成他是一個利用"npm"快速執行的腳本就好。
讓我們來執行這個腳本
npm run start
好的,如果你一樣做到這邊的話!也許你會碰到這個錯誤(也可能沒有,環境嘛...最不好解決的事情)
這邊的意思是,你的專案缺少了套件"http-errors"。這個時候只要"npm i http-errors --save"就可以解決了,不知道這段指令意思的可以重看一下day02關於node的說明。
但通常你會在安裝"http-errors"後重啟仍然因為套件缺失跳出其他缺失套件的錯誤。因為一個專案常常會引用非常多的套件,必須要安裝所有的套件後該專案才會正常運作。而在"package.json"就記錄了該專案需要的套件(通常),只要查看"dependencies"就可以知道了
{
略
"dependencies": {
"cookie-parser": "~1.4.4",
"debug": "~2.6.9",
"ejs": "~2.6.1",
"express": "~4.16.1",
"http-errors": "~1.6.3",
"morgan": "~1.9.1",
"node-sass-middleware": "0.11.0"
}
}
所以當你拿到一個專案後,就必須將依賴的套件安裝完成才能正常運作。這邊套件管理器提供了非常快速的安裝方式就是"npm i"一口氣安裝"package.json"有記錄到的所有套件,記得是有記錄到的才會被安裝。如果在未來在安裝後仍跳出缺失套件...記得寫下來拿去敲對方的頭記得詢問對方為什麼需要該套件,是必要套件就將它利用"npm i XXX --save"記錄到"package.json"內這樣未來其他人才可以簡單的快速使用(也有只在開發環境才需要的套件,這邊先不說明)
來輸入指令吧
npm i
在全部跑完之後,就能正常啟動了。再次執行腳本"start"
npm run start
這邊其實可以省略"run",因為npm執行腳本有看到"start"可以不用加run的例外。所以可以直接"npm start"這樣
好的,沒有意外的話就可以看到以下畫面
這樣就說明你的基本伺服器已經正式啟動了,看看吧(http://localhost:3000/)
有沒有看到Express歡迎你的畫面呢?已經離有模有樣的伺服器越來越接近了!回到VScode的Terminal應該就能看到伺服器發出訊息
這三行可以簡單的說明
GET / 304 19.042ms - - // 在你的網站的根路由,也就是路線的入口的檔案被抓取花了19.042ms
GET /stylesheets/style.css 200 12.186 ms - - // 在你的網站設定公開的public資料夾下的stylesheets抓取了style.css檔案
GET /favicon.ico 404 2.334 ms - 853 // 你的網站沒有該ico檔,該檔案是一個網站的標籤列顯示的圖片。通常網頁沒設定的話會從根目錄找這樣
你可能會對 304 200 404 這熟悉又陌生的數字感到好奇,但既然覺得熟悉基本上八九不離十可以推測出來了。對於不明白的朋友,這邊的404就是page 404 not found,這樣其他數字應該也不用解釋了吧(笑
簡單明瞭的說明伺服器的連線狀況,也是express基本伺服器幫你快速設定好的功能喔!
好的,讓我們在terminal按下 ctrl + c 關掉伺服器開始進入流程講解吧。
再次回到"package.json"看看這個伺服器的進入點在哪個檔案,由剛剛的指令"start"可以得知該網站是藉由"node ./bin/www"開始的。所以該伺服器的進入點就是bin下的www這隻檔案,很簡單吧!
在上一篇我們有看過www的部分程式了,這邊就要針對www說明。(這邊有非常多註解其實也不太需要說明就是了,另外本人非常喜歡用推測和理解的方式來讓自己不去看文件,在大部分的情況好用...但有毛的時候記得還是去看文件)
#!/usr/bin/env node
/**
* Module dependencies.
*/
var app = require('../app'); // 引入 app.js 檔案
var debug = require('debug')('ithome-30day:server');
var http = require('http');
// 引入套件,這兩個套件可以直接到npm網站查詢文件
/**
* 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);
// 這邊已經說明過了,而從這段程式能推測出http這個套件擁有建立伺服器的功能
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
// 這邊應該不難理解,用來標準化port號
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
// 這邊是在上方被http的error所指令觸發的事件,註解的應該很清楚了。
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}
// 這邊嘛,老實說可以推測成當伺服器成功的時候去利用"debug"做事...不去看"debug"看文件是不能知道他到底做什麼的。可以去看,但依本系列走向現在並不需要理解他
好的,現在該檔案唯一需要理解的只有他引入了"app.js"這隻檔案了!而在上一篇我們詳細解說了app.js,現在你對這個伺服器的全貌開始有概念了嘛?
接下來讓我們從輸入網址後開啟網頁後的方式,說明網頁是怎麼被吐出來的!(這也是個人喜歡的debug技巧,一路走進伺服器看東西是怎麼出來的對理解網站架構很有幫助)
好的,讓我們再次開啟伺服器並進入網址(http://localhost:3000/)
這次從路線來看程式碼流程,看一下網址可以得知是由網站的根路線進入的。在上一篇中我們知道了網站的路線是在app.js統一管理的(通常路由都會由某個檔案統一管理,但是...如果沒有那就恭喜你了...)
所以看程式碼
app.use('/', indexRouter);
得知根路線是使用"indexRouter"這個變數也就是藍圖所製作的,再看一下程式碼
var indexRouter = require('./routes/index');
可以得知"indexRouter"是引入"('./routes/index')"這隻檔案,前往檔案
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
到路由了!當根路線被觸發的時候,伺服器就把在views的index.ejs帶著變數"title"後吐出去!這邊伺服器內的程式就差不多了,當然這只是因為這是簡單的伺服器...未來還有中間層等等事情要處理。但在這一系列,並不深入學習那方面的知識(那方面太難,且不能靠推測...我也不太會...)
到這邊,就是去最後一隻檔案"views/index.ejs"了
不知道各位有沒有學過樣本語言?沒有的話...幸好ejs跟html非常像,把它理解成伺服器會吐給遊覽器端處理過的html檔就可以了,是一種SSR。SSR這邊不做討論有興趣的請google(自學良伴)
<!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>
這邊你唯一看不懂的應該只有"<%= title %>",但是看到這邊你應該有能力推測出來。title是什麼了,如果可以那今天就算是結束了。不行的請接著往下看
簡單說明樣板語言的功能是讓伺服器端在吐出頁面的時候可以動態更新網頁內容的方法,<%=%>內的東西在伺服器端會被處理成html的文字。詳細的可以查看EJS官方網站,當然後續也會講到。
所以這邊是將"title"這個變數的value刷到畫面上,而title這個變數可以在router內看到。
這邊只要實際使用一次之後,對於"<%= title %>"就能理解了。讓我們回到routes下的index.js這隻檔案並稍作更改
router.get('/', function(req, res, next) {
res.render('index', { title: '第一個網站' });
});
然後關掉伺服器後重新用"npm run start"開啟再回到(http://localhost:3000/)看看
為什麼要重新開啟?因為一旦node啟動了該伺服器,所有檔案就已經被程序緩存了。你對檔案的所有操控並不影響這個程序,這邊可以不理解沒關係。
現在應該能看出畫面的變化,這樣對於"<%= title %>"是幹嘛的就能推測了吧!
好的,對於流程和程式能夠大致推測出來了!明天就來運用看看!
0 留言