- 使用 Vite 建置 React 搭配 Typescript 開發
- NodeJS 版本
20.10.0
(建議18.16.0
以上) - 本機運行
npm run build
- react
- react-router
- bootstrap
- axios
- sass
- assets:靜態檔案,如 JPG、SVG
- components:自定義元件,如 Button、Header、Footer
- hooks:自定義 Hook,可以將常用流程抽出來共用
- pages:頁面,如 Index、Login
- styles:
- CSS 樣式,支援 SCSS,
_variables.scss
、index.scss
參考bootstrap
,可自行調整
- services:呼叫 API 功能,使用
axios
- store:全局狀態管理,看是要用
Redux
或Zustand
或Context
- utils:常用功能,目前還沒想到(X
- main.tsx:React 的進入點,
React.StrictMode
是否要拿掉? - routers.tsx:
- router 表
- 目前使用
HashRouter
ex:XXXXXX/#/index - 若部署環境可以設定的話再改成
BrowserRouter
ex:XXXXX/index - 有實做
PrivateRoute
功能,看看好不好用(?- AuthRoute:登入後才可進入,若未登入則會導回登入頁
- NonAuthRoute:登入前才可進入,若已登入會導向首頁
- index.scss:引入所有 Bootstrap 樣式,再針對客製化樣式進行調整
- _variables.scss:針對 Figma 重新定義樣式的變數
- _button.scss:簡化 Bootstrap button 樣式,只保留有需要的狀態,目前還有點 bug
- _forms.scss:與原版相同,多新增提示訊息粗體的樣式
- _hero-buttons.scss:自定義元件,所有自定義元件皆需加入 index.scss 中
- 顏色:以下是有定義的顏色變數,可以直接使用
- primary-120 #7B6651
- primary-100 #BF9D7D
- primary-80 #D0B79F
- primary-60 #E1D1C2
- primary-40 #F1EAE4
- primary-20 #FAF7F5
- success-120 #299F65
- success-100 #52DD7E
- success-20 #BCFBBD
- success-10 #E8FEE7
- info-120 #1D66AC
- info-100 #3BADEF
- info-20 #B1EFFD
- info-10 #E6FBFE
- danger-120 #C22538
- danger-100 #DA3E51
- danger-20 #F5CCD1
- danger-10 #FDECEF
- neutral-100 #000000
- neutral-80 #4B4B4B
- neutral-60 #909090
- neutral-40 #ECECEC
- neutral-10 #F9F9F9
- neutral-0 #FFFFFF
- background #140F0A
- balck neutral-100
- white neutral-0
- primary primary-100
- success success-100
- info info-100
- danger danger-100
此專案使用 axios
呼叫 API,並封裝 get
、post
、put
、delete
方法,範例如下
const get = async <TResponse>(url: string, token: string | undefined = undefined): Promise<TResponse> => {
const response = await axios.get<TResponse>(url, {
headers: {
'Content-Type': 'application/json',
'authorization': token,
}
})
return response.data
}
const post = async <TResponse>(url: string, request: any, token: string | undefined = undefined): Promise<TResponse> => {
const response = await axios.post<TResponse>(url, request, {
headers: {
'Content-Type': 'application/json',
'authorization': token,
}
})
return response.data
}
get function 有兩個參數
- API Url
- token (若 API 需要 token 驗證時才需此參數)
post function 有三個參數
- API Url
- request data
- token (若 API 需要 token 驗證時才需此參數)
皆定義 Content-Type
為 application/json
,回傳型態為 TResponse
,泛型可依據不同 API 定義各自回傳型態。
若 API 失敗(StatusCode = 4XX
or 5XX
),統一拋出例外,格式如下:
interface ApiError {
status: string;
message: string;
}
將呼叫 API 方法寫在 ./src/services
中,檔案名稱習慣上以 Service
結尾如:
UserService.ts
。
function 傳入傳出型態可定義 interface 或 type,回傳型態為 Promise<T>
interface LoginRequest {}
interface LoginResponse {}
const login = async (request: LoginRequest): Promise<LoginResponse> => {
const repsonse = await post<LoginResponse>(`${config.baseURL}/api/v1/user/login`, request)
return repsonse
}
在 React 使用時,若使用 async await 需加上 try catch
,catch 觸發條件為 StatusCode 4XX 或 5XX,ex 型態為 ApiError
try {
const data = {
"email": "[email protected]",
"password": "aaaaaaaa"
}
await login(data)
}
catch(ex) {
console.error(ex)
}
Typescript 中需要先定義 state 的結構,包含了 狀態
以及 更新狀態的 function
interface CountState {
count: number;
addCount: () => void;
setCount: (count: number) => void;
}
建立全域 state,習慣上稱呼為 store。所產生的 store 是個 hook,因此習慣上以 useXXXStore
形式命名。
const useCountStore = create<CountState>((set, get) => ({
count: 0,
addCount: () => set(state => ({ count: state.count + 1 })),
setCount: (count) => set(() => ({count}))
}))
建立時需要給 state 初始(預設)值,因此 count 為 0。
addCount、setCount 為更新狀態的 function,使用 set
更新 state,上面範例是 arrow function 寫法,傳統 function 寫法如下:
addCount: function () {
set(function (state) {
return {
count: state.count + 1
}
})
},
setCount: (newCount) => {
return set(() => ({
count: newCount
})
)},
addCount
執行時將 state 中 count + 1,set function 裡面的 state
是目前 store 的值,回傳物件裡頭有 count,值為 state.count + 1
。
setCount
執行時將 state 中 count 改為 newCount
。由於不需要目前的 count,因此不用 set function 裡面的 state
。回傳物件裡頭有 count,值為 newCount
。
使用時呼叫 useCountStore 並回傳所需要的欄位 s => s.count
後即可使用
const Sample = () => {
const count = useCountStore(s => s.count)
const addCount = useCountStore(s => s.addCount)
const handleAddCount = () => {
addCount()
}
return (
<button type='button' onClick={handleAddCount}>{count}</button>
)
}
範例程式可以參考 ./src/store/sample.tsx