Chrome Pointer

2023年12月6日 星期三

使用vue ,express, ejs 結合node.js,實作一個MVC架構簡易網頁

 參考文章

💓Vue教學1-透過vue.js用少少的語法,實現厲害特效

💓為何會出現Refused to apply style from because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled


 參考外部文章

💓Web開發學習筆記20 — Express、EJS

💓[week 17] 後端中階 - 使用 Node.js + Express 框架建立一個靜態網頁

💓前端工程師邁向後端之路 1 – server 是什麼?


- Root Directory
  ├── controllers
  │   └── todo.js
  ├── models
  │   └── todo.js
  ├── views
  │   ├── index.ejs
  │   ├── todo.ejs
  │   ├── todos.ejs
  │   └── static
  │       ├── ex_index.css
  │       └── vue.js
  └── ex_server.js

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



沒有留言:

張貼留言

喜歡我的文章嗎? 喜歡的話可以留言回應我喔! ^^