[ 筆記 ] Express 01 - 基本架構 MVC


Posted by krebikshaw on 2020-10-02

Express 安裝

安裝只要兩個步驟:

1. $ npm init
2. $ npm install express --save

這樣就安裝完成了

建立 app

開始使用 Express,首先要先建立一個 js 檔,引入 express 到你的檔案當中

const express = require('express');

引入的 express 會是一個 function,當我們去執行這個 function,就可以建立一個 app

const express = require('express');
const app = express();   //  執行這個 function,就可以建立一個 app
const app2 = express();   //  執行兩次 function,可以建立兩個 app

指定 port

我們可以指定一個 port 給這個 app,並在最後加上一個監聽器來監聽這個 port

const express = require('express');
const app = express();
const port = 3000;   //  指定一個 port 給這個 app


app.listen(port, () => {    //  加上一個監聽器來監聽這個 port
  console.log(`Example app listening at http://localhost:${port}`)
})

處理 request / response

在 Express 要設定 處理 request / response 非常簡單

以 GET method 為例:

  • 使用 get() 函式,可以讓我們傳入兩個參數:
    • 第一個參數:URL 的目錄
    • 第二個參數:call back function
app.get('/', (req, res) => {
  res.send('Hello World!');  // 設定 response
})

完整的檔案內容:

const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => {
  res.send('Hello World!');
})

app.get('/hello', (req, res) => {
  res.send('Hello man');
})

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`)
})

在 CLI 上面輸入 node <檔名>,就可以啟動這個 Server 了

  • 當網址輸入 localhost:3000 時,網頁上顯示 Hello World!
  • 當網址輸入 localhost:3000/hello 時,網頁上顯示 Hello man

如果要接收 URL 上面不確定的資料,比如說 id,我們可以這樣寫

app.get('/:id', (req, res) => {   // 用 : 來表示這是不確定的參數
  const id = req.params.id        // 用 req.params 來取得上面設定的參數
})

Express 基本架構 MVC

MVC 架構:

  • Model:負責管理資料
  • View:負責管理畫面的顯示
  • Controller:Model 跟 View 之間的協調者

當 Controller 收到 request 以後,會先到 Model 拿資料,Controller 拿到資料之後會將資料交給指定的 View,等到 View 將資料與畫面組合起來之後會拿回來給 Controller,最後再由 Controller 回覆 response

建立 View

在開始建立 View 之前,要先安裝 Template Emgine(模板引擎),這邊選用 EJS 這個模板引擎

npm install ejs

安裝完成後,要在檔案裡面設定參數

app.set('view engine', 'ejs');

預設的目錄會是 views,所以要在專案資料夾之中,新建一個資料夾叫做 views,將所有的 .ejs 檔放在這個資料夾底下

假設我們在 views 底下新建一個檔案叫做 hello.ejs
那我們要叫 express 去 render hello.ejs 這個檔案時,可以使用 render() 這個函式

render() 有兩個參數:

  1. 要 render 的檔案名稱 (副檔名 .ejs 可以審略)
  2. 要帶入的資料(用物件包起來)
app.get('/hello', (req, res) => {   //  接收 URL 為 ..../hello
  res.render('hello');  // 叫 express 去 render views 底下的 hello.ejs 這個檔案
})

如果要將資料帶入 todo.ejs 可以這樣寫:

const express = require('express');
const app = express();
const port = 3000;

app.set('view engine', 'ejs');

let todos = ['first todo', 'second todo', 'third todo'];

app.get('/todos', (req, res) => {
  res.render('todos', {
    todos: todos
  })
})

app.get('/todos/:id', (req, res) => {
  const id = req.params.id
  res.render('todo', {
    todo: todos[id]
  })
})

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`)
})

在 .ejs 的檔案當中,要使用 JavaScript 的程式碼,要使用 <% %> 這個標籤來夾住,如果是要印出資料,可以使用 <%= %> 放要輸出的變數

//  接收一個 todos = ['first todo', 'second todo', 'third todo']  的陣列

<h1>Todo</h1>
<ul>
<% for (let i = 0; i < todos.length; i++) { %>
  <li><%= todos[i] %></li>
<% } %>
</ul>

建立 Model

先在專案資料夾底下,新建一個 models 資料夾,將所有跟 model 相關的檔案都放在這個資料夾底下

model 是負責管理 data 的,所以假設剛剛的 todos 陣列要放在 model 底下管理,可以把它寫成下面這樣:

const todos = [
  'first todo', 'second todo', 'third todo'
]

const todoModel = {
  getAll: () => {
    return todos
  },

  get: id => {
    return todos[id]
  }
}

module.exports = todoModel

建立 Controller

先在專案資料夾底下,新建一個 controllers 資料夾,將所有跟 controller 相關的檔案都放在這個資料夾底下

將 Model 跟 View 之間的協調,交給 controller 來做

const todoModel = require('../models/todo');  // 引入 model

const todoController = {
  getAll: (req, res) => {
    const todos = todoModel.getAll()   // 把 todos 用 getAll 拿出來
    res.render('todos', {
      todos
    })
  },

  get: (req, res) => {
    const id = req.params.id
    const todo = todoModel.get(id)
    res.render('todo', {
      todo
    })
  }
}

module.exports = todoController;

當我們將 Model 跟 View 之間的協調,交給 controller 來做之後,就可以將一開始 app Server 的檔案改得更加簡潔,讓這份檔案成為專門處理路由的檔案

const express = require('express');
const app = express();
const port = 3000;

const todoController = require('../controllers/todo');

app.set('view engine', 'ejs');

app.get('/todos', todoController.getAll)
app.get('/todos/:id', todoController.get)

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`)
})

這樣就做完基本的 MVC 架構了,下面我們要來將原本放在 model 的資料,換成放在「資料庫」,並實作 node.js 跟 mySQL 的溝通
(注意是 node.js 跟 mySQL 的溝通,不是 express 跟 mySQL 的溝通,因為不用 express,node.js 本身就可以跟 mySQL 的溝通)

node.js 跟 mySQL 溝通

第一步:安裝套件

npm install mysql

接下來建立一個 db.js 處理 node.js 跟 mySQL 的溝通

var mysql      = require('mysql');
var connection = mysql.createConnection({
  host     : 'localhost',
  user     : 'me',
  password : 'secret',
  database : 'my_db'
});

module.exports = connection;

回到 app Server 的檔案,將 db.js 引入,並且在 Server 跑起來之後,連線到 mySQL

const express = require('express');
const db = require('./db')  // 將 db.js 引入
const app = express();
const port = 3000;

const todoController = require('../controllers/todo');

app.set('view engine', 'ejs');

app.get('/todos', todoController.getAll)
app.get('/todos/:id', todoController.get)

app.listen(port, () => {
  db.connect()   //  在 Server 跑起來之後,連線到 mySQL
  console.log(`Example app listening at http://localhost:${port}`)
})

再來要來改我們的 model,將 db.js 引入,並且把回傳資料的部分用 call back function 來改寫

使用 connection.query() 帶入的參數為:

  1. 第一個參數:sql 指令 (參數用 ? 代替)
  2. 第二個參數:一個陣列(裡面放入要取代 ? 的值)
  3. 第三個參數:call back function

在此範例中因為將 connection 引入後賦值到 db,所以上面寫的是 db.query()

const db = require('../db')

const todoModel = {
  getAll: (cb) => {
    db.query(
      'SELECT * FROM todos',   
      (err, results) => {
        if (err) return cb(err);
        cb(null, results);  // 因為 cb 的第一個參數放 error,所以如果沒有錯誤第一個參數要傳 null
      }
    );
  },

  get: id => {
    db.query(
      'SELECT * FROM todos WHERE id = ?', [id], 
      (err, results) => {
        if (err) return cb(err);
        cb(null, results);
      }
    );
  }
}

module.exports = todoModel

因為從原本的同步變成非同步了,所以 controller 也要做處理

const todoModel = require('../models/todo');  // 引入 model

const todoController = {
  getAll: (req, res) => {
    todoModel.getAll((err, results) => {
      if (err) return console.log(err);
      res.render('todos', {
        todos: results
      })
    })  
  },

  get: (req, res) => {
    const id = req.params.id
    todoModel.get(id, (err, results) => {
      if (err) return console.log(err);
      res.render('todo', {
        todo: results[0]   // 因為就算只有一個結果 results 一樣會是一個陣列,所以要用 [0] 來取值
      })
    })  
  }
}

module.exports = todoController;

如此一來,我們就完成了基本的 MVC 架構(mySQL 版),接下來會在下篇文章提到更多 Express 的重要概念。


#Express #MVC







Related Posts

package.json & 它的屬性

package.json & 它的屬性

MTR04_0822

MTR04_0822

React 入門 6 - Hooks: useEffect

React 入門 6 - Hooks: useEffect


Comments