使用(React + React-Rudex + 百度地图API)仿小米商城

1,987 阅读9分钟

前言

第一次写文章,希望能和大家交流学习,有不足之处请大家指正。虽然我现在还很菜,但是三十年河东,三十年河西,莫欺少年穷。嘿嘿有些许的中二,言归正传。这个我第一个react前端项目,下面介绍本次项目开发中碰到的一些问题,和一些本人觉得的小亮点。

项目结构

├─ server                   // 后端 
    ├─Data                  // 数据
    ├─public                // 服务器静态资源
    index.js
├─ src
    ├─api                   // 网路请求代码、工具类函数和相关配置
    ├─assets                // iconfront图标和图片
    ├─baseUI                // 基础 UI 轮子
    ├─common                // 通用组件库
    ├─components            // 页面 UI 组件
    ├─pages                 // 各个主页面
    ├─routes                // 路由配置文件
    └─store                 // redux 相关文件
      App.jsx               // 根组件
      main.jsx              // 入口文件

准备工作

创建react项目,使用命令创建项目脚手架

npm init @vitejs/app appName --template react

使用辅助工具Fiddler 抓取所需要的相关图片

使用iconfront获取底部导航相关图标

项目开始

创建完成项目脚手架之后,在开发页面组件之前,还需要一些配置工作,这样更有利于后面的页面的开发。解决不同设备上的适配问题,使用npm安装postcss-pxtorem插件把px自动装换成rem和lib-fiexible获取当前设备根字体的大小,从而获取不同设备的1rem大小 完成不同设备之间的适配。

使用react-router V6完成项目路由的配置,使用最新的useRoutes渲染路由封装成一个组件

export default function WrapRoute() {
    // 使用useRoutes 渲染路由
    const routes = useRoutes([
        {path: '/',  exact: true,  element: <Navigate to='/home' />},
        {path: '/home',  element: <TabBottom />,
            children: [
                {path: '/home',  exact: true,  element: <Navigate to='/home/main' />},
                {path: '/home/main',  element: <Main />},
                {path: '/home/classify',  element: <Classify />},
                {path: '/home/location',  element: <Location />},
                {path: '/home/shopping',  element: <Shopping />},
                {path: '/home/my',  element: <My />}	
            ]
        },
        {path: '/detail/:id',  element: <Detail />}
    ])
    return routes
}

还有要注意的是在react-router V6中,直接进行父子路由嵌套,但是需要在父级路由组件中加上一个<Outlet />占位符

然后就是使用react-redux仓库基本配置,使用combineReducers合并多个分支仓库到总store

import { combineReducers } from 'redux';
import { reducer as mainReducer } from '../pages/main/store/index'
import { reducer as shoppingReducer } from '../pages/shopping/store/index'

export default combineReducers({
    main: mainReducer,
    shopping: shoppingReducer
});

完成底部导航栏的设计,把底部导航栏组件设置为一级路由,其他页面及组件放置在二级路由,这样可以使底部导航栏在每个主页面都显示

这里有一个碰到的小细节,底部导航栏点击高亮默认有一个蓝色背景效果 使用-webkit-tap-highlight-color: transparent; 设置为透明

页面组件开发

首页效果图

首页由多个组件构成

每个组件都使用了memo进行性能优化。因为是移动端项目,所以整体布局使用了Bscroll。顶部搜索栏searchInput组件,使用Bscroll中onScroll监听触发对应事件,完成下拉对应的动画效果。使用flex: 1;固定搜索框右端,加上transition完成动画过渡效果。

轮播图rotationChart组件,使用swiper7完成效果开发。第一次接触swiper,看官方文档逐步了解并完成组件开发。这里值得一提的是原本默认的分页器效果是小圆点,官方也没有提供横条的分页器效果,然后为了修改成我想要的效果,我我是用chrome 选中小圆点,查看默认类名,然后配合!important 修改原有的一些默认样式,从而实现想要的效果。

菜单栏menuBar组件,简单的页面开发,然后为实现左右吸顶效果也是用了Bscroll,当时了解的只有上下吸顶效果,为此也是查阅了很多资料,最后顺着上下吸顶效果的源码,找到了左右吸顶的源码。第一次自己接触源码解决问题,感觉很酷。

然后就是miHome组件,其中上图中框起来的小文字标题,也是使用的swiper组件,从而实现上下翻页切换。主题的miHome组件中的商品卡片整体也是使用的Bscroll,但是通过传参取消了默认的左右吸顶,从而可以实现与上面menuBar组件不一样的吸顶。

接下来就是两个简单的recommend组件和scondaryMenu组件,就是单纯的切页面,然后scondaryMenu组件使用Bscroll来滑动,取消了所有的吸顶效果。

