前言
React作为前端使用频率最高的三大框架之一,组件化是其核心思想。对于学习过React的前端小伙伴来说,React中一切都是组件(相信大家都耳熟能详)。下面将介绍本人实战项目中对复杂组件的组件化设计的过程,希望对各位小伙伴有一定的帮助和启发。
前期准备
在项目开始之前,我们需要对项目所需的开源软件库和相关UI组件有一定的了解。下面是本人在开发中用到的开源库(不太了解的小伙伴可以查阅相关文档进行学习)。
- antd-mobile 是一个基于Preact/React/React Native的UI组件库,拥有大量实用易上手的组件。
- Swiper 能实现触屏焦点图、触屏Tab切换、触屏轮播图切换等常用效果。
- axios 是一个基于 promise 的网络请求库,用于获取后端数据,是前端常用的数据请求工具。
- font-awesome 一套绝佳的图标字体库和CSS框架。
- classnames 很方便地实现给元素添加多个类名。
- styled-components 是css-in-js 最热门的一个库,可以为您的样式生成唯一的类名,并能完成css样式书写的嵌套功能。
另外,开发过程中还用到在线接口工具 faskmock
,它可以让你在没有后端程序的情况下能真实地在线模拟ajax请求。
项目实现
项目效果图
<LOLHome/>
英雄联盟主页组件

1. 设计思路
掌上道聚城主页的实现,需采用组件化思想,细化主页为以下组件。
<Header/>
头部组件*<Home/>
精选主页组件(此组件还需细分为其他功能子组件)*<Banners/>
轮播图组件*<ActivitiesInfo/>
活动功能栏组件*<Activities />
活动栏组件*<GameInfo/>
资讯栏组件*<SaleInfo/>
特惠区组件*<Footer/>
底部组件另外,英雄联盟专区
首页也需如<Home/>
组件一样细分功能模块,具体的就不再赘述。
根据需求,细分功能完成项目架构,项目目录如下:

2 实现Header组件
头部组件通过 Link 路由切换实现页面的跳转,并且完成tab转换。使用useLocation() 我们解构出
pathname
,用来判断当前路径是否匹配,如果匹配将通过classnames将active
样式添加上去,使tab
拥有蓝色下划线效果。
import React from "react";
import { HeaderWrapper } from "./style";
import { Link, useLocation } from "react-router-dom";
import classnames from "classnames";
export default function Header() {const { pathname } = useLocation();return (<HeaderWrapper><div className="nav-box"><i className="iconfont icon-saoyisao"></i><Linkto="/"className={classnames({ navitem: true },{ active: pathname == "/home" || pathname == "/" })}>精选</Link><Linkto="/lol"className={classnames({ navitem: true },{ active: pathname == "/lol" })}>英雄联盟</Link><Linkto="/cf"className={classnames({ navitem: true },{ active: pathname == "/cf" })}>CF穿越火线</Link><Link to="/gamelist"><i className="iconfont icon-jiahao icon-right"></i></Link></div></HeaderWrapper>);
}
效果图如下:

