我?guī)鸵慌笥阎貥嬃它c代碼,他直呼牛批,但基操勿六
點擊上方?前端Q,關注公眾號
回復加群,加入前端Q技術交流群
首先事情是這樣的
我一朋友,用 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>,
??document.getElementById('root')
)
復制代碼
語義大體上:
ReactDOM用render函數(shù),把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-dom/client用createRoot函數(shù),把id為'root'的dom節(jié)點做成了一個渲染器,然后用Render函數(shù)把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 (
)
}
)
復制代碼
PageCenter就是我們的頁面組件,一般都會在這里實現(xiàn)嵌套路由,如:
//PageCenter.tsx
import React from 'react'
import NestRoute from ‘./nestRoute’
import { Route, Switch } from "react-router-dom";
export default () => {
return (
<>
<>
)
}
)
復制代碼
em ~~~,跟 app.tsx 中實現(xiàn)的頂層路由很像,一脈相承。
評價一波 v5 路由的組織方式吧
tsx文件大臃腫:每配置一個路由,就寫一個Route組件,我個人是不喜歡的,我不希望我的tsx的代碼太多,這是我的喜好,為了閱讀起來容易,清晰。項目的文件夾結構復雜嵌套:頂層路由和嵌套子路由配置分離,直接影響了工程項目中對項目的文件夾結構的編排。因為不能夠很直接理清頁面組件間的組織關系,不理清會很混亂,維護難度加大,所以理清關系就落在了 項目的文件夾結構設計了,這就會導致項目的文件夾結構隨著v5 路由的組織方式的復雜而復雜。
React-router v6
可能是因為 v5 的種種原因,才導致 v6 的變化那么大,最突出便是:
v6 痛快的推出了配置式路由:一個簡單的配置對象,充分描述出了路由的樣子和組織關系,痛快~~~。 簡潔的路由嵌套方式:僅僅在配置了嵌套路由組件中,使用新推出的標簽就搞定了,優(yōu)雅~~~。
不過~~~,也有一些破壞性的改變,讓我措手不及,比如:
路由攔截無了!!!:攔截啊可是,怎么沒有了,這。。。 withRouter無了!!!:函數(shù)組件我能用hook搞搞,類組件咋辦,這。。。
em ~~~沒事 repect,畢竟進步嘛,怎么會沒代價呢,沒有咱就自己搞被,不坐車就不會走了么?
我為此寫了一個庫r6helper[2],盡可能的彌補了升級 v6 帶來的影響
攔截,安排上了。 
withRouter,安排上了。
路由好了,那么路由懶加載得有吧,怎么搞?
方式還是依然是通過 React.lazy配合import的動態(tài)引入,代碼如下。
const Login = React.lazy(() => import('./login'))
復制代碼
然后還要在通過React.Suspense包裹一下這個懶加載組件,否則的話會報錯,這個問題我的那個朋友可是卡住了很久。。。,原因就是忘記了要在懶加載組件外包裹一層React.Suspense。
...>}>{ }
復制代碼
但是,朋友又跟我講,每加一個頁面,就寫個lazy引入組件和Suspense包裹,那么頁面一多,代碼就會變成這樣:
const Login = React.lazy(() => import('./pages/login'))
const PageCenter = React.lazy(() => import('./pages/pageCenter'))
const Page1 = React.lazy(() => import('./pages/page1'))
const Page2 = React.lazy(() => import('./pages/page2'))
const Page3 = React.lazy(() => import('./pages/page3'))
const Page4 = React.lazy(() => import('./pages/page4'))
const Page5 = React.lazy(() => import('./pages/page5'))
...
export default () => {
return useRoutes([
{
path: '/',
element: ...>}>{ } ,
children: [
{ path: "codePLay", element: },
]
},
{
path: '/pageCenter',
children: [
{
path: '/page1',
element: ...>}>{ }
},
{
path: '/page2',
element: ...>}>{ }
},
{
path: '/page3',
element: ...>}>{ }
},
{
path: '/page4',
element: ...>}>{ }
},
{
path: '/page5',
element: ...>}>{ }
},
]
},
{
path: '/404',
element: not found
},
])
}
復制代碼
嵌套路由
//PageCenter.tsx
import React from 'react'
export default () => {
return (
)
}
)
復制代碼
這樣看起來就非常的冗余,很多重復的代碼,希望我能幫他優(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'))
export?Page2?=?React.lazy(()?=>?import('./pages/page2'))
export?Page3?=?React.lazy(()?=>?import('./pages/page3'))
export?Page4?=?React.lazy(()?=>?import('./pages/page4'))
export?Page5?=?React.lazy(()?=>?import('./pages/page5'))
復制代碼
然后我們重構一下之前的引入代碼:
const { Login, Page1, Page2, Page3, Page4, Page5 } from './pages'
復制代碼
封裝包裝組件,支持多類型
寫一個能夠包裝多類型的組件,都可以包裝:
組件,包括:函數(shù)組件和 類組件。 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, Page2, Page3, Page4, Page5 } from './pages'
...
export default () => {
return useRoutes([
{
path: '/',
element:wrapper(Login),
},
{
path: '/pageCenter',
children: [
{
path: '/page1',
element: wrapper(Page1)
},
{
path: '/page2',
element: wrapper(Page2)
},
{
path: '/page3',
element: wrapper(Page3)
},
{
path: '/page4',
element: wrapper(Page4)
},
{
path: '/page5',
element: wrapper(Page5)
},
]
},
{
path: '/404',
element: wrapper(not found)
},
])
}
復制代碼
em ~~~樸實無華,但是代碼看起來舒服不少,朋友感嘆學到不少干貨,我感覺這就是基本操作,233333。
關于本文
作者:閑D阿強
https://juejin.cn/post/7085674288933502984

往期推薦



最后
歡迎加我微信,拉你進技術群,長期交流學習...
歡迎關注「前端Q」,認真學前端,做個專業(yè)的技術人...


