前言
第一次写文章,希望能和大家交流学习,有不足之处请大家指正。虽然我现在还很菜,但是三十年河东,三十年河西,莫欺少年穷。嘿嘿有些许的中二,言归正传。这个我第一个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函数来改变仓库中的数据,这样极大的保证了仓库中数据安全性,也可以更好的进行数据管理。
在shopping页面组件下建立一个分支仓库store,初始化一个商品数据数组selectGoods。首先判断数组是否为空,显示不同的页面组件。使用connect连接仓库,第一个参数mapStateToPorps连接到指定仓库的某个数据,第二个仓库的数mapStateToDispatch定义dispatch函数,通知Reducer函数执行相应的操作,对store中的数据增删等操作,对redux仓库进行数据流管理。在首页中点击添加加入购入车触发事件,把当前页的数据添加到selectGoods数组中,然后再购物车页面组件中使用map循环selectGoods数组中的数据在页面上显示对应的商品卡片,数据驱动界面MVVM思想。其他的选中、删除、全选等redux数据管理就不再赘述,可以看文章末尾的源码链接查看源码。
总结
完成这一个react项目,也是我对自己21年前端学习的总结。再接下来想继续学习前端知识,不断的学习、不断的提升自己,希望能和更多人进行知识分享和交流学习。最后,如果如果这篇文章对你有帮助的话,希望掘友们多多点赞噢!!!🙇
源码
项目源码:gitee地址
线上预览 地址,pc端查看使用chrome手机模拟iPhone 12Pro最佳,还在更新迭代中