我帮一朋友重构了点代码,他直呼牛批,但基操勿六
首先事情是这样的
我一朋友,用 react 开发前端时间不长,一些简单的功能和页面没啥大问题。前不久React 18 发布了,他就用 create-react-app 创建了一个新项目,合计练练手,但谁成想遇到了种种问题,让我帮看看,于是就有了接下来要聊的一些看似简单,但是对新手却很绊脚的小问题。
react都 18 了,但为啥还是 ReactDom.render?
create-react-app新创建的项目,还是用的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节点上。
那么用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节点做成了一个渲染器,然后用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 痛快的推出了配置式路由」:一个简单的配置对象,充分描述出了路由的样子和组织关系,痛快~~~。 「简洁的路由嵌套方式」:仅仅在配置了嵌套路由组件中,使用新推出的标签就搞定了,优雅~~~。
不过~~~,也有一些破坏性的改变,让我措手不及,比如:
「路由拦截无了!!!」:拦截啊可是,怎么没有了,这。。。 「 withRouter无了!!!」:函数组件我能用hook搞搞,类组件咋办,这。。。
em ~~~没事 repect,毕竟进步嘛,怎么会没代价呢,没有咱就自己搞被,不坐车就不会走了么?
我为此写了一个库r6helper[1],尽可能的弥补了升级 v6 带来的影响
拦截,安排上了。 
withRouter,安排上了。
路由好了,那么路由懒加载得有吧,怎么搞?
方式还是依然是通过 React.lazy配合import的动态引入,代码如下。
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>
)
}
)
这样看起来就非常的冗余,很多重复的代码,希望我能帮他优化一下,em ~~~没问题,开整。
优化代码
主要从两个方面入手:
组件 lazy引入上然后 Suspense包裹上
统一入口
首先页面组件都放在了pages路径下,然后再定向导入,我们加个index在pages文件夹下,进行统一管理。
// 文件: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。