后面就是商品页面commodity组件,前面两个商品是自己写了两个json数据在后端,然后是secondaryContent组件,商品的数据都是使用mockjs模拟数据,都是通过axios请求拿到数据。因为数据都是自己写的json数据,所以就简单的写了两条商品数据,然后通过路由传参跳转路由页面,通过参数不同拿到不同的商品数据,在detail组件中显示出不同的数据页面,数据驱动页面,坚守MVVM思想。其中下面是secondaryContent组件需要的mockjs后端模拟假数据的代码。其它页面请求数据也都是使用axios。其中后端还使用了koa2-cors来实现跨域,还有koa-static来使用服务器静态资源。

router.get('/home/context', async (ctx) => {
  let {limit = 20, page = 0 } = ctx.request.query
  // console.log(limit,page);

  let data = Mock.mock({
   'list|20': [{
    'id': '@increment',
    "imgUrl1": Random.image('150x150'),
    "imgUrl2": Random.image('150x150'),
    "imgUrl3": Random.image('150x150'),
    "price|1-10000": 10000,
    'name': '@ctitle(5,10)',
    "text1": "① @ctitle(5,10)",
    "text2": "② @ctitle(5,10)",
    "text3": "③ @ctitle(5,10)",
    'imgsrc': Random.image('150x150'),
    "isSelect": false,
    "count": 1
    }]
  }) 
  
  // 返回响应体
  ctx.body = {
    success: true,
    data: data
  }
})

使用axios请求mockjs模拟的后端数据,每次请求20条,配合懒加载,在数据到来之前图片显示一张loading图,进行些许的性能优化。然后设置一个监听事件,使用在Bscroll中的handlePullUp上拉加载更多,配合react的Hooks函数useEffect()的使用,使其滑到页面底部就继续请求20条数据。

百度地图API

这是我第一次接触百度地图API,也碰到了一些问题,但迎难而上的感觉很不错。整个页面核心部分就是百度地图API的使用了。其他的就是简单的页面开发。

使用官方的 React-BMapGL文档 。使用npm方式安装react组件库,然后通过es模块加载

npm install react-bmapgl --save

其中遇到的难题是,自定义标记的图标样式,我当时首先使用的提供的<CustomOverlay />组件,设置地图覆盖物,然后发现它不提供点击事件的函数。然后就转为使用提供的<Marker />点标注。但是这里如何自定义图标困扰了我很久,在网上查询了很多文章也没有找到react函数式组件中怎样自定义Maker的图标。最终更还是根据之前提到的看Bscroll源码的经验,如上图源码截图中标注。IconString定义的是默认图标样式,然后我尝试使用BMapGl.Icon,如下代码

// 自定义icon 图标
let icon1 = new BMapGL.Icon(xiaomi, new BMapGL.Size(20, 20))

其中接受的第一个参数是图片的地址,这里我是使用的从外部引入的本地图片定义变量名为xiaomi,后面第二个参数是设置图片的大小尺寸,等等还可以接受其他的参数。

然后就可以通过,Maker自身属性的点击事件,点击不同的Maker图标选中下面不同的地址卡片。通过一样的index选中对应的地址卡片。然后先通过useRef标记Bscroll节点,获取使用Bscroll中的scrollTo方法自动滑动定位到选中的地址卡片。

购物车功能

下面就是此次项目的中的核心部分react-redux数据流管理。

在react组件中,有时候会遇到一个组件的数据或状态要在另一个组件中用到,这时我们可以使用context,多个组件中共享数据或状态,这样就会有多个组件都可以访问或修改这个数据或状态,如果时父子组件中还可以使用传参props共享状态,但是仅限于父子组件之间。所以都有一定的缺点,这是就可以使用react-rudex进行统一的数据流管理。需要访问数据或状态就使用cannect连接对应的仓库,需要修改状态或者数据只能通过diapatch去通知reducer函数来改变仓库中的数据,这样极大的保证了仓库中数据安全性,也可以更好的进行数据管理。

QQ录屏20220215170314.gif

shopping页面组件下建立一个分支仓库store,初始化一个商品数据数组selectGoods。首先判断数组是否为空,显示不同的页面组件。使用connect连接仓库,第一个参数mapStateToPorps连接到指定仓库的某个数据,第二个仓库的数mapStateToDispatch定义dispatch函数,通知Reducer函数执行相应的操作,对store中的数据增删等操作,对redux仓库进行数据流管理。在首页中点击添加加入购入车触发事件,把当前页的数据添加到selectGoods数组中,然后再购物车页面组件中使用map循环selectGoods数组中的数据在页面上显示对应的商品卡片,数据驱动界面MVVM思想。其他的选中、删除、全选等redux数据管理就不再赘述,可以看文章末尾的源码链接查看源码。

总结

完成这一个react项目,也是我对自己21年前端学习的总结。再接下来想继续学习前端知识,不断的学习、不断的提升自己,希望能和更多人进行知识分享和交流学习。最后,如果如果这篇文章对你有帮助的话,希望掘友们多多点赞噢!!!🙇‍

源码

项目源码:gitee地址

线上预览 地址,pc端查看使用chrome手机模拟iPhone 12Pro最佳,还在更新迭代中