前言
秋招正在进行时,听说很多公司都是React+ts技术栈,最近在准备提升自己的React实战能力,而且很多公司已全面升级到react hooks函数式 编程风格,读到了神三元在掘金的React Hooks 与 Immutable 数据流实战,写的很不错,于是,自己动手写一个仿极客时间APP的实战项目,提高自己的实战能力。
项目整体使用的技术栈:react hooks + redux + mocker-api + express + immutable
项目优化:
- 图片懒加载: 监听屏幕滚动事件,以防造成页面空白甚至卡顿
- 路由懒加载: react-lazyload,React自带的路由懒加载,进行性能优化。
- memo:页面优化,减少组件的重复渲染。
- better-scroll:超级好用的scroll基础组件,滚动列表,提高用户体验。
- styled-component:组件化,引入style-component,爱上切页面。
- 路由:react-router-config 让网页地址更清晰
- 防抖:使用户体验更佳。
- Immutable:结构共享,能够最大效率地更新数据结构,提高 React 渲染性能
项目效果如下:
项目结构
对项目结构的整体架构,是学习神三元react架构思想的第一步,也是自己写实战项目重要的一步。
-
全栈项目:一分为二的思想,前后端分离
-
api 架构:深入分析 axios配置文件config性更好, request 每个结构返回一个promise对象的函数,通过看函数,就可以把握应用业务全局。
-
组件思想:页面与组件分离,把页面开发当成搭积木。
-
后端API设计流程
- mongodb/mysql 写真后端 mockerAPI 解决了跨域
- 模块化输出api配置
- require json -> reducer -> connet -> 组件
数据请求都放在actions中,异步的actions中 dispatch多个action
-
项目整体目录结构如下:
├─React-geek-time
├─ geek-time_backend //前端部分
│ ├─ index.js
│ ├─ mocker //提供假数据
│ │ ├─ data //数据
│ │ └─ mocker.js //数据请求
├─ geek-time_frontend //后端部分
- 前端目录结构如下
├─src
├─ api // 数据请求接口及数据
├─ App.css
├─ App.js
├─ assets // 静态资源
├─ common // 公用组件
├─ components // 子组件
├─ index.css
├─ index.js // 入口文件
├─ layouts // 布局页面
├─ pages // 页面
│ ├─ explore // 发现页面
│ ├─ forum // 讲堂页面
│ ├─ my // 我的页面
│ ├─ payment // 支付页面
│ ├─ study // 学习页面
│ └─ tribe // 部落页面
├─ routes // 路由
└─ store // 总的store redux 模块化
前端部分
这里前端部分主要是对 路由配置及懒加载、styled-component样式、redux结构的设计及React.memo性能优化的使用讲解。
路由
-
路由配置:
routes/index.js部分代码如下:-
react-router-config:路由的设置,能够更清晰的看到网页地址。 -
react-lazyload:路由懒加载。
使用
React.lazy和Suspense来实现路由懒加载组件进行优化,可以理解成,先存放不加载就是lazy,需要时即满足渲染条件就开始加载,就是Suspense,这样可以快速打开页面。React文档对React.lazy的说法:
React.lazy必须通过调用动态的
import()加载一个函数,此时会返回一个Promise,并解析(resolve)为一个带有包含React组件的默认导出的模块。我们使用
Suspense,它必须是延迟加载组件的父级(即React.lazy加载的组件只能在<React.Suspense>组件下使用) -
styled-component
-
styled-components专门解决切页面, 不用过于组件化的方法,且方便复用,类名不会冲突 -
图片是
styled-components的写法,在项目中我是在My.jsx页面中,给列表容器添加一个样式style.js,直接在组件中包一下即可
redux 结构的设计
在axios定义完接口后用Redux进行数据请求,这里想讲一下 Redux 数据缓存。
就是你现在切换到浏览课程的讲堂页面时,然后切回到首页发现页面,在浏览器的 Network 中会看到又发了两次网络请求,而这两次请求是完全没有必要的,纯属浪费性能。而我们利用 Redux 的数据来进行页面缓存 成本最低。
useEffect(() => {
//immutable 数据结构中长度属性 length
// 如果页面有数据,则不发请求
if (!pathList.toJS().length || !directionList.toJS().length) {
getForumListDataDispatch();
}
if (!infoList.toJS().length) {
getInfoListDataDispatch();
}
}, [getForumListDataDispatch, getInfoListDataDispatch, pathList, directionList, infoList])
memo 的使用
-
在
react页面渲染过程中,为了减少diff算法,优化性能,使用shouldComponentUpdate,因为 默认的shouldComponentUpdate会在props或state发生变化时返回true, 表示组件会重新渲染,从而调用render函数,进行新旧DOM树的diff比较。 -
但是这个项目全面拥抱函数式组件,不再用类组件了,因此
shouldComponentUpdate就不能再用了。React为函数组件提供了一个memo方法,它和PureComponent在数据比对上唯一的区别就在于 只进行了props的浅比较。而且它的用法很简单,直接将函数组件React.memo包裹然后导出即可。形如:function Home () { //xxx } export default React.memo (Home); 他将上一次的
props与这次的props作比较 相同返回true不同返回false。
3.memo为高阶组件,通过记忆组件渲染结果的方式来提高组件的性能表现。默认情况下其只会对复杂对象做浅层对比,如果我们想要控制对比过程,可以将自定义的比较函数通过第二个参数传入来实现。通过第二个参数指定一个自定义的方法 来判断两次 props 有什么不同,memo会帮我们缓存上一个值,当我们接收一个新的值之后,两个值进行比较,相同的话,拒绝修改,直接使用已经缓存的值,不同的话, 则改变。 达到即缓存又更新的能力。
后端部分
后端主要实现假数据的模拟和数据请求,使用mockjs模拟假数据方便前后端对接,axios的GET和POST请求封装,express使用代理,解决跨域问题。
mockjs模拟假数据
在data目录下用json文件格式写模拟的假数据,然后在mock.js里面引入即可使用模拟的数据
axios的GET和POST请求封装
- 在
api目录下, 用axios定义了接口函数axiosInstance
const axiosInstance = axios.create({
baseURL:baseUrl
})
// 回复处理
axiosInstance.interceptors.response.use(
res => res.data,
err => {
console.log(err, '网络错误')
}
)
-
然后在请求中引入
axiosInstance,通过这个函数,get得到URL获取我们需要的数据,部分如下:// 获取学习路径和课程方向的数据 export const getLessonsRequest = () => { return axiosInstance.get("/all"); } // 获取全部课程的数据 export const getInfoListRequest = count => { return axiosInstance.get(`/infos?offset=${count}`); }
express
通过express框架使用代理来启动一个服务。
入口文件index.js
const express = require('express');
const path = require('path');
const apiMocker = require('mocker-api');
const app = express();
// 使用代理
// 跨域问题, => 已解决。
apiMocker(app, path.resolve('./mocker/mocker.js'))
//使用express监听8080端口号,
app.listen(8080);