我的博客地址
文章目錄
- 項(xiàng)目及其技術(shù)棧介紹
- 前端: 項(xiàng)目初始化
- 前端: 使用Sass和Antd
- 前端: 開發(fā)體驗(yàn)優(yōu)化
- 前端: 搭建路由和狀態(tài)管理
- 前端: 支持Axios
- 前端: 打包與環(huán)境變量設(shè)置
- 前端: 團(tuán)隊(duì)代碼規(guī)范
- 后端: 項(xiàng)目初始化和使用Koa相關(guān)
- 后端: 使用TypeORM和MySQL
- 部署: 使用nginx部署前端項(xiàng)目
- 部署: 后端部署
- 部署: 使用jenkins自動(dòng)化部署
前言
該篇博客介紹如何使用react-router4+搭建前端路由,并使用最新的suspense和lazy構(gòu)建路由的按需加載。同時(shí)介紹狀態(tài)管理的搭建,其中在狀態(tài)管理搭建中,會嘗試使用最新的useReducer
+context
來搭建前端的狀態(tài)管理,以及他與TypeScript之間的配合。
- React Router4+,suspense,lazy
- useReducer + context
前端路由搭建
安裝react-router
首先我們需要安裝react-router
以及他的類型文件
npm i -S react-router-dom
npm i -D @types/react-router-dom
在這里說一下react-router
和react-router-dom
之間的區(qū)別:
react-router
是react-router-dom
的核心代碼,提供理由的核心api,例如Router、Route、Switch
等,而react-router-dom
提供了BrowserRouter,Link
等api,react-router-dom
里面包含了react-router
,所以一般只需要安裝react-router-dom
-
編寫路由頁面
然后我們在src/containers/views
中編寫兩個(gè)路由頁面PageA
和PageB
image.png -
接著我們還是在
src/containers/views
中新建App
組件,并在App組件下新建routerMap.tsx
文件用作存放路由表。
在routerMap
文件中,我們引入lazy
方法,并通過import()
引入PageA
和PageB
,然后通過統(tǒng)統(tǒng)放到一個(gè)數(shù)組中,將這個(gè)數(shù)組導(dǎo)出去:
image.png -
我們?nèi)サ?code>App/index.tsx中,從
react-router-dom
中導(dǎo)入BrowserRouter, Route和Switch
,以及從React中導(dǎo)入Suspense
組件,然后編寫路由文件,并將其用Suspense
包裹起來,使用了lazy方法導(dǎo)入的組件必須用Suspense
包裹,否則不能加載出來并報(bào)錯(cuò):
image.png -
在這里需要注意的是,如果使用的是
BrowserRouter
,需要在webpack中加上devServer配置,并打開historyApiFallback
,原因是當(dāng)跳轉(zhuǎn)到page-b
頁面的時(shí)候,他會試圖尋找page-b.html
,但我們是單頁應(yīng)用,所以沒有這個(gè)文件,從而出現(xiàn)找不到的情況,而historyApiFallback
會在找不到頁面的情況下跳轉(zhuǎn)回index.html
,避免直接出現(xiàn)找不到的情況:
image.png
-
最后我們回到頁面查看跳轉(zhuǎn)情況:
路徑為/
的時(shí)候加載PageA
:
image.png
當(dāng)路徑切換為/page-b
的時(shí)候,則加載PageB
:
image.png
這時(shí)候我們的前端路由就搭建好了,并且可以進(jìn)行動(dòng)態(tài)加載 -
使用
withRouter
方法進(jìn)行路由跳轉(zhuǎn)
去到PageA
頁面添加如下代碼:
image.png
image.png
這時(shí)候點(diǎn)擊跳轉(zhuǎn)B按鈕的時(shí)候就能夠跳轉(zhuǎn)到/page-b
路徑了:
image.png
狀態(tài)管理搭建
在React技術(shù)棧中我們通常使用Mobx
,Redux
進(jìn)行前端狀態(tài)管理,但在本項(xiàng)目中未采用這兩個(gè)庫,而是使用了useReducer + context
的方式來進(jìn)行搭建,而本文中則使用一個(gè)加減計(jì)數(shù)器的方式來展示如何搭建和使用useReducer + context
狀態(tài)管理
進(jìn)行這一步之前首先需要知道如何使用useContext、createContext以及useReducer
-
首先我們在
src
文件夾下新建兩個(gè)文件夾store
和components
,其中store
用于存儲共用狀態(tài)和配置,components
用于存放組件。
image.png -
然后我們在
store
文件夾中新建index.tsx
文件和count
文件夾,在count
文件夾中新建index.tsx
、reducer.ts
以及types.d.ts
文件:
image.png -
其中
index.tsx
文件用于存放Provider
和初始狀態(tài),如下圖:
image.png -
然后在
reducer.ts
中,我們開始編寫reducer
:
image.png
編寫完成在index.tsx
中導(dǎo)入:
image.png -
解決類型報(bào)錯(cuò)問題
這時(shí)候你會發(fā)現(xiàn)reducer和index.tsx中都存在很多報(bào)錯(cuò),這是因?yàn)闆]有添加類型導(dǎo)致ts認(rèn)為他們是any類型,所以我們?yōu)樗麄兲砑宇愋臀募?br> 我們?nèi)サ街靶陆ǖ?code>types.d.ts文件中,定義好state的接口和action的類型,為了避免今后store變得原來越大,我們使用命名空間來避免命名沖突:
image.png
之后到reducer.ts
將類型補(bǔ)充上去:
image.png
而index.tsx
中props
的報(bào)錯(cuò)則可以使用React自帶的ComponentType
類型將Provider
定義為組件類型以解決:
image.png
image.png -
定義Context的類型
另外有些小伙伴可能會有如下ts錯(cuò)誤:
image.png
這是因?yàn)槲覀冊?code>createContext中傳入的參數(shù)為null
導(dǎo)致的。
要解決這個(gè)問題首先我們?nèi)サ?code>src/types文件夾中新增context.d.ts
文件,接著定義Context的類型,注意因?yàn)橹霸贑ontext的value
中我們放入的是state
和dispatch
,所以這兩個(gè)都需要進(jìn)行定義,而state和dispatch每個(gè)store可能都不一樣,所以需要用到泛型:
image.png
之后到index.tsx
中進(jìn)行使用,注意這里我們從react中導(dǎo)入了Dispatch
類型:
image.png
最后我們到tsconfig.json
中將strictNullChecks
的屬性設(shè)置為false:
image.png
-
最后我們回到
store/index.tsx
文件中,導(dǎo)入所有的Provider
和useXXXStore
,然后組成一個(gè)數(shù)組,通過一個(gè)總的Provider
進(jìn)行導(dǎo)出:
image.png
然后到src/index.tsx
中導(dǎo)入這個(gè)總的Provider
,并使用它包裹<App />
組件
image.png -
驗(yàn)證成果
驗(yàn)證成果之前我們需要先將store和components的路徑別名分別添加進(jìn)webpack和tsconfig中去,不動(dòng)怎么添加的看我上一篇文章:
image.png
image.png
我們到之前新建的components
文件夾中新建兩個(gè)組件CountOperation
和ShowCount
:
然后在里面將
useTestStore
方法從store引入,這個(gè)方法執(zhí)行后可以得到state
和dispatch
并且這些都是帶有類型提示的,說明我們的ts和
useReducer + context
結(jié)合得非常棒了:最后把這兩個(gè)組件在PageA
頁面中引入進(jìn)行成果驗(yàn)證:
查看結(jié)果:
這樣我們的useReducer + context
的狀態(tài)管理方案就完成了。
難點(diǎn)我估計(jì)在于對Context的類型編寫,因?yàn)檫@一塊會用到嵌套的泛型,而泛型在TypeScript中是一個(gè)難點(diǎn),但這個(gè)難點(diǎn)是必須攻克的: