• React组件开发-仿哔哩哔哩移动端首页


    前言

    什么是React?

    React是一个简单的javascript UI库,用于构建高效、快速的用户界面。它是一个轻量级库,因此很受欢迎。它遵循组件_设计模式声明式编程范式_ 和 函数式编程 概念,使前端应用程序更高效。它使用虚拟DOM来有效地操作DOM。它遵循从高阶组件到低阶组件的单向数据流

    React 中一切都是组件。 我们通常将应用程序的整个逻辑分解为小的单个部分。 我们将每个单独的部分称为组件。 通常,组件是一个javascript函数,它接受输入,处理它并返回在UI中呈现的React元素。

    一、项目准备

    预期效果:

    实际效果:GitHub Pages预览

    1.项目初始化

    想要搭建一个好的项目,称手的工具必不可少。

    在我们已经安装好 vscode 和 node 的前提下,我们需要在终端环境下初始化一个项目,项目我们取名为 bilibili-page:

    这里我们使用 vite 进行搭建,它相比于 webpack 更快

    npm init @vitejs/app bilibili-page 
    
    • 1

    执行该命令后我们选择 react:

    因为这个项目使用的是 js 语法,所以我们选择 react,如果对 ts 语法比较熟悉的小伙伴也可以选择 react-ts。

    选择好以后,我们依次执行如下命令: 进入项目目录,并使用 npm i 或 (npm install) 安装 react。

    cd bilibili-page
    npm install 
    
    • 1
    • 2

    2.安装依赖

    除此之外,我们还需要安装以下依赖,使用命令(npm i xxx)默认安装最新版本:

    1.antd-mobile:Ant Design Mobile 简称 antd-mobile,是 Ant Design 的移动规范的 React 实现,是一个基于Preact/React/React Native的UI组件库。我们用它来实现轮播图效果。2.axios:axios 是一个基于 promise 的网络请求库,用于获取后端数据,是前端常用的数据请求工具。3.react-routerreact-router-dom:react-router 用来实现了路由的核心功能,而 react-router-dom 基于 react-router,加入了在浏览器运行环境下的一些功能。4.weuireact-weui:weui 是一套同微信原生视觉体验一致的基础样式库,react-weui 是微信提供的前端 UI 库,在 react 中我们可以直接拿来使用。这里我们用来实现 loading 状态,优化用户体验。5.styled-components:安装这一依赖,我们能够实现 css in js,是 react 中设计组件很好用的一种方法。6.classnames:它是用来合并类名的,存在多个类名变量时,想添加到对应的元素中,采用 classnames 非常方便。3. 在线接口

    另外,我还使用了在线接口工具 faskmockfastmock 可以让你在没有后端程序的情况下真实地在线模拟 ajax 请求,实现前后端分离,这里我用 fatmock 实现项目初期纯前端的效果演示。

    二、搭建项目

    1.主要功能

    1.路由跳转及tab切换,包括一级路由和二级路由,当用户进入到首页时,默认跳转到首页的推荐页面。2.轮播图的实现,可自动循环播放以及手动滑动。3.loading状态,由于视频数据是从远程请求过来的,需要一些等待时间,加上了 loading 状态后,填补了页面的空白状态,优化用户体验。在该项目中,我主要用来实现 bilibili 首页页面的快速搭建,具体的实现功能后面我会一一去实现,例如下拉刷新、上滑加载、点击视频详情页功能,以及搜索、点赞、投币等功能。

    2.设计思路

    根据需求,我们需要在 src 目录下新建以下文件夹:

    1.在 api 文件夹下,我们将数据请求封装在 api 目录下方便管理,在 api 下的 request.js 文件中,我们定义了一个接口 getVideos 用来获取视频信息数据。

    import axios from 'axios'
    
    export const getVideos = () => 
     axios.get('https://www.fastmock.site/mock/059647e88be0d33ef58d6ab4bf009dd9/bilibili/videolist') 
    
    • 1
    • 2
    • 3
    • 4

    2.在 assets 文件夹用来存放静态资源文件,如:本地图片、下载的图标字体和全局的css样式,用来做 reset,样式重置。3.components 文件夹用来存放组件,主要有底部路由(一级路由)、头部搜索框以及导航路由(二级路由)。4.config 目录下的 index,js,用来对页面进行标题配置:```
    // 页面标题配置
    export const pageTitle = {‘/’: ‘首页’,‘/home’: ‘首页’,‘/dynamic’: ‘动态’,‘/vip’: ‘会员购’,‘/mine’: ‘我的’
    }

    
    5.modules 目录下的 rem.js 用来对进行移动端的适配,字体大小随着机型的不同进行变化。
    
    
    • 1
    • 2
    • 3

    document.documentElement.style.fontSize = document.documentElement.clientWidth / 3.75 + ‘px’

    window.onresize = function() {document.documentElement.style.fontSize = document.documentElement.clientWidth / 3.75 + “px”
    }

    
    6.pages 目录,用来存放单页面级别组件,例如:主页(主页下的多个页面)、动态、会员购和我的。7.routes 目录,这里我们将路由抽离出来放到这个文件夹下,便于管理。我们希望有些比较大的组件,只有在此组件被加载时,内部资源才被引用,因此我们引入 lazy 实现懒加载。由于我们在 `vite.config.js` 中添加了如下配置,引入了 nodejs 的 `path` 模块,使用`path.resolve` 来处理绝对路径:```
    import { defineConfig } from 'vite'
    import react from '@vitejs/plugin-react'
    import path from 'path'
    
    export default defineConfig({plugins: [react()],resolve: {alias: {'@': path.resolve(__dirname, 'src')}}
    }) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    完成配置之后,就可以用 @ 来替代 src

    import { lazy } from 'react'
    import { Routes, Route } from 'react-router-dom'
    import Home from '@/pages/Home'// 首页
    const Dynamic = lazy(() => import('@/pages/Dynamic'))// 动态
    const Vip = lazy(() => import('@/pages/Vip'))// 会员购
    const Mine = lazy(() => import('@/pages/Mine'))// 我的
    
    const Live = lazy(() => import('@/pages/Home/Live'))// 直播
    const Recommend = lazy(() => import('@/pages/Home/Recommend'))// 推荐
    const Hot = lazy(() => import('@/pages/Home/Hot'))// 热门
    const Animation = lazy(() => import('@/pages/Home/Animation'))// 动画
    const Movies = lazy(() => import('@/pages/Home/Movies'))// 影视
    const Campus = lazy(() => import('@/pages/Home/Campus'))// 校园
    
    const RoutesConfig = () => {return (}>} >{/* 二级路由 */}} />} />} />} />} />} />}>}>}>)
    }
    
    export default RoutesConfig 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    8.utils 目录:在这个目录下的 index.js 中,我新建了一个 isPathPartlyExisted 函数,用来判断路径中是否存在 '/home'

    /**
     * @author codinglin
     * @func 根据path判断是否在数组配置中
     * @params {path string}
     * @return boolean 
     */
    export const isPathPartlyExisted = (path) => {let pathRes = path.split('/')if (pathRes[1] && pathRes[1] === 'home') return truereturn false
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    三、功能的具体实现

    1.路由跳转及tab切换

    1.1 一级路由

    我们从 react-router-dom 中解构出 LinkuseLocation,Link 实现页面的跳转,从 useLocation 中我们解构出 pathname,用来判断当前路径是否匹配,如果匹配就将 active 样式添加上去。例如首页,当路径为 '/','/home'或者'/home/...'时,底部的首页图标高亮。

    import React from 'react'
    import { Link, useLocation } from 'react-router-dom'
    import { FooterWrapper } from './style'
    import classnames from 'classnames'
    import { isPathPartlyExisted } from '@/utils'
    
    export default function Footer(props) {const { pathname } = useLocation()return (首页...)
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    效果如下图:

    1.2 二级路由

    我们首先将二级路由放到一个数组中,包括id、desc和path,然后我们使用 map 方法代替forEach循环,来遍历并对每个值应用转换函数,通过点击我们既想要跳转又想要添加样式,我们使用了比 Link 更高级的 NavLink

    import React from 'react'
    import { Wrapper } from './style'
    import { NavLink } from 'react-router-dom'
    
    export default function HomeNav() {let homeNavs = [{ id: 1, desc: '直播', path: '/live'},{ id: 2, desc: '推荐', path: '/recommend'},{ id: 3, desc: '热门', path: '/hot'},{ id: 4, desc: '动画', path: '/animation'},{ id: 5, desc: '影视', path: '/movies'},{ id: 6, desc: '校园', path: '/campus'}]return (
    {homeNavs.map((item, index) => {return ({item.desc})})}
    ) }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.轮播图的实现

    轮播图我使用了 antd-mobile 中的 SwiperToast,具体大家可以参考:mobile.ant.design/zh/componen… 在 Swiper 组件中加入 autoplayloop,使得轮播图既能自动播放又能循环。Toast.show 在你触发了点击事件之后,能够弹出相关信息。

    import React from 'react'
    import './style.css'
    import { Swiper, Toast } from 'antd-mobile'
    import classnames from 'classnames'
    
    export default function SetMovie() {const images = ['https://dogefs.s3.ladydaily.com/~/source/wallhaven/small/o3/o3wel5.jpg', 'https://dogefs.s3.ladydaily.com/~/source/wallhaven/small/28/28wqwg.jpg', 'https://dogefs.s3.ladydaily.com/~/source/wallhaven/small/x8/x8kwd3.jpg', 'https://dogefs.s3.ladydaily.com/~/source/wallhaven/small/o3/o3edql.jpg']const items = images.map((image, index) => ({backgroundImage: `url(${image})`}}onClick={() => {Toast.show(`你点击了电影 ${index + 1}`)}}>
    ))return (
    {items}
    ) }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    效果如下图:

    3.设置 loading 状态

    首先我们从引入的 WeUI 中解构出 Toast,然后再给 loading 设置默认状态为 true,当从远程请求到数据之后,setLoading(false),状态消失,数据显示,优化用户体验。

    import React, { useEffect, useState } from 'react'
    import SetMovie from '../SetMovie'
    import { Wrapper } from './style'
    import { getVideos } from '@/api/request'
    import { Link } from 'react-router-dom'
    import WeUI from 'react-weui'
    
    const {Toast
    } = WeUI;
    
    export default function Recommend() {const [loading, setLoading] = useState(true)const [videos, setVideos] = useState([])useEffect(() => {(async() => {let { data } = await getVideos()setVideos([...data])setLoading(false)})()}, [])return (<> 加载中......)
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    效果如下图:

    4.完成数据渲染

    从远程请求过来的数据通过唯一 key 值 video.id,通过 map 分配给 video,完成数据的渲染。

    ...
    export default function Recommend() {const [loading, setLoading] = useState(true)const [videos, setVideos] = useState([])useEffect(() => {(async() => {let { data } = await getVideos()setVideos([...data])setLoading(false)})()}, [])return (<> 加载中...{videos && videos.map(video => (
    {video.bofang}{video.pinglun}{video.time}
    {video.title}
    {video.up}
    ))}
    ) }
    • 1
    • 2
    • 3

    单个视频 demo 效果如下图:

    四、总结

    当我们在自己动手写第一个React项目(或其他项目)时,建议大家把思路理清楚,不要急于写样式,把大致的模板建好,然后逐个组件进行完善,如果大家想要快速搭建项目,可以直接使用我在 github 上传的一个 React 的单页应用模板,如果你对 git 较为熟悉,建议直接在终端 git clone,然后再 npm i,不会使用 git 的小伙伴也可以直接下载,模板地址:React单页应用模板项目地址:哔哩哔哩移动端首页预览效果:GitHub Pages

  • 相关阅读:
    从进程,线程去了解浏览器内部的流程原理
    IDEA 自动生成序列号
    古典密码小记
    【Maxent物种分布模型】气候变化对响尾蛇地理分布的影响
    中移链共识机制介绍
    目标检测中几个算法的正负样本划分策略
    Python算法练习 10.14
    labelme标注信息统计及使用方法
    随笔-北漂之旅启程
    蓝桥杯 第 2 场算法双周赛 第4题 通关【算法赛】c++ 优先队列 + 小根堆 详解注释版
  • 原文地址:https://blog.csdn.net/web22050702/article/details/126373825