一、前言
本项目是为了给
react新手入门
做的一个仿星巴克
组件demo,如果能帮助到您,还请给一个小小的点赞
,其次本项目部分运用神三元
大佬在react-cloud-music
开源项目中的代码:react-cloud-music源码,react-cloud-music小册讲解。其中用到的一些工具组件
我会说明。
1.0、废话
- 主要是想在学习大佬的项目途中将一些知识融入到自己的项目(理解)当中,和第一集项目
网络请求
有着比较大的更新,其他原来的页面只是更新了redux
数据流管理,如果感兴趣的话可以收藏
或者关注专栏
1.1、项目展示与项目地址
- 样式方面为了赶工还没有做好
rem适配
,待文章发布后会加急修改
一下适配 - 可以的话请给一个star支持一下:项目源码github
- 可以的话请给一个star支持一下:项目源码gitee
1.2、本次更新内容
- 做了网络请求的封装
api/config
- 搜索菜品防抖(
debounce
) - 菜单页面懒加载(
lazy-load
) - 更新
redux
数据流管理(不要问我为什么还在用redux
) - 搜索时热门搜索列表(
hotlist
) - 首页轮播图实现(
swiper
)
二、redux
2.0、技术设计
- 这次的更新除了
redux
是新东西以外,其他没有改变 - 相较于第一集,
网络请求
方面有较大的变化
2.1、理解redux(从useState过渡)
- 首先我不得不说,这个项目的
数据量
根本不至于用到redux,但是为了更好的理解redux
,我还是把他加到了这个项目当中,那么我们要如何从原来
在页面中通过useState()
定义数据的初始状态,以及改变的setXXX()
函数,到现在
的redux数据流管理? - 从页面的
useState
的思想中跳出来,当我们开发一个大型项目
的时候,不可能把数据放在当前要运行
的页面,这样不利于协同开发
,页面多了数据也无法管理
,所以我们用一个公共的数据仓库
存一下页面所涉及的数据 - 当前页面要发生
状态变化
的时候,当前页面
打一个电话给store
,store
收到了要变化的条件
,返回给页面对应的状态
,从而实现MVVM
(具体实现中间还有很多过程,为了便于理解这么讲)。
- Store:Store 就是保存数据的地方,你可以把它看成一个容器。
- State:Store对象包含所有数据。如果在某个页面数据,就要对 Store 生成快照。
- Action:在原来的useState中,State 的变化,会导致 View 的变化。但是,用户接触不到 State,只能接触到 View。所以,State 的变化必须是 View 导致的。Action 就是 View 发出的通知,表示 State 应该要发生变化了。
- ActionCreators:指明Action变化的种类
- dispatch():是 View 发出 Action 的唯一方法。
- Reducer:Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。
三、项目实现
3.0、封装网络请求
- 上次我们把
数据过滤
放在数据请求
里,这样请求一多,request
文件会变得很复杂 - 为了在代码上线时候可以快速修改
localhost:3000
为服务器端url
我们进行了一层封装,实现request.config
{/*代码实现*/} // 配置请求对象 import axios from 'axios' // 本地调试 dev 开发阶段,baseurl为远程基础url export const baseUrl = "https://www.fastmock.site/mock/5321bf649d06645c4266f3e0d45ae1cc"; const axiosInstance = axios.create({ baseURL: baseUrl }) axiosInstance.interceptors.response.use( res => res.data, err => { console.log(err, '网络错误~~') } ) export { axiosInstance }
{/*代码实现*/} import axios from 'axios' import { axiosInstance } from "./config"; export const getMenuListRequest = () => axiosInstance.get('/menu/all') export const getHotListRequest = () => axiosInstance.get('/menu/hot')
3.1、实现搜索+防抖+useMemo
- 这里把搜索懒加载放到
0.8s
实现搜索一次,效果明显一点,如果是自己写项目,0.3
左右即可,太长用户体验不好 - 搜索防抖的实现
工具类
随处可见,如果想自己实现的话可以参考下面的代码,甚至可以直接用lodash
中的`debounce - 搜索实现
- 这里我仿造
神三元
大佬在其项目中的搜索,最外层用了一个Search组件
,内层用SearchBox
包了一层 Search
组件有一个query变量(便于理解我们叫他fatherQuery
),和一个handleQuery函数
用于修改fatherQuery
SearchBox
组件有自己的query(便于理解我们叫他sonQuery
),同时接收传进来的Search
中的fatherQuery
和handleQuery
- 在
SearchBox
中修改的sonQuery
,进行防抖处理,每隔800
毫秒,执行一次handleQuery
去修改Search
中的fatherQuery
,因为最后是fatherQuery
执行dispatch
改变状态
//父组件Search function Search(props) { const {hotList,suggestList} = props; const {getHotKeyMenuDispatch,getSuggestMenuDispatch} = props //这个useEffect只有每隔800ms才会得到重新渲染的机会 useEffect(()=>{ getSuggestMenuDispatch(query); },[query]) //用于在子组件searchBox执行,更新父组件的query //但是子组件有防抖,需要每隔800ms执行一次 const handleQuery = (q) =>{ setQuery(q) } return( <> <SearchBox newQuery={query} handleQuery={handleQuery}> </SearchBox> </> ) }
export default memo(function SearchBox(props) { //得到父组件的变量与函数 const {handleQuery} = props const [query,setQuery] = useState('') useEffect(() => { //父组件的query去dispatch更新状态 handleQueryDebounce(query) }, [query]) //防抖 let handleQueryDebounce = useMemo(() => { return debounce(handleQuery,800) }, [handleQuery]) //返会给父组件query值 const handleChange = (e) =>{ let val = e.currentTarget.value setQuery(val) } return ( <SearchWrapper> <div className="search_box"> <input type="text" className='box' placeholder='搜索菜单' onChange={handleChange} ref={queryRef}> </input> <i className='iconfont icon-sousuo' onChange={handleChange}></i> </div> </SearchWrapper> ) })
- 这里我仿造
- useMemo
- 这里是一个很适合用到
useMemo
的地方,因为当fatherquery
改变的时候会触发useEffect
重新渲染
整个子页面,但是我们handleQuery
函数却不用每次都执行,useMemo()会记住这次运行的结果,是一个性能优化
的好帮手,在跨页面数据流动减少复杂状态更新
的地方常常用到。 - 例如网站首页加载了很多图片,但是我们跳到其他页面再回来又需要重新渲染这个页面,十分浪费性能,这种类似的情况就可以用到
useMemo()
- 这里是一个很适合用到
- CSSTransition
- 这个是用来实现点击搜索后,页面
从右到左划过
的效果,需要的可以去仓库拉去样式
- 这个是用来实现点击搜索后,页面
3.2、懒加载
- 在菜单展示页面和搜索出来的菜品都有
lazyload效果
。 lazyload
的实现在今天已经非常简单了,以前可能还需要手写判断视口
,或者用他人写好的工具库
。- 我们只需要
npm i react-lazyload
- 在
img
遍历的过程中加入Lazyload组件
,并准备好替换图片
放在placeholder
上{/*代码实现*/} <div className="good"> {/* lazyload组件,用placeholder存放懒加载时的图片 */} <Lazyload placeholder={ <img width="120%" height="100%" src={loading} />}> <img src={goodItem.img} alt=""/> </Lazyload> <div className="name">{goodItem.goods}</div> </div>
3.3、首页
- 首页差不多做到了
1:1
复刻,但是首页没有什么业务逻辑
,所以后续估计也不会更新首页了,会着眼于其他页面的业务进行打造,争取做到一个比较适合前端面试
的程度。 - 轮播图的效果主要是运用了
antd
的swiper
组件 - 如果首页有想了解的样式或者布局可以去仓库拉取。
const swiper1_img = [img1,img2] //items遍历轮播图的图片 const items = swiper1_img.map((item, index) => ( <Swiper.Item key={index} > <img src={item} alt="" /> </Swiper.Item> )) export default function Home() { return ( <Container> <LoopImg> <div title='循环'> {/*用items占位*/} <Swiper loop autoplay className='sw'>{items}</Swiper> </div> </LoopImg> </Container> )}
四、结尾
最后还是希望大家不要吝啬1kb的
赞
和gitee的star
,这对我这真的很重要,谢谢
,期待下次更新能让这个项目更加完善。并且推荐一下神三元大佬
的项目,如果难以理解可以购买一下他的小册
,有对应的讲解小册链接
我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿。