[NodeJS Become A Full Stack Developer] — Express、Router 及 EJS 入門淺談

Express

Introduction & 前言

今天會提到關於 NodejsTemplate,如果你在前端有使用過 **Pug(Jade)**,會比較好理解一些,如果沒用過也沒關係,跟著這篇一起練習吧。

NodejsTemplate 有許多種,這邊示範的為 EJS


Summary & 摘要

EJS Template

在前面的文章 [NodeJS Become A Full Stack Developer] — 從0開始 NodeJS 小試身手 我們開啟了全端之路,在入門如何安裝及使用 Nodejs 之後這篇文章就要簡短講解如何快速建立後端環境,並且規劃簡單的架構及使用模板建立多頁面。


老樣子

按照慣例文章開頭還是要講一下什麼是 Express

跟前端一樣,Nodejs 也有三個比較流行的框架,分別為 Express、KoaHapi,但你可能會有點疑問,我安裝了這些框架但他們好像沒有幫我弄好環境?

沒有錯,這些框架和前端那些框架有些異曲同工之妙,這三個框架提供了一些更方便的 API 可以使用,但環境必須要自行建立,如果你不想要手動建立環境的話別擔心,可以考慮研究一下 **Express Generator(註1)**,但這邊推薦新手先自行從 0 開始建立環境才能比較了解為什麼 Express Generator 要幫我把環境這樣放。

Nodejs 框架三劍客

關於三個框架的好壞這邊稍微提一下(不是專業的分析,有誤還請各路大神見諒),詳細可參考 **如何正確選擇Node框架:Express、Koa還是Hapi**:

  1. Express
  • 優點:三個框架內最小且靈活的框架、學習曲線較低、可客制程度高、可擴展性高

  • 缺點:需要大量的手動建置,且框架對於組織需要非常明確。

  1. Koa
  • 優點:由 Express 原班人馬開發,在 koa2 開始使用了 async/await,在非同步控制上,代碼可讀性更高、性能好。

  • 缺點:Koa 的社群相對較小(現在似乎不一定了)、和 Express 的插件不相容。

  1. Hapi
  • 優點:issues 數量少,維護與關注狀態都非常好、對於路由配置更加詳細。

  • 缺點:配置上結構較複雜。

註1:之後會再寫一篇如何使用 Express Generator 快速建立 Nodejs 專案。


起手式一

首先建立一個新專案資料夾,然後進入這個專案資料夾:

1
2
3
4
5
6
7
$ mkdir express-example

$ cd express-example

$ npm init/init -y // 加上 -y 快速建立

$ npm i express -S // 安裝 express

目前的資料夾

到目前為止在專案資料夾應該會出現 node_modules package.json package-lock.json(註2)。

註2:關於 node_modules package.json package-lock.json 前面文章提到過很多次,這邊再稍微講一下,*node_modules 是放各種套件的地方,package.json 則是紀錄你這個專案使用了什麼套件,所以之後把專案上傳可以不用上傳 node_modules 只需要讓下一個人拿到 package.json 即可安裝你之前安裝過的所有套件,最後 package-lock.json 則是紀錄套件使用的版本號。*


起手式二

裝好之後在專案底下建立 app.js 並且輸入以下內容:

1
2
3
4
5
6
7
8
9
10
11
12
// app.js

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

app.get('/', function (request, response) {
response.send('Hello Express!');
});

app.listen(3000, function () {
console.log('Example Nodejs Express listening on port 3000!');
});

之後在終端機輸入 node app.js 就可以看見終端機的訊息:

成功跑在 3000 port

接著在網址上輸入 http://localhost:3000/ 就會出現下面的訊息:

成功啟動

起手式二 - 補充

上面我們可以成功在網頁上顯示 Hello Express! 聰明的你一定想得到是透過 response.send() 這個方法,這邊就要講解關於 requestresponse

request 為 從使用者那邊發送過來的請求,response 為服務器這端發送回去的回應,我們可以透過 request 拿到一些使用者的請求及資訊,比方說 request.routereq.param('key')req.body.xxx 等等。

這邊試著將 app.js 的程式碼改為以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
// app.js

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

app.get('/', function(request, response){
console.log(request);
response.send('Hello Express!');
});

