前言
随着现实需求的不断增加,工程的体量越来越大、项目的功能越来越复杂,组件化、模块化的开发思维也显得愈来愈重要。各个组件之间分工明确,彼此又能够互相沟通,共同完成任务。数据在它们之间相互传递,共享使用。不仅组件之间如此,在与同小组的开发人员合作时,标准化的组件和接口也能使协作效率大幅度提升。
正文
项目概述
基于React Router和axios数据请求的单页响应式项目
涉及知识点
Router路由:可完成单页页面跳转,可以避免页面出现白屏的现象,使用户体验得以提升。axios接口:用于获取页面所需数据。
Swiper轮播图:安装swiper ,轻松完成图片轮播。
fastmock在线数据网站:可以模拟远程数据请求,使用JSON 格式数据。
styeld-components:将css 写在js 中,相较于传统css ,语法上允许选择器嵌套使用,加强了代码的可读性与逻辑性。
值得一提的是,直接获取数据仅在小型项目上使用,大型项目会使用redux 进行数据的获取和管理,可以简单理解为家里堆放杂物和大型物流仓库的区别。
首页展示
包含组件
<Header />:头部组件
<Ads />:首屏广告组件
<Card />:卡片组件
<Club />:俱乐部组件
<Goods />:商品组件
<Culture />:文化组件
<Footer />:底部路由组件
这七个组件按先后顺序挂载,共同构建起了<Home />页面 不同的组件将会放在不同的目录下,目录将会在稍后进行分享,<Footer />作为路由组件,也会放在稍后详细讲解。
目录讲解
项目名: starbucks
api下放置request.js文件,用来进行数据请求。
assets下放置资源文件,一般为本地资源。
components下放置页面通用组件,存放将在多个页面都会使用到的组件,单一页面独有的组件放在自己页面的目录下。
pages下放置页面,不同的页面都存放再此,其中包括<Home />以及其他页面。
Routes下放置路由配置文件,所有的路由都存放在该文件里,并且向外输出一个<RoutesConfig />组件,并在App.jsx中挂载。
对于一个项目来说,合理划分文件是一件极其重要的工作,建立一个条理清晰的目录在开发过程中也是十分必要的,它不仅可以让我们在开发过程中思路更清晰,同时在给小组其他成员查看的时候也更为便捷。总而言之,拥有一个条理清晰的目录百利而无一害。
数据请求与管理
数据存储
一般对于大型项目而言,数据都是后端预先处理好,存储在后台服务器上,前端可以通过接口取用。本项目的数据存放在fastmock 上,数据格式为JSON,可以通过接口地址获取。
数据请求和管理
同样对于大型项目而言,会使用redux 对数据进行请求并存储在自建的store 仓库中,再进行统一的管理,尽管redux 因为过于复杂繁琐而一直受到诟病,但它依旧是目前主流用来做状态管理的JS库。
当前项目并未使用redux 进行数据管理,而是使用axios 来做数据请求,使用useState 来做状态管理。笔者继续更新项目,后续将推出使用redux 进行数据请求管理的版本。
import axios from 'axios'
export const getAds = () =>
axios.get('https://www.fastmock.site/mock/e7acf40b32f914332046f70956593675/starbucks/ad')
export const getGoods = () =>
axios.get('https://www.fastmock.site/mock/e7acf40b32f914332046f70956593675/starbucks/goods')
export const getCard = () =>
axios.get('https://www.fastmock.site/mock/e7acf40b32f914332046f70956593675/starbucks/card')
export const getCulture = () =>
axios.get('https://www.fastmock.site/mock/e7acf40b32f914332046f70956593675/starbucks/culture')
此处是较为粗略的数据请求方法,代码有进一步的优化空间,后续将会持续更新优化,感兴趣的小伙伴可以自行查阅。
路由搭建
在配置好数据请求文件之后,便来到了我们的第二个重点——路由搭建。
import React, { lazy } from 'react'
import { Routes, Route } from 'react-router-dom'
import Home from '../pages/Home'
const Store = lazy(() => import('../pages/Store'))
const Account = lazy(() => import('../pages/Account'))
const Menu = lazy(() => import('../pages/Menu'))
const More = lazy(() => import('../pages/More'))
export default function RoutesConfig() {
return (
<>
<Routes>
<Route path='/' element={<Home />}></Route>
<Route path='/home' element={<Home />}></Route>
<Route path='/store' element={<Store />}></Route>
<Route path='/account' element={<Account />}></Route>
<Route path='/menu' element={<Menu />}></Route>
<Route path='/more' element={<More />}></Route>
</Routes>
</>
)
}
路由组件的搭建其实也十分简单,只是有几个需要注意的点:
1. 在
main.jsx中引入{ BrowserRouter },并将<App />挂载在它下面,如图:
2. 使用 lazy 延迟加载,并在
App.jsx中引入 Susponse, 如图:
<Footer /> 组件开发
1. 在components/Footer 下创建index.jsx 和 style.js 文件,这两个文件分别负责组件和组件的样式, index 代码如下:
import React from 'react'
import { Link, useLocation } from 'react-router-dom'
import { FooterWrapper } from './style'
import classnames from 'classnames'
export default function Footer() {
const { pathname } = useLocation()
return (
<FooterWrapper>
<Link to="/home" className={classnames({active:pathname == '/home' || pathname == '/'})}>
<i className="iconfont icon-zhuye"></i>
<span>主页</span>
</Link>
<Link to="/store" className={classnames({active:pathname == '/store'})}>
<i className="iconfont icon-mendian"></i>
<span>门店</span>
</Link>
<Link to="/account" className={classnames({active:pathname == '/account'})}>
<i className="iconfont icon-zhanghu"></i>
<span>我的账户</span>
</Link>
<Link to="/menu" className={classnames({active:pathname == '/menu'})}>
<i className="iconfont icon-kafei"></i>
<span>菜单</span>
</Link>
<Link to="/more" className={classnames({active:pathname == '/more'})}>
<i className="iconfont icon-gengduo"></i>
<span>更多</span>
</Link>
</FooterWrapper>
)
}
需要注意
icon 图标通过 icon-font 网站获取
active 通过 classnames 设置
2. 在style.js 中设置样式,style 代码如下:
import styled from 'styled-components'
export const FooterWrapper = styled.div`
padding-top: 5px;
width: 100%;
height: 50px;
background: #fff;
position: fixed;
bottom: 0;
left: 0;
display: flex;
color: rgba(0, 0, 0, 0.56);
box-shadow: 0 1px 1px 1px rgb(0 0 0 / 12%);
a {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
font-size: 0.9em;
color: rgba(0, 0, 0, 0.56);
&.active {
color: #00A862;
}
i{
font-size: 1.5em;
}
}
`
使用了styled-components 将css 写入js,让代码逻辑性更强,阅读更清晰。
其他组件开发
剩余组件本质基本一致,在这里就展示其中一个组件作为例子,其他组件读者可以自行思考。
后面会附有代码地址,可自取。
<Ads /> 组件
Ads 是 Home 中的组件,一般直接放在 Home 目录下,在 Home/index.jsx 下引入并挂载 Ads/index.jsx 代码如下:
import React, { useEffect } from 'react'
import { AdsWrapper } from './style'
import { Link } from 'react-router-dom'
import Swiper from 'swiper'
export default function Ads({ads}) {
// console.log(ads, '+++++++')
// const { ad } = ads
// let swiper = null
useEffect(() => {
new Swiper('.btn-ads', {
loop: true,
autoplay: {
delay: 3000
}
})
}, [])
// 有点小bug
const nextImg = () => {
new Swiper('.swiper-container', {
loopPreventsSlide: false,//默认true,阻止
navigation: {
nextEl: '.right-arrow',
},
})
}
return (
<AdsWrapper>
<div className="btn-ads swiper-container">
<div className="swiper-wrapper">
<div className="swiper-slide">
<Link to="/home">
{<img src={ads[0].img} />}
</Link>
</div>
<div className="swiper-slide">
<Link to="/home">
{<img src={ads[1].img} />}
</Link>
</div>
</div>
<div className="right-arrow">
<button className='slick-next slick-arrow' onClick={nextImg}>
Next
</button>
</div>
</div>
</AdsWrapper>
)
}
其中使用了
swiper做轮播图,需要注意使用特定的class类名即可。 这里的代码也可以稍作优化,当数据量较大的时候,可以使用map函数,将数据循环挂载上去。map函数如下:
return items.map(item => {
return (
<Link key={item.id}
className=""
to="/">
// 这里写相应的HTML 代码即可
</Link>
)
})
再此之后便是css 样式设置,可以通过切页面的方式从网页上获取即可,代码如下:
import styled from "styled-components";
export const AdsWrapper = styled.div`
.btn-ads {
overflow: hidden;
img {
width: 100%;
}
.right-arrow {
display: block;
.slick-next {
z-index: 9999;
height: 36px;
width: 36px;
right: 12px;
border: 0;
background: url(https://www-static.chinacdn.starbucks.com.cn/prod/assets/icons/icon-arrow-white.svg) transparent no-repeat center center;
content: '';
font-size: 0;
line-height: 0;
outline: none;
padding : 0;
position: absolute;
top: 50%;
margin-top: -25px;
}
}
}
`
写到此处,此项目也基本可以收尾了,只需要将剩下的组件补齐并挂载上去,小伙伴们可以尝试自己完成,需要代码的小伙伴可以自取。
最后
此项目还有许多功能和组件尚未实现,在上线之后,笔者将会持续更新项目,将项目完善起来,感兴趣的小伙伴可以加关注。最后的最后,如果这篇文章有给你带来一点启发或者帮助的话,请给我一个大大的点赞,谢谢~