3. 实现Home 主页组件
<Home/>
精选主页组件细分为以下5个功能组件
<Banners/>
轮播图组件<ActivitiesInfo/>
活动功能栏组件<Activities />
活动栏组件<GameInfo/>
资讯栏组件<SaleInfo/>
特惠区组件
3.1 <Banners/>
轮播图组件
轮播图我是使用的4.5.0版本的Swiper,轮播图的具体实现有固定的的html结构(小伙伴们可通过前文中的链接进行学习),此处需注意Swiper4轮播设置 autoplay: true后,即默认设置了 disableOnInteraction: true,这将导致用户手动切换swiper之后,轮播图不再自动切换。需要将 autoplay: true 换成 autoplay:{disableOnInteraction: false} ,设置后用户操作swiper之后自动切换不会停止,每次都会重新启动autoplay
import React, { useEffect } from "react";
import { Wrapper } from "./style";
import { Link } from "react-router-dom";
import Swiper from "swiper";
export default function Banners() {useEffect(() => {new Swiper(".btn-banners", {loop: true,// autoplay: true,autoplay: { disableOnInteraction: false },pagination: {el: ".swiper-pagination",},});}, []);return (<Wrapper><div className="btn-banners swiper-container"><div className="swiper-wrapper"><div className="swiper-slide"><Link to="/lol"><img src="https://game.gtimg.cn/images/daojushop/uploads/ad/202206/20220624113503_254960.jpg"alt=""/></Link></div><div className="swiper-slide"><Link to="/lol"><imgsrc="https://game.gtimg.cn/images/daojushop/uploads/ad/202206/20220610102334_849392.jpg"alt=""/></Link></div><div className="swiper-slide"><Link to="/lol"><imgsrc="https://game.gtimg.cn/images/daojushop/uploads/ad/202206/20220610110952_781001.jpg"alt=""/></Link></div><div className="swiper-slide"><Link to="/lol"><imgsrc="https://game.gtimg.cn/images/daojushop/uploads/ad/202206/20220622143800_528795.jpg"alt=""/></Link></div><div className="swiper-slide"><Link to="/lol"><imgsrc="https://game.gtimg.cn/images/daojushop/uploads/ad/202206/20220627010127_701077.jpg"alt=""/></Link></div></div><div className="swiper-pagination"></div></div></Wrapper>);
}
效果图如下:

3.2 <ActivitiesInfo/>
活动功能栏组件,<Activities />
活动栏组件和<GameInfo/>
资讯栏组件
这三个功能组件通过封装axios请求数据函数,从fastmock获取数据。
import axios from 'axios'
export const getActivitiesInfo = () => axios.get('https://www.fastmock.site/mock/cfc81e73033082b126f9464c167c4e75/beers/activiesinfo')
export const getActivities = () => axios.get('https://www.fastmock.site/mock/cfc81e73033082b126f9464c167c4e75/beers/activies')
export const getGameInfo = () => axios.get('https://www.fastmock.site/mock/cfc81e73033082b126f9464c167c4e75/beers/gameinfo')
display:flex
弹性布局可以解决移动端大多的css布局。<ActivitiesInfo/>
组件 和<Activities />
组件和<GameInfo/>
资讯栏组件使用display:flex完成一系列的页面布局,且使用了fontawesome的<i className='fa fa-chevron-circle-right icon-right'></i>
右箭头跳转图标。这里展示部分代码
// <ActivitiesInfo/>
<Wrapper><div className="item">{activitiesinfo.map((item) => {return (<Link to="/" key={`item${item.id}`} className={`item${item.id}`}><img src={item.img} alt="" /><span>{item.title}</span></Link>);})}</div>
</Wrapper>
// <Activities/>
<Wrapper><div className="title"><h3>精彩活动</h3><a href="/home"><i className="fa fa-chevron-circle-right icon-right"></i></a></div><div className="activity">{activities.map((item) => {return (<Linkto="/home"key={Math.random() * 1000 + item.aid}className={item.aid}><img src={item.img} alt="" /><span>{item.desc}</span></Link>);})}</div>
</Wrapper>
// `<GameInfo/>`
<Wrapper><div className="title"><h3>精选资讯</h3><a href="/home"><i className="fa fa-chevron-circle-right icon-right"></i></a></div><div className="game-info">{gameinfo.map((item) => {return (<Linkto="/home"key={`info${item.gid}`}className={`game-info${item.gid}`}><img src={item.img} alt="" /><p>{item.desc}</p><div className="content"><span>{item.author}</span><i className="iconfont icon-guankan"></i><span>{item.guankan}</span><i className="iconfont icon-dianzan"></i><span>{item.dianzan} </span></div></Link>);})}</div>
</Wrapper>
// 设置`<GameInfo/>`组件左边文字显示效果p {// 设置文字超过2行则显示为...省略号word-break: break-all;text-overflow: ellipsis;overflow: hidden;display: -webkit-box;-webkit-box-orient: vertical;-webkit-line-clamp: 2; }
效果如下图

3.3 <SaleInfo/>
特惠区组件
同样使用Swiper,
<SaleInfo/>
组件使用配置swiper的freeMode为true。默认情况下Swiper 每次滑动时只滑动一个Slide,并且会自动贴合Wrapper。开启自由模式后,Swiper 会根据惯性滑动可能不止一格且不会贴合。下面给出swiper的配置
let swiper = null;useEffect(() => {if (swiper) return swiper = new Swiper('.sale',{observer: true, observeParents: true, freeMode: true})},[])
效果图如下:

4 实现LOLHome组件
和Home组件类似,LOLHome组件可复用Home组件中的
<Banners/>
轮播图组件,<GameInfo/>
资讯栏组件和<ActivitiesInfo/>
活动功能栏组件,其中<ActivitiesInfo/>
组件经过修改成拥有轮播图的功能,并添加了<Gift/>
组件
4.1 修改后的<ActivitiesInfo/>
组件
修改后加入为Swiper增加滚动条,并开启自由模式freeMode,下面只给出swiper配置修改部分的代码
useEffect(() => {if (swiper) returnswiper = new Swiper('.item', {observer: true, observeParents: true, freeMode: true,scrollbar: {el: '.swiper-scrollbar',dragSize: 30,},})}, [])
效果图如下:

4.2 <Gift/>
组件
此组件外部同样使用弹性布局felx完成整体布局,内部使用flaot浮动布局完成图片浮动在左边,文字环绕图片居右边。
<Wrapper><div className="title"><h3>我的礼包</h3><a href="/home"><i className="fa fa-chevron-circle-right icon-right"></i></a>{/* <RightOutline /> */}</div><div className="gift-info">{giftinfo.map((item) => {return (<Linkto="/lol"key={`info${item.gid}`}className={`gift-info${item.gid}`}><img src={item.img} alt="" /><span>兑换</span><p>{item.desc}</p><p style={{ color: "red" }}>{item.price}</p></Link>);})}</div></Wrapper>
效果图如下:

5. 实现Footer组件
尾部通过 Link 实现页面级路由的跳转,跳转后通过路由判断完成
active
效果。
<HeaderWrapper><div className="nav-box"><i className="iconfont icon-saoyisao"></i><Linkto="/"className={classnames({ navitem: true },{ active: pathname == "/home" || pathname == "/" })}>精选</Link><Linkto="/lol"className={classnames({ navitem: true },{ active: pathname == "/lol" })}>英雄联盟</Link><Linkto="/cf"className={classnames({ navitem: true },{ active: pathname == "/cf" })}>CF穿越火线</Link><Link to="/gamelist"><i className="iconfont icon-jiahao icon-right"></i></Link></div></HeaderWrapper>
效果图如下:
