TS + React + Antd + Koa2 + MongoDB 打造 TodoList 全棧應用

B2D1發表於2019-02-20

前言

JS 作為一門弱型別語言,時常受到 Java,C# 等老牌程式語言的“歧視”,並且誕生之初,只能執行在瀏覽器端上,被戲稱為“玩具語言”。早前 Node.JS 的出現,讓 JS 向後端領域進軍。如今 TypeScript 的出現則讓它煥發了新的生命,從本質上向這個語言新增了可選的靜態型別和基於類的物件導向程式設計,作為JavaScript的一個超集,與當下最流行的前端框架 React 有著天生的契合度。目前可謂大紅大紫。

2019年,我們有必要全面掌握 TypeScript,並應用到實踐中。

本專案以功能清晰的 TodoList 為切入點,結合 React全家桶、Antd 打造程式碼健壯性強,使用者介面簡潔的前端應用,以 Koa2 + MongoDB 為核心構建可維護性高的後端服務。

是一個全棧應用 TodoList 的最佳實踐。大家可以自己動手試一試。

專案預覽

話不多上,我們先看實際效果。 線上訪問地址,大家可以自行註冊體驗。(自己搭建的阿里雲伺服器,已設定 HTTPS 安全,不過由於是學生版,首屏載入速度較慢,請耐心等待,後續會進行優化)

TS + React + Antd + Koa2 + MongoDB 打造 TodoList 全棧應用

技術棧

  • 前端
    • TypeScript(使 JS 成為強型別語言)
    • React(當下最流行的前端框架)
    • Axios(處理 HTTP 請求)
    • Ant-Design(UI 框架)
    • React-Router(處理頁面路由)
    • Redux(資料狀態管理)
    • Redux-Saga(處理非同步 Action)
  • 後端
    • Koa2(基於 Node.js 平臺的下一代 web 開發框架)
    • MongoDB(非關係型資料庫)

功能點

  • RESTful 風格介面設計
  • HTTP請求封裝,錯誤處理
  • 元件化,程式碼分層
  • 使用者登入註冊
  • Todo 的關鍵詞查詢
  • Todo 內容修改
  • Todo 狀態更改
  • Todo 記錄刪除

實踐分析

TypeScript

TS最基礎的賦予我們給JS變數設定型別的能力,還帶來了介面,泛型,列舉,類,裝飾器,名稱空間等全新內容。

let a: number = 1; // int a = 1;
let b: string = 'Hello'; // string b = 'Hello'
let arr: number[] = [1, 2, 3]; //  int arr[] = {1,2,3};
複製程式碼

TS + React + Antd + Koa2 + MongoDB 打造 TodoList 全棧應用
TS可以約束我們的傳參,變數型別,介面型別,從而避免在開發時產生不必要的錯誤。小夥伴可以觀看官方文件

/interface/UserState.ts為例,匯出了一個介面

 export interface UserState {
    user_id?: string; // ?代表可選
    username?: string;
    err_msg?: string;
}
複製程式碼

user繼承UserState介面,所以 會有屬性推導,而在JS中,我們需要自己輸入user.err_msg,繁瑣且易出錯。

TS + React + Antd + Koa2 + MongoDB 打造 TodoList 全棧應用
在React,我們主要通過無狀態元件function,和有狀態元件class來構建應用,包括props的傳遞,函式的傳參,類的繼承等都非常需要型別約定,可以說TS和React“天生一對”,使用他們,我們的程式碼健壯性提高了一個檔次。

redux狀態管理

狀態管理是目前構建單頁應用中不可或缺的一環,簡單應用使用元件內 State 方便快捷,但隨著應用複雜度上升,會發現資料散落在不同的元件,元件通訊會變得異常複雜,這時候就需要redux來管理全域性狀態。它遵循三個原則:

  • 元件資料來源於 Store,單向流動
  • 只能通過觸發 action 來改變 State,通過定義actionTypes,做到全域性惟一
  • Reducer 是純函式

