快速上手 react项目开发

1,626 阅读7分钟

前言: 原来一直使用vue开发,第一次接触react项目,如何快速上手?首先要能看懂项目中的代码,从目录结构到业务代码再到数据流。本文从一个umi+dva项目的目录结构开始,帮助大家建立起对react项目的基本认知,以便于在最开始接触react项目的时候不会一头雾水。

以下针对 react+ts+umi+dva+antd 搭建的项目;

首先是目录结构,一个umi项目的目录结构是这样的:

├── config
    ├── config.js // umi 配置,同 .umirc.js,二选一 
├── dist/ // 默认的 build 输出目录 
├── mock/ // mock 文件所在目录,基于 express 
├── public/ // 全局相对路径文件 
└── src/ // 源码目录,可选 
    ├── assets/ // 静态文件 
    ├── components/ // 全局共用组件 
    ├── layouts/index.js // 全局入口文件 
    ├── models/ // 全局models文件,存放全局共用数据store 
    ├── pages/ // 页面目录,业务组件 
        ├── .umi/ // dev 临时目录,需添加到 .gitignore 
        ├── .umi-production/ // build 临时目录,会自动删除 
        ├── index/ // 首页模块 
        ├── manager/ // 管理端模块 
            ├── components/ // 管理端-局部公共组件 
            ├── models/ // 管理端-局部models,存放manager的store 
            ├── services/ // 管理端-局部services,存放manager的接口 
            ├── index.js // 业务组件index 
            ├── page.js // 业务组件page 
        ├── 404.js // 404 页面 
    ├── services/ // 全局services文件,存放全局公共接口 
    ├── utils/ // 全局工具类 
    ├── global.css // 约定的全局样式文件,自动引入,也可以用 global.less 
    ├── global.js // 约定的全局Js文件,自动引入,可以在这里加入 polyfill 
    ├── app.js // 运行时配置文件 
├── .umirc.js // umi 配置,同 config/config.js,二选一 
├── .env // 环境变量 
└── package.json

一个业务组件文件的结构是这样的:

-users 
|-index.tsx ---------主页面的列表展现 
|-index.less ------------主页面样式展现
|-model.ts ---------仓库 
|-service.ts ---------异步接口的逻辑 
|-components ---------组件文件夹 

一个完整的React页面就是View + Service + Model ,分别对应着index.tsx,service.ts,model.ts。业务目录中,标配局部公用组件components,局部共用数据models,局部共用接口services,_layout.js入口文件需要则引入。

react、redux、redux-saga、react-router和umi、dva之间的关系

下面我想先说明一下react、redux、redux-saga、react-router和umi、dva之间的一个关系,以便于我们能更好的理解之后的知识

  • react:实现UI界面的一个库,没有考虑数据的存储和流向的问题
  • redux:专门处理数据的存储和流向的问题
  • redux-saga:基于redux的,专门处理异步数据
  • react-router:是一个基于 React 之上的强大路由库,它可以让你向应用中快速地添加视图和数据流,同时保持页面与 URL 间的同步;
  • dva:是一个基于redux和redux-saga的数据流方案。 (1)、也就是说redux和redux-saga是dva的一个基础,dva是一个实现数据流的,也就是实现仓库。
    (2)、为了简化开发体验,内置了react-router和fetch。
  • umi:umi 以路由为基础的,同时支持配置式路由和约定式路由,保证路由的功能完备,并以此进行功能扩展。然后配以生命周期完善的插件体系。

(1)、umi实际的核心就是路由,也就是react-router
(2)、上述中dva实现了一部分的react-router,umi是把react-router再次进行重新的整合和约定。 为了便于理解,这里画了一张图

image.png

dva基本流程

在介绍redux这个趁手工具之前,应该先知道英国在什么时候使用它,每个工具的诞生都是为了解决某些问题,可不能滥用哦~

曾经有人说过这样一句话:“如果你不知道是否需要 Redux,那就是不需要它。”
Redux 创造者也补充了一句:“只有遇到 React 实在解决不了的问题,你才需要 Redux 。”
不要把 Redux 当作万灵丹,如果你的应用没那么复杂,就没必要用它。
四张动图解释为什么使用redux