app.listen(3000, function () {
console.log('Example Nodejs Express listening on port 3000!');
});

你會看見 request 有許多資訊可以拿取:

許多資訊

後面會再提到如何顯示 HTML 在頁面上。


規劃及準備

前面了解到基本路由的概念,這時候我們就要開始規劃怎麼更改我們的專案架構,可以想像我們之後會有很多路由,我們可以把路由另外放一個資料夾,然後再透過 app.js 引入進來。

先在專案建立資料夾 routes(放路徑) 然後在資料夾內在建立 index.js

這時候你的架構會長這樣:

目前的架構

接著打開 /routes/index.js 之後把內容改為以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// /routes/index.js

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

router.get('/', function(request, response){
response.send('Hello Express!');
});

router.get('/detail', function(request, response){
response.send('This is detail page!');
});

module.exports = router;

然後把 app.js 改寫為以下:

1
2
3
4
5
6
7
8
9
10
11
12
// app.js

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

var indexRouter = require('./routes/index'); // 引入 index 的所有路由

app.use('/', indexRouter);

app.listen(3000, function () {
console.log('Example Nodejs Express listening on port 3000!');
});

這樣我們就把 index 的路由都統一放到 /routes/index.js 去管理了,如果還不太明白,我們再多建立一個 router,一樣在 /routes 底下建立一個 user.js,然後內容改為以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// /routes/user.js

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

router.get('/', function(request, response){
response.send('Hello User!');
});

router.get('/user-edit', function(request, response){
response.send('This is user-edit page!');
});

module.exports = router;

接著一樣在 app.js 引入路由:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// app.js

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

var indexRouter = require('./routes/index'); // 引入 index 的所有路由
var userRouter = require('./routes/user'); // 引入 user 的所有路由

app.use('/', indexRouter);
app.use('/user', userRouter);

app.listen(3000, function () {
console.log('Example Nodejs Express listening on port 3000!');
});

緊接著在終端機輸入 node app.js 啟動網頁,分別輸入網址 http://localhost:3000/http://localhost:3000/detailhttp://localhost:3000/userhttp://localhost:3000/user/user-edit

架構概念

懶人包:建立 routes 資料夾,每次要新增一個路由分類時,先到 routes 資料夾底下新增,例:usermemberlogin 等等,然後再到 app.js 分別引入,例:var indexRouter = require('./routes/index');var userRouter = require('./routes/user'); 然後在使用路由 app.use('/', indexRouter);app.use('/user', userRouter);


EJS

快速導覽

學玩怎麼使用 router 之後這邊就要提到,後端寫好一支 API 可能是吐回一段文字,可能是直接渲染一個畫面,也可能是一段 JSON,前面我們使用的 response.send('xxx'); 這個意思就是吐一段文字回去給瀏覽器,

這時候你肯定會想我寫了一個 HTML 想渲染給使用者看,我不就要寫 response.send('<h1>我是標題</h1>');,其實不用這麼麻煩,這邊要介紹到的 EJS 就是方便你渲染及動態帶入參數。

另外 Template 的好處應該大家也都知道了,如果我今天有十支檔案都有 header,假如 header 有個地方要修改,不用十支都修改,只需要修改共用的 Template 即可。

快速起手

在開始動作之前我們先在終端機輸入 npm i ejs-locals -S(註3) 安裝 EJS

接著在專案資料夾底下新增 /views/index.ejs/views/user.ejs 然後把 app.js 改為下列內容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// app.js

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

var indexRouter = require('./routes/index'); // 引入 index 的所有路由
var userRouter = require('./routes/user'); // 引入 user 的所有路由

app.use('/', indexRouter);
app.use('/user', userRouter);

app.listen(3000, function () {
console.log('Example Nodejs Express listening on port 3000!');
});

然後把 /views/index.ejs 的內容改為下列的內容:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- /views/index.ejs -->

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
Hello Express By index.ejs.
</body>
</html>

然後再回到 /routes/index.js 改為下列內容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// /routes/index.ejs

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

router.get('/', function(request, response){
response.render('index');
});

router.get('/detail', function(request, response){
response.send('This is detail page!');
});

module.exports = router;

