我?guī)鸵慌笥阎貥嬃它c代碼,他直呼牛批,但基操勿六
首先事情是這樣的
我一朋友,用 react 開發(fā)前端時間不長,一些簡單的功能和頁面沒啥大問題。前不久React 18 發(fā)布了,他就用 create-react-app 創(chuàng)建了一個新項目,合計練練手,但誰成想遇到了種種問題,讓我?guī)涂纯矗谑蔷陀辛私酉聛硪牡囊恍┛此坪唵危菍π率謪s很絆腳的小問題。
react都 18 了,但為啥還是 ReactDom.render?
create-react-app新創(chuàng)建的項目,還是用的ReactDom.render,如下:
import?React?from?'react'
import?ReactDOM?from?'react-dom'?//《----------react?17使用的ReactDOM
import?App?from?'./App'
import?'./index.css'
ReactDOM.render(
??<React.StrictMode>
????<App?/>
??React.StrictMode>,
??document.getElementById('root')
)
?語義大體上:
?ReactDOM用render函數,把JSX Elements組件,渲染到id為'root'的dom節(jié)點上。
那么用react 18的新寫法改造一下
??
react 18改了
//index.tsx
import?React?from?'react'
import?{?createRoot?}?from?'react-dom/client'?//《----------react?18使用的ReactDOM/client中的createRoot
import?App?from?'./App'
import?'./index.css'
function?render()?{
??const?root?=?createRoot(document.getElementById('root')!)
??root.render(
????<React.StrictMode>
??????<App?/>
????React.StrictMode>
??)
}
?語義大體上:
?react-dom/client用createRoot函數,把id為'root'的dom節(jié)點做成了一個渲染器,然后用Render函數把JSX Elements渲染出來。
react 都 18 了,React-router 得 v6 啊,但變化好大,咋用啊?
??
React-router v6可謂是變化著實不小,之前v5組織路由是這樣的:
React-router v5
//app.tsx
import?React?from?'react'
import?{?BrowserRouter?as?Router,?Route,?Switch?}?from?"react-router-dom";
export?default?()?=>?{
??return?(
??????<Router>
????????<Switch>
??????????<Route?path="/"?component={Login}?/>
??????????<Route?path="/pageCenter"?component={PageCenter}?/>
????????Switch>
??????Router>
????)
??}
)
??
PageCenter就是我們的頁面組件,一般都會在這里實現嵌套路由,如:
//PageCenter.tsx
import?React?from?'react'
import?NestRoute?from?‘./nestRoute’
import?{?Route,?Switch?}?from?"react-router-dom";
export?default?()?=>?{
??return?(
??????<>
????????<Switch>
??????????<Route?path="/pageCenter/nestRoute"?component={NestRoute}?/>
????????Switch>
??????<>
????)
??}
)
?em ~~~,跟 app.tsx 中實現的頂層路由很像,一脈相承。
?
評價一波 v5 路由的組織方式吧
tsx文件大臃腫:每配置一個路由,就寫一個Route組件,我個人是不喜歡的,我不希望我的tsx的代碼太多,這是我的喜好,為了閱讀起來容易,清晰。項目的文件夾結構復雜嵌套:「頂層路由和嵌套子路由配置分離」,直接影響了工程項目中對項目的文件夾結構的編排。因為不能夠很直接理清頁面組件間的組織關系,不理清會很混亂,維護難度加大,所以理清關系就落在了 項目的文件夾結構設計了,這就會導致項目的文件夾結構隨著v5 路由的組織方式的復雜而復雜。
React-router v6
可能是因為 v5 的種種原因,才導致 v6 的變化那么大,最突出便是:
「v6 痛快的推出了配置式路由」:一個簡單的配置對象,充分描述出了路由的樣子和組織關系,痛快~~~。 「簡潔的路由嵌套方式」:僅僅在配置了嵌套路由組件中,使用新推出的標簽就搞定了,優(yōu)雅~~~。
不過~~~,也有一些破壞性的改變,讓我措手不及,比如:
「路由攔截無了!!!」:攔截啊可是,怎么沒有了,這。。。 「 withRouter無了!!!」:函數組件我能用hook搞搞,類組件咋辦,這。。。
em ~~~沒事 repect,畢竟進步嘛,怎么會沒代價呢,沒有咱就自己搞被,不坐車就不會走了么?
我為此寫了一個庫r6helper[1],盡可能的彌補了升級 v6 帶來的影響
攔截,安排上了。 
withRouter,安排上了。
路由好了,那么路由懶加載得有吧,怎么搞?
方式還是依然是通過 React.lazy配合import的動態(tài)引入,代碼如下。
const?Login?=?React.lazy(()?=>?import('./login'))
然后還要在通過React.Suspense包裹一下這個懶加載組件,否則的話會報錯,這個問題我的那個朋友可是卡住了很久。。。,原因就是忘記了要在懶加載組件外包裹一層React.Suspense。
...</>}>{>}</React.Suspense>
但是,朋友又跟我講,每加一個頁面,就寫個lazy引入組件和Suspense包裹,那么頁面一多,代碼就會變成這樣:
const?PageCenter?=?React.lazy(()?=>?import('./pages/pageCenter'))
const?Page1?=?React.lazy(()?=>?import('./pages/page1'))
...
export?default?()?=>?{
??return?useRoutes([
????{
??????path:?'/pageCenter',
??????children:?[
????????{
??????????path:?'/page1',
??????????element:?<React.Suspense?fallback={<>...>}>{ }</React.Suspense>
????????},
??????]
????}
??])
}
嵌套路由
????//PageCenter.tsx
????import?React?from?'react'
????export?default?()?=>?{
??????return?(
??????????<div>
??????????????<Outlet?/>
??????????div>
????????)
??????}
????)
這樣看起來就非常的冗余,很多重復的代碼,希望我能幫他優(yōu)化一下,em ~~~沒問題,開整。
優(yōu)化代碼
主要從兩個方面入手:
組件 lazy引入上然后 Suspense包裹上
統(tǒng)一入口
首先頁面組件都放在了pages路徑下,然后再定向導入,我們加個index在pages文件夾下,進行統(tǒng)一管理。
//?文件:pages/index.ts
export?Login?=?React.lazy(()?=>?import('./pages/login'))
export?Page1?=?React.lazy(()?=>?import('./pages/page1'))
然后我們重構一下之前的引入代碼:
const?{?Login,?Page1?}?from?'./pages'
封裝包裝組件,支持多類型
寫一個能夠包裝多類型的組件,都可以包裝:
「組件」,包括:「函數組件」和 「類組件」。 「lazy 組件」。 「jsx element」。
那么代碼如下:
//?加載異步組件的loading
type?ChildT?=?React.LazyExoticComponent<()?=>?JSX.Element>?|?React.FC
export?const?wrapper?=?(Child:?ChildT,?cutonFallBack?:?CutonFallBackT)?=>?{
??//?判斷jsx
??if?(Child.type?&&?!Child._init?&&?!Child._payload)?{
????return?Child
??}?else?{
????//?判斷是否為clas和function組件
????if?(typeof?Child?===?'function')?{
??????return?<Child>Child>
????}?else?{
??????//?判斷是否為lazy組件
??????return?(
????????<React.Suspense?fallback={cutonFallBack?||?<>...>}>
??????????{</Child>}
????????React.Suspense>
??????)
????}
??}
}
那么這樣整體重構后的代碼,就大體變成了
const?{?Login,PageCenter,?Page1?}?from?'./pages'
...
export?default?()?=>?{
??return?useRoutes([
????{
??????path:?'/',
??????element:wrapper(Login),
????},
????{
??????path:?'/pageCenter',
??????children:?[
????????{
??????????path:?'/page1',
??????????element:?wrapper(Page1)
????????},
????????
????{
??????path:?'/404',
??????element:?wrapper(<div>not?founddiv>)
????},
??])
}
em ~~~樸實無華,但是代碼看起來舒服不少,朋友感嘆學到不少干貨,我感覺這就是基本操作,233333。
原文