redux的概念:
一个项目中有公共数据和私有数据。 在redux中,公共数据就是Redux中Store的概念。 在Dva中提出了一个新的概念:Model。
所以在Dva中我会把Model理解为公共数据,state理解为私有数据。

那么在Dva中页面和仓库是怎么沟通的呢?
首先页面会发起请求,如果是异步数据,需要仓库中专到同步,再返回给页面。
也就是页面发送请求可以是同步也可以是异步,但是回来的时候只有一个口,通过同步返回。
下面是一张dva的流程图,从图中我们可以这么通俗理解:
Reducer可以理解为水龙头,水的来源可能不同,但是出水口只有一个,也就是Effect接收不同的接口的数据,然后统一返回给reducer,reducer再统一返回给页面。

image.png

文档

讲完最基本的架构关系以及数据流,一些api的用法也比较重要,关于api的使用主要参考文档。

react-hooks: useState,useEffect,useCallback,useMemo,useRef

redux-hooks: useSeletor,useDispatch...

⚠️ 1、React Hooks must be called in a React function component or a custom React Hook function.
⚠️ 2、React doesn't allow you to call hooks conditionally.

一些新手容易疑惑的地方:

最开始接触他们的时候,很容易傻傻分不清useEffect,useCallback,useMemo三个hooks,好像功能都一样,其实这三者的区别还是挺明显的。

  • useEffect useEffect可以帮助我们在DOM更新完成后执行某些副作用操作,如数据获取,设置订阅以及手动更改 React 组件中的 DOM 等
    有了useEffect,我们可以在函数组件中实现 像类组件中的生命周期那样某个阶段做某件事情 (具有componentDidMountcomponentDidUpdate 和 componentWillUnmount的功能)
    使用规则:
    useEffect 是在 render 之后浏览器已经渲染结束才执行
    (1)、没有传第二个参数时,在每次 render 之后都会执行 useEffect中的内容
    (2)、第二个参数是可选的,类型是一个空数组;第二个参数是非空数组,useEffect接受第二个参数来控制跳过执行,下次 render 后如果指定的值没有变化就不会执行
    (3)、第二个参数是空数组的时候,useEffect 只在第一次渲染时执行,由于空数组中没有值,始终没有改变,所以后续render不执行,相当于生命周期中的componentDidMount\

  • useCallback 和 useMemo useCallback 与 useMemo 一个缓存的是函数,一个缓存的是函数的返回就结果。useCallback 是来优化子组件的,防止子组件的重复渲染。useMemo 可以优化当前组件也可以优化子组件,优化当前组件主要是通过 memoize 来将一些复杂的计算逻辑进行缓存。当然如果只是进行一些简单的计算也没必要使用 useMemo,这里可以考虑一些计算的性能消耗和比较 inputs 的性能消耗来做一个权衡(如果真有逻辑跟这个比较逻辑差不多,也没必要使用 useMemo ,还能减少一点对键盘磨损 😅)。)但性能优化也会有成本,缓存过多时会占用内存过多,垃圾回收器不会及时释放,变成了负优化。因此,在大多数情况都不应该使用。
    那useCallback和useMemo分别要在什么场景使用呢?

[这里为了方便看懂前人写的代码,顺便提一下在子组件内部使用的性能优化的方式,有了react-hooks后应该用不到了? 函数组件会用到React.memo(),是一个高阶组件,通过对前后props进行浅比较,如果前后props不一致,该组件将重新渲染,反之,不进行渲染,使用缓存中的组件。类组件使用 shouldComponentUpdate,在子组件中使用 shouldComponentUpdate, 判定该组件的 props 和 state 是否有变化,从而避免每次父组件render时都去重新渲染子组件。]

参考文章:
[umi+dva项目快速上手指南]: blog.csdn.net/u010074572/…
[Dva 是如何进行数据流管理的]: juejin.cn/post/694790…
[什么时候使用 useMemo 和 useCallback]: jancat.github.io/post/2019/t…
[详解 useCallback & useMemo]: juejin.cn/post/684490…