Node.js Express-又小又靈活的web應用框架

最後修改日期|Aug 04, 2021

DowYuu言

我來掃一下這裡積了半年的灰塵了,咳咳。

最近空閒時在玩Express,寫個筆記紀錄紀錄。往後有遇到新狀況也會不定時來更新一下…吧(?)

安裝Express

1
npm install express -g

查看Express版本

1
npm express -v

安裝Express生成工具

1
npm install express-generator -g

初始專案

Express預設使用Jade 視圖引擎與純CSS,Jade 視圖引擎語法請參考連結

1
2
3
4
5
6
7
8
express <project-name>

<!-- e.g. -->
express my-project-la

<!-- 後面可加參數(生成器參數見下方網址或express --help) -->
<!-- 像是個人css引擎想用less,就可用參數:-c less -->
express <project-name> -c less

如上面範例中所示,會在目前目錄下建立並產製一個叫my-project-la的專案資料夾,後面可帶參數,參數請見express --help或是查看生成器參數

初始專案後記得進入該專案資料夾中使用npm install語法安裝該專案所有使用的套件。

1
2
cd my-project-la
npm install

啟動

1
npm start

開發階段好幫手:nodemon-讓伺服器在檔案更改時自動重新啟動

在開發階段,前端檔案在檔案更動後還可以立刻看到效果,但後端檔案更動必須要重啟整個伺服器才會刷新。

每當一個小小的修正就要不斷的重啟是很煩人的,裝了nodemon之後它會自動偵測每當有檔案更改時會自動重啟伺服器,舒舒服服!!

1
npm install --save nodemon

安裝完nodemon後,開啟package.json並在scripts區塊加上語法:

1
2
3
4
5
6
7
8
{
  ...
  "scripts": {
    "start": "node ./bin/www",
    "devstart": "nodemon ./bin/www"
  },
  ...
}

這邊示範取名devstart,於是啟動伺服器的語法改為npm run devstart

1
2
3
4
5
<!-- 一般啟動語法(更動檔案必須手動重啟伺服器) -->
npm start

<!-- 使用nodemon啟動語法(更動檔案自動重啟伺服器) -->
npm run devstart

用了nodemon啟動語法會發現,每當有檔案更動,就會有自動重啟的訊息跑出來呦!舒舒服服!!

1
2
3
4
...(更動檔案前)
[nodemon] restarting due to changes...
[nodemon] starting `node ./bin/www`
...(更動檔案後自動重啟伺服器了,灑花!)

引入Router

初始化後的Express專案其實都有基本的範例,像是預設產出的Express專案就有引入indexusers這兩個Router,這可以在app.js中看到:

1
2
3
4
5
6
7
// in app.js

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

這表示當URL連結到/(根目錄)時,會使用indexRouter,當URL連結到/users(e.g. 127.0.0.1/users)時,會使用usersRouter

對應的Router檔案預設是放在專案資料夾中的routes資料夾,若要自己新增Router,就比照原先範例,在app.js中將該routes引入(require可省略副檔名.js):

1
2
3
4
5
6
// in app.js

var newRouter = require('./routes/newJs');
...
// 當url為「/new」時,就會進入「newRouter」(即檔案routes/newJs.js)。
app.use('/new', newRouter);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// in routes/newJs.js

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

// 「router.」可以在後方一直串下去
router.get('/', function(req, res, next) {
  // 當url為「網址/new/」時(e.g. http://127.0.0.1:3000//new/),做某件事
  ...
}).get('/test', function(req, res, next) {
  // 當url為「網址/new/test」時(e.g. http://127.0.0.1:3000//new/test),做某件事
  ...
});

module.exports = router;

render(頁面渲染)

前面初始專案時有提到,Express預設使用Jade 視圖引擎,你可以在views資料夾中看到初始的範例.jade檔案,且在app.js中看到視圖引擎設置,預設還貼心的有註解說明:

1
2
3
4
5
// in app.js

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

因為如此,在Router中使用res.render(<filename>, <params>),就會去views資料夾中找到對應<filename>檔案(不需要加附檔名.jade)解析成HTML,顯示給你看:

