theme: Chinese-red
前言
React介绍
React 是一个用于构建用户界面的JavaScript库,主要用于构建UI。React拥有较高的性能以及很多特点,比如JSX(这是JavaScript的扩展语法)和组件。组件化思想在React中的应用十分广泛,通过构造组件可以将代码很好的得到复用。此外,React 的声明式设计和通过对DOM的模拟,给它带来了高效灵活的特点。
项目描述
小红书作为一款定位于社交 && 电商 的App,近些年来越来越受到更多人的关注,它的用户群体也在不断扩大。作为一名小红书的使用者,以及结合近期正在学习React 相关知识,便有了使用React 仿小红书首页的想法。此项目实现小红书首页的基本页面,接下来就带你进入项目展示及实战。
成品展示
话不多说,直接上图
搞错了,重来
项目实战
开始阶段
在Vscode中创建一个React 项目,创建项目很多种方式,使用官方脚手架create-react-app或者Webpack创建,在此我使用的是Vitejs方式创建,这种方式会比其他方式快很多。如果大家对此感兴趣的话,后面会写一篇关于Vite和Webpack的文章,大家的点赞与支持,是我不断创作的动力来源 ღ( ´・ᴗ・` )
文件夹配置
项目创建好后,接下来就是将相关文件中的代码以及相关多余的文件清理干净,然后创建好项目中最基本的文件夹。
- api文件夹 用于网络请求方面
- assets文件夹 用于存放部分图标图片等静态资源
- pages文件夹 用于页面
- commpents文件夹 用于可复用的组件
更详细的文件目录如下:
依赖安装
相关文件夹配置完成后,可以开始安装项目需要的依赖
- npm i axios 用于获取后端数据
- npm i react-router react-router-dom 用于项目路由跳转
- npm i styled-components 用于实现Css in JS
- npm i antd-mobile 一个好用的UI组件库
- npm i classname 用于添加多个类名
安装成功后如下所示:
组件及页面分析
路由介绍
在以上工作完成后,接下来就是搭建相关路由了,路由在项目中是十分重要的。Route
【路由】可以理解为现实中路由器后面的接口,Routes
【路由器】可以理解为现实的路由器用来管理路由。在此项目中,我设置了如下几个路由:
const Home = lazy(() => import('./pages/Home'))
const Mine = lazy(() => import('./pages/Mine'))
const Message = lazy(() => import('./pages/Message'))
const Shop = lazy(() => import('./pages/Shop'))
const Choose = lazy(() => import('./pages/Choose'))
<Routes>
<Route path="/" element={<Home/>}></Route>
<Route path="/home" element={<Home/>}></Route>
<Route path="/mine" element={<Mine/>}></Route>
<Route path="/message" element={<Message/>}></Route>
<Route path="/shop" element={<Shop/>}></Route>
<Route path='/choose' element={<Choose />}></Route>
</Routes>
路由搭建需要注意以下几点:
- 在main.jsx 中引入{BrowserRouter}
方能正常使用
- 使用路由懒加载,可提升加载速度,需引入{Suspense}
若出现某个页面跳转后不显示底部导航栏,可以通过简单的条件判断和{useLocation}
接受传来的值来实现。比如如下所示:
import {useLocation} from 'react-router-dom'
const {pathname}=useLocation()
if (pathname == '/choose') return
数据请求
在此项目中采用的是fastmock来请求与管理数据,然后在api文件夹中创建request.js来获取数据。后续在相关页面中引入即可。
```
import axios from 'axios'
export const Getlist = () =>
axios.get
('https://www.fastmock.site/mock/33e7fec4e60b54344eaa2c59a55b379d/red_book/red_book/list')
export const Getfood = () =>
axios.get
('https://www.fastmock.site/mock/33e7fec4e60b54344eaa2c59a55b379d/red_book/red_book/food')
```
底部导航栏
底部导航栏通过路由的切换来实现。上面已经讲过一些路由知识,在此就不做过多描述。在components文件夹中创建Footer组件,用于底部导航栏。在此注意点击中间加号跳转后底部导航栏不会一起跳转,因此需要做相关判断来实现跳转新页面,源码如下:
Footer.jsx
```
import React from 'react'
import {Link,useLocation} from 'react-router-dom'
import {FooterWrapper} from './style'
import classnames from 'classnames'
import { Badge } from 'antd-mobile'
function Footer(props) {
const {pathname}=useLocation()
if (pathname == '/choose') return
return (
首页
商城
消息
我
)
}
export default Footer
```
主页面引入Footer,部分源码如下:
```
<>
}>
}>
}>
}>
}>
}>
```
顶部导航栏
顶部导航栏通过Tab栏来实现内容的切换。此处有两处导航栏,在发现页面下也有一处导航栏,因此需要实现两次导航栏效果。两次导航栏实现十分简单,源码如下:
Home .jsx
```
import React, { useState,useEffect } from "react";
import { Tabs,Popup } from 'antd-mobile'
import Care from './Care'
import Find from './Find'
import City from './City'
import Search from '../Search'
import Daily from '../Daily'
import './style.css'
const Home = () =>{
const [visible1, setVisible1] = useState(false)
const [visible2, setVisible2] = useState(false)
return(
<span
onClick={() => {
setVisible1(true)
}}
>
<i className="iconfont icon-yuzhouxingqiu-12"></i>
</span>
<Popup
visible={visible1}
onMaskClick={() => {
setVisible1(false)
}}
position='left'
bodyStyle={{ height: '100%',width:'100%' }}
>
{<Daily />}
</Popup>
<span
onClick={() => {
setVisible2(true)
}}
>
<i className="iconfont icon-sousuo"></i>
</span>
<Popup
visible={visible2}
onMaskClick={() => {
setVisible2(false)
}}
position='right'
bodyStyle={{ height: '100%',width:'100%' }}
>
{<Search />}
</Popup>
</div>
)
}
export default Home
```
Find.jsx
```
import React, { useState,useEffect } from "react";
import { Tabs,Collapse,SpinLoading } from 'antd-mobile'
import Recommend from '../../Recommend'
import Video from '../../Video'
import Liver from '../../Liver'
import Food from '../../Food'
import { Getlist } from '../../../api/request'
import { Getfood } from "../../../api/request"
import './style.css'
const Find = () =>{
const [list,SetList] = useState([])
useEffect(()=>{
(async() => {
let { data } = await Getlist()
SetList(data)
})()
},[])
const [food,SetFood] = useState([])
useEffect(()=>{
(async() => {
let { data } = await Getfood()
SetFood(data)
})()
},[])
return(
<div>
<Tabs defaultActiveKey='1' style={
{'--title-font-size':'15px','--active-line-height':'0px',
'--active-title-color':'#ed2b43'}}>
<Tabs.Tab title='推荐' key='1'>
{list.map(item=>(
<Recommend source={item} key={item.id}/>
))
}
</Tabs.Tab>
<Tabs.Tab title='视频' key='2'>
<Video/>
</Tabs.Tab>
<Tabs.Tab title='直播' key='3'>
<Liver />
</Tabs.Tab>
<Tabs.Tab title='美食' key='4'>
{food.map(item=>(
<Food entry={item} key={item.id}/>
))
}
</Tabs.Tab>
<Tabs.Tab title='知识科普' key='5'>
.........
</Tabs.Tab>
.........
此后代码大致相同,便在此处省略
</Tabs>
<SpinLoading style={{'--color':'#ed2b43'}} className="load"/>
</div>
)
}
export default Find
```
本项目中Tab栏使用的是antd-mobile里封装好的Tab栏,参考antd-mobile里的文档,然后引入相关组件,即可使用。虽然用起来比较方便,但是因为是已经封装好的组件,对于一些样式的修改会比较局限。若要修改样式,可以参考文档对其相关样式进行修改或者加入className类名重新定义相关样式。
下面附上相关网址,感兴趣的朋友可以参考相关文档,这是一个不错的UI组件库。
Ant Design Mobile - Ant Design Mobile
首页内容
首页是此项目的主页面,进入此页面后,可以实现页面跳转等相关功能,页面上的数据存储在fastmock中。然后在Recommend和Food页面中通过遍历将所需数据获取在页面上。此外,跳转到搜索页面,里面采用的搜索框也是antd-mobile里封装好的Search,对样式稍作修改然后直接调用即可。对于图标,采用iconfont上的图标足以满足大部分需求。在assets文件夹中引入提前加入好图标的font文件夹,此后也能方便地在font文件夹中修改图标样式。部分源码如下:
Search.jsx
```
import React from "react";
import { NavBar,SearchBar } from "antd-mobile";
import {Link} from "react-router-dom"
import './style.css'
const Search = () =>{
return(
}}
/>
搜索
)
}
export default Search
```
Recommend.jsx
```
import React from "react";
import './style.css'
const Recommend = ({source}) =>{
const {img,content,author,icon} = source
return(
{content}
{author}
)
}
export default Recommend
```
Food.jsx
```
import React from "react";
import './style.css'
const Food = ({entry}) =>{
const {img,content,author,icon} = entry
return(
{content}
{author}
)
}
export default Food
```
总结
这是初步搭建的项目,里面有许多功能以及相关组件还未完全实现,后期随着对React 知识的深入学习与掌握,会将此项目加以修改和完善,并继续以此发文。如有优化或者错误的地方,欢迎各位大佬在评论区里指出。最后,如果此篇文章能给大家带来帮助的话,谢谢各位的点赞 ღ( ´・ᴗ・` )