前言
ReactReact是用于构建用户界面的JavaScript库,起源于Facebook的内部项目,该公司对市场上所有 JavaScript MVC框架都不满意,决定自行开发一套,用于架设Instagram的网站。
页面效果图
<Home/>AcFun主页组件实现
项目准备
项目初始化
在项目开始之前,我们需要先初始化一个项目,这时候就需要一个好的前端脚手架来帮助我们创建一个react工程目录。这里我使用的是Vite,vite是一种新型的前端构建工具,它具有类型齐全的API,丰富的功能,闪电般快速的热更新。使用起来也很简单只需要输入
npm init @vitejs/app就可以快速创建一个react项目。
项目依赖
在项目初始化完成后,再把项目所需的依赖装好开始动手写项目了。以下是我在项目中所使用到的依赖
"dependencies": {
"antd-mobile": "^5.16.0",
"axios": "^0.27.2",
"classnames": "^2.3.1",
"font-awesome": "^4.7.0"
"prop-types": "^15.8.1",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-router": "^6.3.0",
"react-router-dom": "^6.3.0",
"styled-components": "^5.3.5",
"swiper": "^4.5.0"
},
-
antd-mobile 是一个基于Preact/React/React Native的UI组件库,里面有大量的react UI组件可供使用。
-
Swiper 是一款轻量级的轮播图插件,可以快速地制作出一个轮播图
-
axios 可以在浏览器中发送 XMLHttpRequests,用于请求数据
-
font-awesome Font Awesome 拥有大量的可缩放矢量图标,它可以被定制大小、颜色、阴影以及任何可以用CSS的样式。
-
classnames 能够动态的去修改组件类名
-
styled-components 是css-in-js 最热门的一个库,可以为您的样式生成唯一的类名,并能完成css样式书写的嵌套功能。
-
react-router React Router 是一个基于 React 之上的强大路由库
项目目录结构
| AcFun
| |--node_modules
| |--public
| |--src
| | └──api
| | |--assets
| | |--components
| | |--pages
| | |--routers
| | |--utils
| | |--App.css
| | |--App.jsx
| | |--main.jsx
| | |--vite.config.js
- public 公共文件夹,里面有根据不同手机屏幕大小设置不同的rem,配置适应性
var init = function () {
var clientWidth = document.documentElement.clientWidth || document.body.clientWidth;
if (clientWidth >= 640) {
clientWidth = 640;
}
var fontSize = 20 / 375 * clientWidth;
document.documentElement.style.fontSize = fontSize + "px";
}
init();
window.addEventListener("resize", init);
- assets 目录下放着静态资源,例如字体,图标,样式初始化文件
- components 目录下存放着通用组件
- pages 目录下存放着项目页面组件
- routes 目录下存放着路由配置文件
- utils 目录下存放着一些工具文件
页面构成
AcFun主页的实现,根据react组件化的思想分为以下部分
<Home />AcFun主页组件(由多个子组件组成)
<Search />搜索组件<History />历史记录组件<Banners />轮播图组件<Classify />视频分区组件<Videos />视频展示组件<Footer />底部组件
<Home />组件
<Home />组件由<Search />、<Classify />、<Footer />将页面分为了三个部分.<Classify />组件又是由<Banners />和<Footer />组成。
import React, { useState, useEffect } from 'react'
import Classify from './Classify'
import Search from './Search'
export default function Home() {
return (
<div>
<Search />
<Classify />
</div>
)
}
实现效果如下图所示
<Search />组件
<Search />组件使用了flex弹性布局进行三列式布局,使用justify-content:center将<Search />组件中的三个部分都设置内容居中。
export default function Search() {
return (
<Warpper>
<div className="search_headIcon">
<Link to='/mine'>
<Space block wrap>
<Avatar style={{ '--border-radius': '50%', '--size': '1.5rem' }}
src="https://imgsa.baidu.com/forum/pic/item/8644ebf81a4c510f5a0e17e46a59252dd52aa502.jpg" />
</Space>
</Link>
</div>
<div className="search" >
<SearchBar placeholder='请输入内容' style={{
'--border-radius': '15px',
}} />
</div>
<div className="history">
<HistoryIcon />
</div>
</Warpper>
)
}
实现效果如下图所示
<Banners />组件
组件使用的是Swiper轮播图制作插件,可以快速地制作出一个轮播图。这里为了避免出现bug我使用的是4.5.0版本的Swiper。loop设置为 true 则开启循环(loop)模式,autoplay里设置是否自动播放,以及自动播放时间。
import React, {useEffect} from 'react'
import { Wrapper } from './style'
import Swiper from 'swiper'
export default function SetMeal() {
useEffect(() => {
new Swiper('.home_info_banners', {
loop: true,
autoplay: {
delay: 3000
},
pagination: {
el: '.swiper-pagination'
}
})
}, [])
return (
<Wrapper>
<div className="home_info_img">
<div className="home_info_banners swiper-container">
<div className="swiper-wrapper">
<div className = "swiper-slide">
<p>
<img width="100%" src="https://tx-free-imgs.acfun.cn/BiDW4b7vBt-UZFZN3-JFRRza-iyyyQ3-ymeMne.png?imageslim&imageView2/1/w/690/h/208/format/webp" alt=""/>
</p>
</div>
<div className="swiper-slide">
<p>
<img width="100%" src="https://tx-free-imgs.acfun.cn/IB57dFCpTp-VbiEzu-ziu6ni-y6f67f-eABvY3.png?imageslim&imageView2/1/w/690/h/208/format/webp" alt=""/>
</p>
</div>
<div className="swiper-slide">
<p>
<img width="100%" src="https://tx-free-imgs.acfun.cn/RncVDLIVj0-Frqymy-fQ7Nbq-b63i2y-IFv6N3.png?imageslim&imageView2/1/w/690/h/208/format/webp" alt=""/>
</p>
</div>
<div className="swiper-slide">
<p>
<img width="100%" src="https://tx-free-imgs.acfun.cn/rYR1WMMqHt-FZnEz2-JnIBNb-FfIzEb-UN3yiq.png?imageslim&imageView2/1/w/690/h/208/format/webp" alt=""/>
</p>
</div>
<div className="swiper-slide">
<p>
<img width="100%" src="https://tx-free-imgs.acfun.cn/xY5CkrZXoE-EVRV7f-JRRvEb-zI7nUz-vIbqAz.png?imageslim&imageView2/1/w/690/h/208/format/webp" alt=""/>
</p>
</div>
</div>
<div className="swiper-pagination"></div>
</div>
</div>
</Wrapper>
)
}
实现效果如下图所示
<Classify />组件
<Classify>使用的是antd-mobile 的标签页组件制作的,用<Tabs.Tab>包裹住内容组件就可以实现当前内容分成同层级结构的组,进行内容切换展示。设置defaultActiveKey='2' 进入页面时默认会展示key='2'的内容组
import React, { useEffect, useState } from 'react'
import Banners from './Banners'
import { getBanners } from '@/api/request'
import { getHistory } from '@/api/request'
import { Popup, Tabs, Button } from 'antd-mobile'
import { getVideo } from '@/api/request'
import Video from './Videos'
import { TabWarpper } from './styled'
import AllChannels from '../AllChannels'
// import { lorem } from 'demos'
export default function Classify() {
const [visible1, setVisible1] = useState(false)
const [history, setHistory] = useState([])
const [banners, setBanners] = useState([])
const [video, setvideo] = useState([])
useEffect(() => {
(async () => {
let { data: HistoryData } = await getHistory()
let { data: bannerData } = await getBanners()
let { data: VideoData } = await getVideo()
setBanners(bannerData)
setvideo(VideoData)
setHistory(HistoryData)
})()
}, [])
console.log(video);
return (
<TabWarpper>
<div className="tabs">
<Tabs defaultActiveKey='2'>
<Tabs.Tab title='直播' key='1'>
直播
</Tabs.Tab>
<Tabs.Tab title='精选' key='2'>
<Banners banners={banners} />
<Video video={video} />
</Tabs.Tab>
<Tabs.Tab title='热门' key='3'>
热门
</Tabs.Tab>
<Tabs.Tab title='放声唱' key='4'>
放声唱
</Tabs.Tab>
<Tabs.Tab title='番剧' key='5'>
番剧
</Tabs.Tab>
<Tabs.Tab title='动画' key='6'>
动画
</Tabs.Tab>
<Tabs.Tab title='娱乐' key='7'>
娱乐
</Tabs.Tab>
<Tabs.Tab title='游戏' key='8'>
游戏
</Tabs.Tab>
<Tabs.Tab title='生活' key='9'>
生活
</Tabs.Tab>
</Tabs>
<div
onClick={() => {
setVisible1(true)
}}
>
<div className="allChannels">
<i className='iconfont'></i>
</div>
</div>
<Popup
visible={visible1}
showCloseButton
onClose={() => {
setVisible1(false)
}}
onMaskClick={() => {
setVisible1(false)
}}
bodyStyle={{ height: '100%' }}
>
<AllChannels />
</Popup>
</div>
</TabWarpper>
)
}
实现效果如下图所示
<Videos />组件
在这个组件里通过封装axios请求数据函数,从fastmock获取数据。
export const getVideo = () =>
axios.get('https://www.fastmock.site/mock/ea6be613b6fc7bdeb4349ac17a870bc5/acfun/video')
在<Videos />这里我同样使用的是flex弹性布局进行页面布局,使用flex-wrap: wrap;设置换行,使用:.feed-item:nth-child(2n)选择所有偶数的视频盒子设置一个margin-left,给左右两个盒子设置一个间距。
import React from 'react'
import { Warpper } from './style';
const Videos = ({ video }) => {
return video.map(item => {
return (
<li className='feed-item' key={item.id}>
<div className="">
<img className='feed-item-img' src={item.img} />
<div className="feed-item-info">
<div className="item-img play-icon"></div>
<span className="item-text">{item.goods}</span>
<div className="item-img comment-icon"></div>
<span className="item-text">{item.comment}</span>
{/* <span className="item-text item-time">{item.comment}</span> */}
</div>
<p className='feed-item-title line-cut2'>
{item.title}
</p>
</div>
</li>
)
})
}
实现效果如下图所示
<Footer />组件
在<Footer />组件中通过Link来实现路由跳转,通过跳转后的路由判断设置active效果
import React from 'react'
import { Link, useLocation } from 'react-router-dom'
import { FootWrapper } from './style'
import classnames from 'classnames'
import { isPathPartlyExisted } from '@/utils'
import '@/assets/font/iconfont.css'
export default function Footer() {
const { pathname } = useLocation()
if (isPathPartlyExisted(pathname)) return
return (
<FootWrapper>
<Link to="/video" className={classnames({active:pathname == '/video' || pathname == '/' })}>
<i className="iconfont"></i>
<span>视频</span>
</Link>
<Link to="/find" className={classnames({active:pathname == '/find' || pathname == '/' })}>
<i className="iconfont"></i>
<span>发现</span>
</Link>
<Link to="/dynamic" className={classnames({active:pathname == '/dynamic' || pathname == '/' })}>
<i className="iconfont"></i>
<span>动态</span>
</Link>
<Link to="/mine" className={classnames({active:pathname == '/mine' || pathname == '/' })}>
<i className="iconfont"></i>
<span>我的</span>
</Link>
</FootWrapper>
)
}
实现效果如下图所示
总结
AcFun首页组件设计与实现到此基本完成了,但是由于时间原因还是有许多功能、页面和组件还没有完成,后续会不断的进行完善。希望这篇文章能够帮助到大家。