我帮一朋友重构了点代码,他直呼牛批,但基操勿六
首先事情是这样的
我一朋友,用 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。