在上面我們把原本的 response.send() 改為 response.render('index') 意思就是把 views 底下的 index.ejs 渲染回去給瀏覽器,那為什麼 nodejs 會知道我們要選染哪一隻檔案回去呢?因為上面我們使用了下列的程式碼:

1
2
3
4
5
6
// app.js

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

上面的 path 就是 nodejs 原始的 API,而 __dirname 我們在前面的 [NodeJS Become A Full Stack Developer] — 從0開始 NodeJS 小試身手 有提到,這邊大概的意思就是當我使用 response.render('index') 的時候,會自動幫我抓路徑 response.render('index') => response.render('目前路徑/views/index'),最後一行則是把目前的 view 引擎設定為 ejs

現在停止終端機的指令,重新輸入 node app.js 打開瀏覽器輸入 http://localhost:3000/ 吧,會出現下面的畫面就代表成功了:

成功渲染

註3:這邊建議安裝 ejs-locals 而非 ejs 因為前者多了更多的 API 可以使用

帶入參數

但是通常我們後端在渲染畫面的時候,必定會先去資料庫拿資料,在渲染畫面,而參數帶入的方法也很簡單,只需要在渲染的時候加進去即可,接著再去 ejs 顯示出來,找到 /routes/user.js 這隻檔案,並且改寫為下列內容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// /routes/user.js

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

router.get('/', function(request, response){
response.render('user',{
'username': '大雄',
'age': 18
});
});

router.get('/user-edit', function(request, response){
// 假設到資料庫拿到資料了
var user_arr = [
{ username: '胖虎', age: 20 },
{ username: '小夫', age: 22 },
{ username: '靜香', age: 16 },
{ username: '多拉A夢', age: 100 },
]
response.render('user',{
'user_arr': user_arr,
'show': true
});
});

module.exports = router;

接著也把 /views/user.ejs 改寫一下吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- /views/user.ejs -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
Hello Express By index.ejs.

<%= username %>
</body>
</html>

聰明的你會立馬發現 <%= username %> 這個新寫法,其實這就是模板的原理,在要替換的地方使用變數,再透過更新資料去觸發畫面更動,這也是 Vue 的原理,像六角學院校長說的,並非魔術!

接下來重新啟動終端機輸入 node app.js 一樣打開瀏覽器輸入 http://localhost:3000/user 你會發現神奇的大雄被帶進來了,如下圖:

user頁面成功渲染

if-else

另外 ejs 可以跟 pug 一樣寫 js 在上面,所以我們也可以寫判斷,把 /views/user.ejs 改寫為下列內容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- /views/user.ejs -->

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
Hello Express By index.ejs.

<% if(show){ %>
<% for(var i = 0; i < user_arr.length; i++) { %>
<p>username: <%= user_arr[i].username %></p>
<p>age: <%= user_arr[i].age %></p>
<% } %>
<% } %>
</body>
</html>

接著一樣重新啟動終端西,執行 node app.js 之後打開瀏覽器輸入 http://localhost:3000/user/user-edit 會出現下面的內容:

user頁面成功渲染

我們可以在參數使用 Boolean 然後在渲染時依靠 true/false 去決定是否顯示內容,也可以用迴圈的方式把資料給渲染出來,這和我們使用 JS 來渲染畫面到 HTML 是大同小異的。

關於更多的 ejs 用法可以參考 ejs docs


範例

這邊附上簡單的 express-example,裡面包含簡單的 routes 拆分 及 ejs 的模板使用。

另外有補上簡單的靜態資源引入,用法很簡單,只需要在 app.js 加上一行 app.use(express.static('public')); 即可,接著在需要引入的地方只需要直接把路由從 public 接著打下去即可,因為預設會去找 public 這個資料夾,例:<img src="/images/soGood.jpeg">

GitHub: 傳送門


Conclusion & 結論

這篇文章原本想簡短介紹 EJS,但因為提到了 Router 不得不在最後把文章標題從 Express 及 EJS 入門淺談 改為 Express、Router 及 EJS 入門淺談,原本想長話短說,不知不覺又越寫越長,不過到這邊也大致對 nodejs 的環境有些許了解了,之後的篇章將會繼續講解如何使用 nodejsCookieSession


參考網站