1
2
3
4
5
6
// in routes/index.js

router.get('/', function(req, res, next) {
  // 渲染「views/index.jade」並帶入參數「{ title: 'Express' }」
  res.render('index', { title: 'Express' });
});

至於為什麼會連同layout一同渲染,是因為預設views/index.jade中有寫到extends layout,後面也有提到頁面渲染不使用layout要怎麼做。

render(頁面渲染)不使用layout

於該.jade頁面中將extends layoutblock content拿掉即可。

要記得預設的HTML宣告是寫在layout.jade中的,如果不要用記得要加上HTML宣告。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//- 原本的views/index.jade
extends layout

block content
  h1= title
  p Welcome to #{title}

//- 若要拿掉layout,記得放上HTML宣告
doctype html
html
  head
    title= title
    link(rel='stylesheet', href='/stylesheets/style.css')
  body
    h1= title
    p Welcome to #{title}

頁面引入非存放在public中的檔案

預設Express會將專案中的public資料夾路徑引用入根目錄。

1
2
3
4
// in app.js

// 就是這行做了這件事
app.use(express.static(path.join(__dirname, 'public')));

所以在views/layout.jade中,的可見其<link>可直接引用根目錄底下的/stylesheets資料夾,就會自動連結至public資料夾中,不須依照相對路徑在那邊../public,也增加隱密性。

若要引用的檔案並非存放在public資料夾中,也可使用app.use引入設置路徑名稱與指定資料夾,像是要引用裝在node_modules中的程式。

這邊用sweetalert2(這是個美化alert的好用套件)當範例,頁面要引用其jscss時即可使用/swal,程式會自動對應到node_modules/sweetalert2/dist資料夾:

1
2
3
// in app.js

app.use('/swal', express.static(path.join(__dirname, 'node_modules/sweetalert2/dist')))
1
2
3
4
5
// in .jade

// - 這裡的「/swal」其實是對應到「node_modules/sweetalert2/dist」嘿嘿
link(rel="stylesheet", href="/swal/sweetalert2.min.css")
script(src='/swal/sweetalert2.min.js')

Router取得傳入參數

取得參數 使用語法 範例 取得 備註
網址中帶的參數 req.params.<參數> app.get(‘/log/:date’, …) req.params.date  
$.ajax中帶的data req.query. $.ajax中的data:{ date: ‘2021.01.01’ } req.query.date  
form中的輸入格 req.body.<該元件的name> <input type="text" name="account" > req.body.account 使用套件multer
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
28
29
30
31
32
33
34
// in Router

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

router.post('/login', function(req, res) {
  // 取得form中name=account與name=password的元件value
  let account = req.body.account;
  let password = req.body.password;
  ...
}).get('/file/:name', function (req, res) {
  // 取得網址中/file/???中的???值( ???被命名為參數name)
  let fileName = req.params.name; // e.g. 網址為「http://127.0.0.1/file/test.docx」就會取得fileName = test.docx
  ...
}).get('/setLog', function(req, res){
  // 取得前端ajax傳來的data中的key參數

  // 前端$.ajax
  // $.ajax({
  //   url: nowUrl + '/setLog',
  //   type: 'GET',
  //   cache: false,
  //   dataType: 'json',
  //   data: { 'log': '使用者dowyuu登入成功' },
  //   success: function(data) {
  //     console.log(data); // { 'success': true }
  //   },
  //   error: function(){
  //     alert('傳送log至服務端失敗。');
  //   }
  // })
  let log = req.query.log; // log = 使用者dowyuu登入成功
  ...
})

Router參考

ajax 回傳

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// in Router
var express = require('express');
var router = express.Router();

router.get('/getResult', function(req, res, next){
  let data = {
    'result': 'success'
  };
  res.send(data);
});

// in 前端
$.ajax({
  url: '/getResult',
  type: 'get',
  dataType: 'json',
  success: function(data){
    console.log(data); // { 'result': 'success' }
  }
});

在javascript中帶入jade參數

1
2
3
4
// in .jade

script.
    alert('這頁是:#{page}')

參考