MVC(Model-View-Controller)架構實作教學,實作簡易的 todo list API。
在 MVC 架構中,request 的處理流程大致如下:
1 被發出的 request 首先會由 Controller 接收和處理。Controller 負責接收來自用戶端的請求,根據請求的類型和內容執行相對應的操作。
2 接著,Controller 通常會與 Model 進行互動,從 Model 中取得相應的資料。Model 主要負責管理和操作應用程式的資料,它提供了資料的存取、更新、刪除等功能。
3 獲取資料後,Controller 會將這些資料傳遞給 View。View 是負責呈現使用者介面的部分,它會接收從 Controller 來的資料,使用特定的模板(template)或視圖檔案來呈現最終的內容。
4 最後,Controller 會結合從 Model 取得的資料與 View 提供的 template,形成最終的 response。這個 response 包含了經過資料處理後的內容,並作為回應返回給用戶端。這樣完成了整個 request 的處理流程。
這種分層和分工的結構有助於將應用程式的不同部分解耦,使其更易於維護和擴展。 Controller 處理用戶端的請求和路由,Model 管理資料,而 View 負責呈現最終的內容,各司其職,協同工作。
Model:用來管理 todos 的資料
Controller:控制器
express 目錄的 index.js:提供路由
views 目錄的 todo.ejs 和 todos.ejs:提供模版
models 目錄的 todo.js:提供資料
controllers 目錄的 todo.js:結合 model 和 view,根據路由回傳 Response
以下是EJS的基本語法:
<% JavaScript 程式碼 %>
<%- %> 會經過解析然後印出來,用於引入 HTML 內容
<%= %> 會直接印出原始碼,用於輸出資料,避免被解析成語法,可視為一種 XSS 防禦
ex_server.js
const express = require('express');
const app = express();
const port = 5001;
const path = require('path');
// 引入 controller
const todoController = require('./controllers/todo')
app.use(express.static(path.join(__dirname, '/views/static'))) //設定絕對路徑
//app.use(express.static('views')) //標準 在這個目錄之下的所有目錄都包含
// 導入Static檔案,Static檔案包括如:css檔、js檔、img檔。所在預設資料夾名稱為:public。然後在ejs檔中加入css連結。
app.set('view engine', 'ejs')
app.set('views', path.join(__dirname, '/views')) //設定檔案所在的目錄
// 可直接使用 controller 的方法拿取資料和進行 render
app.get('/todos', todoController.getAll)
app.get('/todos/:id',todoController.get)
app.get('/',todoController.index)
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
/*
const express = require('express');
const app = express();
const port = 5001;
// 引入 controller
const todoController = require('./controllers/todo')
// 設定 view engine
app.set('view engine', 'ejs')
app.get('/', (req, res) => {
res.render('index')
})
app.get('/hello', (req, res) => {
// 叫 express 去 render views 底下叫做 hello 的檔案,副檔名可省略
res.render('hello')
})
// 建立 todos data
const todos = [
'first todo', 'second todo', 'third todo'
]
app.get('/todos', (req, res) => {
// 第二個參數可傳入資料
res.render('todos', {
todos: todos // todos: todos 一樣的話可省略寫法
})
})
// 加上 :id 代表不確定的參數
app.get('/todos/:qq', (req, res) => {
// params: 可拿到網址列上指定的參數
const id = req.params.qq
console.log("id : "+id)
const todo = todos[id]
res.render('todo', {
todo
})
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
*/
views/static/vue.js
const main = {
data() {
return {
checked1 : false
}
},
methods: {
go_back () {
this.checked1= true;
window.location.href="/todos";
},
go () {
this.checked1= true;
const txt = document.querySelector('.txt');
window.location.href="/todos/"+txt.value;
}
}
}
Vue.createApp(main).mount('#main')
views/static/ex_index.css
button {
background-color: rgb(107, 44, 19); /* Green */
border: none;
color: white;
padding: 10px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
}
.click_btn {
background-color: rgb(107, 19, 81); /* Green */
border: none;
color: white;
padding: 10px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
}
li {
font-size: 20px;
color: rgb(65, 3, 3);
}
/* 由大到小的 RWD */
.box1 {
height: 80px;
background: lightpink;
}
views/todos.ejs
<html>
<head>
<title>Todos</title>
<link href="/ex_index.css" rel="stylesheet">
<script src="https://unpkg.com/vue@3.3.0/dist/vue.global.js"></script>
</head>
<body>
<div id="main">
<h1>Todos</h1>
<ul>
<% for(let i = 0; i < todos.length; i++) { %>
<li><%= todos[i]%></li> // 加上等於代表後面的東西要輸出
<% } %>
</ul>
<input type="text" class="txt" value="Write numbers~">
<button v-on:click="go()">點我</button>
</div>
<script src="/vue.js"></script>
</body>
</html>
views/todo.ejs
<html>
<head>
<title>Todo</title>
<link href="/ex_index.css" rel="stylesheet">
<script src="https://unpkg.com/vue@3.3.0/dist/vue.global.js"></script>
</head>
<body>
<div id="main">
<h1>Todo</h1>
<h2><%= todo %></h2>
<h2>我是ID:<%= id %></h2>
<button class="click_btn" v-on:click="go_back()">Back</button>
</div>
<script src="/vue.js"></script>
</body>
</html>
views/index.ejs
<html>
<head>
<title>weiwei index yo</title>
<link href="/ex_index.css" rel="stylesheet">
<script src="https://unpkg.com/vue@3.3.0/dist/vue.global.js"></script>
</head>
<body class="box1">
<div class="box1">
<div id="main" >
<h1 id="wei">weiwei index yo</h1>
<h2><%= index2 %></h2>
<button v-on:click="go_back()">To dos</button>
</div>
</div>
<script src="/vue.js"></script>
</body>
</html>
models/todo.js
const todos = [
'first todo', 'second todo', 'third todo'
]
// 建立一個 todoModel 物件,裡面放存取資料的方法(function)
const todoModel = {
getAll: () => {
return todos
},
get: id => {
if (todos[id]){
return todos[id]
}
else{
return "Null唷~"
}
},
index: () => {
return ["Index loveaa","qqq"]
}
}
module.exports = todoModel
controllers/todo.js
// 先從 model 引入 todos 資料
const todoModel = require('../models/todo')
// 建立一個 todoController 物件,透過方法來存取 model 的資料
const todoController = {
// 傳入參數 req, res
getAll: (req, res) => {
const todos = todoModel.getAll()
res.render('todos', { //叫 express 去 render views 底下
todos
})
},
get: (req, res) => {
const id = req.params.id
const todo = todoModel.get(id)
console.log('id : '+id)
res.render('todo', {
id,
todo
})
},
index: (req, res) => {
const index2 = todoModel.index()
res.render('index', {
index2
})
}
}
module.exports = todoController
沒有留言:
張貼留言
喜歡我的文章嗎? 喜歡的話可以留言回應我喔! ^^