由於Reducer只能是純函式(簡單來說,一個函式的返回結果只依賴於它的引數,並且在執行過程裡面沒有副作用,我們就把這個函式叫做純函式。),而當處於請求(Fetch)場景時,Action需要發起非同步請求,包含了副作用,所以使用藉助Redux-Saga來處理非同步Action,處理後返回成功的同步Action並觸發,此時是一個純函式,最終改變store資料。

以FETCH_TODO(獲取Todo資源)為例,資料流向如下圖所示:

TS + React + Antd + Koa2 + MongoDB 打造 TodoList 全棧應用

介面設計

由於採用的是前後端分離開發,我們通過約定介面來進行資料交換,而當下最流行的便是 RESTful 風格介面,它有以下幾個要點:

  • 根據請求目的,設定對應 HTTP Method,如 GET 對應讀取資源(Read),PUT 對應更新資源(Update),POST 對應建立資源(Created),DELETE 代表刪除資源(Delete),對應資料庫 CRUD 操作
  • 動詞表示請求方式,名詞表示資料來源,一般採用複數形式 如 GET/users/2 獲取 id 為2的使用者
  • 返回相應的HTTP狀態碼,常見的有200 OK請求成功,201 CREATED建立成功,202 ACCEPTED更新成功,204 NO CONTENT刪除成功,401 UNAUTHORIZED未授權,403 FORBIDDEN禁止訪問,404 NOT FOUND資源不存在,500 INTERNAL SERVER ERROR伺服器端內部錯誤

以Todo的路由為例,我們可以設計出以下介面

TS + React + Antd + Koa2 + MongoDB 打造 TodoList 全棧應用

程式碼分層

首先看後端檔案目錄:

TS + React + Antd + Koa2 + MongoDB 打造 TodoList 全棧應用
我們關注於db service routes這三個資料夾。

  • db建立資料模型(Model),相當於MySQL的建表環節
  • service呼叫資料模型處理資料庫的業務邏輯,對資料庫進行CURD,返回加工後的資料
  • routes呼叫service中的方法處理路由請求,設定請求響應

學過Java的小夥伴都知道,一個介面要通過 Domain層,DAO層,Service層,才會進入 Controller層呼叫,我們的專案類似於這種思想,更好的邏輯分層不僅能提高專案的維護性,還能降低耦合度,這在大型專案中尤為重要。

錯誤處理

service/user為例,我們定義了userService類,用於處理user的業務邏輯,其中的addUser為註冊使用者時呼叫的方法。

TS + React + Antd + Koa2 + MongoDB 打造 TodoList 全棧應用
由於我們設定了usr欄位唯一,所以當使用者註冊時,輸入已經註冊過的使用者名稱,就會丟擲異常。這時候,我們要catch,並向呼叫此方法的路由丟擲我們自定義的異常'save failed'隨後路由層會捕獲錯誤,返回使用者名稱已存在的HTTP響應。這就是一個較為典型的的錯誤處理過程。

TS + React + Antd + Koa2 + MongoDB 打造 TodoList 全棧應用

統一響應

關於API呼叫的返回結果,為了格式化響應體,我們在/utils/response.js編寫處理響應的通用函式。

返回一組訊息,指明呼叫是否成功。這類訊息通常具有共同的訊息體樣式。 通用返回格式是由msgerror_codedatarequest四個引數組成的JSON響應體:

TS + React + Antd + Koa2 + MongoDB 打造 TodoList 全棧應用
當我們請求GET https://b2d1.top:5000/api/todos/5c6a5b2f6622ab4bf6fed958/all時,獲取指定使用者的所有Todo,返回以下響應體
TS + React + Antd + Koa2 + MongoDB 打造 TodoList 全棧應用
這不僅僅加強了規範性,而且利於前端接收請求,做出更好的判斷或排錯

寫在結尾

本專案還有許多技術要點、開發技巧,由於篇幅有限,作者就不一一提及了,想繼續深入瞭解的小夥伴,請在評論區留言,我會考慮再寫第二篇繼續剖析。

希望小夥伴們參照原始碼,親自動手做一個屬於自己的TodoList,共勉!

之後一段時間,可能會推出如何搭建自己的伺服器,並設定HTTPS安全教程,還有小程式的全棧開發流程,請期待吧!

TodoList:GitHub地址

相關文章