我帮一朋友重构了点代码,他直呼牛批,但基操勿六

前端人

共 4083字,需浏览 9分钟

 ·

2022-04-20 23:02

首先事情是这样的

我一朋友,用 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')
)

语义大体上:ReactDOMrender 函数,把 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/clientcreateRoot 函数,把 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路径下,然后再定向导入,我们加个indexpages文件夹下,进行统一管理。

// 文件: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。

原文

https://juejin.cn/post/7085674288933502984
浏览 9
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报