持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
前言
React是近几年来前端项目开发非常火的一个框架,其背景是Facebook团队的技术支持,市场占有率也很高,对于很多新手来说可能很多人都在犹豫是选react还是vue,如果时间充裕的话还是首选react(虽然有一点难),但不得不说React无论从性能上还是思想上(组件化)都比vue更为先进。下面我带来的react组件化设计希望能够帮助到一些和我一样刚学习react的新手
组件展示
- 剪映原版
- 仿剪映盗版
项目搭建与安装依赖
- 项目初始化 npm init @vitejs/app 选择react (这里我使用的是vite搭建项目,如果大家用其他的更顺手也可以用其他的)
- 安装相关依赖 npm react-router react-router-dom
- 其他相关依赖
- npm i axios 使用ajax请求服务端接口(这里我使用的是fastmock www.fastmock.site/#/ 在线模拟ajax请求)
- npm i classnames 用来合并类名的,存在多个类名变量时,想添加到对应的元素中
- npm i styled-components 使用atyled-components设置页面样式
- npm i swiper 使用swiper轮播图 注意下载的版本是4.5.0的(这里我先前安装的是最新版本的不会用导致轮播图轮播根本动不了)
- 创建文件夹
- 根目录 public 静态资源目录 不需要在src 里面引入
- src下
- api 封装axios接口
- assets 放置静态资源 如图片/字体图标/全局样式
- components 放置通用组件
- pages 单页面存放
- routers 独立配置文件 把组件封装到一起
- 启动项目 npm run dev 默认在3000端口
- 初始化设置
- 不同型号手机端适应页面
-
在public文件夹下创建js子文件夹创建adapter.js文件
这里设置20px为1rem 后面所有的大小将不再使用px,使用rem代替px确保能够适应所有的手机型号
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); ```
-
- 下载相应的图标、图片和对样式reset
- 在assets 下创建font、image和styles文件夹
- 字体图标就在iconfont上去下载自己想要的或者使用font-awesome组件库
- image 可以下载需要的图标或者等会在fastmock传入数据(可要可不要)
- reset 就是对页面进行样式重置(这里代码太长,推荐随便到掘金或者github上找一个比较全的样式重置就好了)
- 在assets 下创建font、image和styles文件夹
- 导入数据
- api文件夹下创建request.js文件 封装axios 首先在fastmock上创建好数据(这里是我的fastmock地址)
import axios from 'axios' export const getCourse=()=> axios.get('https://www.fastmock.site/mock/758a51ea7ac094acd3f3de097e2da039/beers/course')
- 不同型号手机端适应页面
页面设计思路
- 页面级别组件
<Home/>
他是其他页面的父组件<Header/>
头部组件每个页面都需要使用<Footer/>
尾部组件tabbar 可以切换页面<HomeDetailNav/>
由这个组件切换到二级页面
- 精品课二级页面
<Banner/>
录播图组件<Lists />
列表展示组件<Course/>
课程展示页面
项目页面详细设计
路由配置
这个项目的路由配置有点多,会用到二级路由,所以我们将所有的路由单独封装到routers文件夹下 这里使用lazy懒加载,提升主页打开的速度
我主要写的是Home和Jingpin组件,其他页面主要是占位,在后续会完成其他页面的制作
import { useState, lazy } from 'react'
import { Routes, Route, Link} from 'react-router-dom'
import Home from '../pages/Home'
const Course = lazy(() => import('../pages/Course'))
const Jianji = lazy(() => import('../pages/JianJi'))
const Mine = lazy(() => import('../pages/Mine'))
const News = lazy(() => import('../pages/News'))
const TongKuan = lazy(() => import('../pages/Tongkuan'))
const Jianpai = lazy(() => import('../pages/Jianpai'))
const Mianfei = lazy(() => import('../pages/Mianfei'))
const Tuijian = lazy(() => import('../pages/Tuijian'))
const Zhibo = lazy(() => import('../pages/Zhibo'))
const Jingpin = lazy(() => import('../pages/Jingpin'))
// Routes 不能和react-router-dom 一样
const RoutesConfig = () => {
return (
<Routes>
<Route path='/' element={<Home/>}>
<Route path='/' element={<Jingpin/>}/>
</Route>
<Route path='/jianji' element={<Jianji/>}></Route>
<Route path='/tongkuan' element={<TongKuan/>}></Route>
<Route path='/home' element={<Home/>}>
{/* 二级路由 */}
<Route path='/home/tuijian' element={<Tuijian/>}/>
<Route path='/home/mianfei' element={<Mianfei/>}/>
<Route path='/home/jingpin' element={<Jingpin/>}/>
<Route path='/home/' element={<Jingpin/>}/>
<Route path='/home/zhibo' element={<Zhibo/>}></Route>
<Route path='/home/jianpai' element={<Jianpai/>}/>
</Route>
<Route path='/news' element={<News/>}></Route>
<Route path='/mine' element={<Mine/>}></Route>
<Route path='/course/:id' element={<Course/>}></Route>
</Routes>
)
}
export default RoutesConfig
App.jsx
在App.jsx中代码很简单,由Header组件和Footer组件 还有页面二级路由组件构成
这里使用Suspense 等待异步组件时渲染一些额外内容,让应用有更好的用户体验,在页面还没有加载出来时提示loading加载中
import { useState,Suspense } from 'react'
import './App.css'
import RoutesConfig from './routers'
import Header from './components/Header'
import Footer from './components/Footer'
function App() {
return (
<div className="App">
<Header />
<Suspense fallback={<div>loading...</div>}>
<RoutesConfig />
</Suspense>
<Footer />
</div>
)
}
export default App
Header组件展示
- 头部做的是一个输入框和一个div盒子放置,引入了字体图标iconfont,这里只是简单的切页面,具体样式就不展示了
<Header>
<div className="inphead">
<i className='iconfont icon-sousuo sousuo'></i>
<input type="text" placeholder='好友组团卡点照' className='inp' />
<div className='stu'>
<i className='iconfont icon-24gl-playSquare'></i>
<p>学习中心</p>
</div>
</div>
</Header>
Footer组件展示
- 这里使用路由NavLink实现对页面的跳转 当跳转到当前底部图标和文字时会高亮显示
export default function Footer() {
const {pathname}=useLocation()
return (
<FooterWrapper>
<NavLink to="/jianji" className={classnames({active:pathname=='/jianji'})}>
<i className='iconfont icon-jianji'></i>
<span>剪辑</span>
</NavLink>
<NavLink to="/tongkuan" className={classnames({active:pathname=='/tongkuan'})}>
<i className='iconfont icon-shipin'></i>
<span>剪同款</span>
</NavLink>
<NavLink to="/home" className={classnames({active:pathname=='/home' || pathname=='/'})}>
<i className='iconfont icon-wodekecheng'></i>
<span>创作课堂</span>
</NavLink>
<NavLink to="/news" className={classnames({active:pathname=='/news'})}>
<i className='iconfont icon-xiaoxi'></i>
<span>消息</span>
</NavLink>
<NavLink to="/mine" className={classnames({active:pathname=='/mine'})}>
<i className='iconfont icon-31wode'></i>
<span>我的</span>
</NavLink>
</FooterWrapper>
)
}
二级路由
在<Home/>
组件中引入<HomeDetailNav/>
二级组件和使用<Outlet />
对下面的组件进行占位 下面主要介绍<HomeDetailNav/>
组件
export default function HomeDetailNav(){
let homeNavs = [
{ id: 1, desc: '推荐', path: '/tuijian'},
{ id: 2, desc: '免费专区', path: '/mianfei'},
{ id: 3, desc: '精品课', path: '/jingpin'},
{ id: 4, desc: '直播', path: '/zhibo'},
{ id: 5, desc: '拍剪教学', path: '/jianpai'},
]
return(
<Wrapper >
<div className="navbar swiper-container">
<div className="nav-box swiper-wrapper">
{
homeNavs.map((item, index) => {
return (
<NavLink
index={index}
to={`/home${item.path}`}
key={item.id}
className="nav-item swiper-slide"
>
{item.desc}
</NavLink>
)
})
}
</div>
</div>
</Wrapper>
)
}
这里使用map循环输出二级路由的地址 点击不同的文字可以到不同的页面 且路由有高亮显示
精品课页面组件介绍
轮播图设计
<Banner/>
组件这里使用swiper组件库对轮播图进行设置 使用useEffect钩子对轮播图进行渲染,轮播图设置3秒自动轮播
export default function Banner() {
useEffect(() => {
new Swiper('.home_info_banner',{
loop:true,
autoplay: {
delay:3000
}
})
},[])
return (
<Wrapper>
<div className="home_info_img">
<div className="home_info_banner swiper-container">
<div className="swiper-wrapper">
<div className="swiper-slide">
<img src={img1} alt="" width="100%" />
</div>
<div className="swiper-slide">
<img src={img2} alt="" width="100%" />
</div>
</div>
<div className="swiper-pagination"></div>
</div>
</div>
</Wrapper>
)
}
列表展示
<Lists/>
这个组件很简单就是使用一级路由直接跳转
<Link to="/kecheng">
<i className='iconfont icon-wodekecheng'></i>
<span>全部课程</span>
</Link>
...
课程组件
<Course/>
课程组件 负责课程的展示 这里由fastmock传入数据
布局使用弹性布局加absolute定位
<Wrapper>
{/* <h1>超值限时抢</h1> */}
{
course&&course.map(
item=>(
<div className="course-flex">
<Link
className='course-List'
to={`/course/${item.id}`}
key={item.id}
>
<div className="course-Box">
<div className="course-Img">
<img src={item.img} alt="" />
<div className="get-Course">
<p>{item.coursenum}节课</p>
</div>
</div>
<div className="course-Content">
<div className="course_header">{item.header}</div>
<div className="course_name">
<span>{item.name}</span> <p>|</p>
<span>{item.people}</span>
</div>
<div className="course_price">
<span className='course_price_now'><p>¥</p>{item.price}</span>
<span className='course_price_old'><p>¥</p>{item.low_price}</span>
</div>
</div>
</div>
</Link>
</div>
)
)
}
</Wrapper>
这里我没找到合适的图片就都用了相同的图片,后续如果找到了会修改的
最后
项目待改进地方
这个组件我做的比较简单,就是简单的切页面和路由转换,还有很多地方还没有做出来,后续会继续更新,完成这个项目,请持续关注
后续项目待完善地方
- 完善换一换,可以切换到下一页
- 点进课程可以看到课程内容
- 再写几个页面
项目地址
感谢看到最后,如果觉得文章还不错的话点个赞再走吧 (^▽^)