React组件设计-仿网易有道翻译主页

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

前言

React组件化开发非常有利于搭建项目,也提高了组件的复用性。

由于频繁使用网易有道翻译这个软件,让我萌生出想要征服ta的冲动。开发过程中遇到了些许问题,页面还有很多功能还未完善,现在只有一个首页,后续功能持续完善中。

前期准备

在组件页面成型之初需要几个开源组件库:

  • axios:它是一个基于promise 的网络请求库,用于获取后端数据(fastmock网站可以让你在没有后端程序的情况下能真实地在线模拟ajax请求),是前端常用的数据请求工具;
  • antd-mobile:由蚂蚁金融团队推出的一个开源的react组件库,这个组件库拥有很多使用的组件;
  • swiper:能实现触屏焦点图、触屏Tab切换、触屏轮播图切换等常用效果。
  • styled-compenonts:真正的css in js,增强 CSS 以对 React 组件系统进行样式设置的结果,具有简单的动态样式轻松维护等优点。

正文

组件展示

image.png

组件设计思路

  • 顶部:用flex布局,方便快捷(一切皆可flex
  • 搜索栏:使用antd-mobile组件库的SearchBar,点击转跳到搜索页面
  • 图标轮播和轮播图:主要使用swiper进行设计,实现自动轮播效果
  • 底部栏:用fixed固定住

组件封装

先对项目进行脚手架的建构(使用vite脚手架,使用起来快速方便)

npm init @viteja/app 
  • src下的目录内容

image.png

  • api:存放与数据相关的链接,组件所有的数据将会在这一个文件夹下的request.js中使用ajax进行数据请求
  • assets:存放静态资源,font、image等
  • components:放置重复使用的组件
  • config:存放页面标题配置
  • modules:配置页面自适应横竖屏
  • pages:各个页面
  • routes:页面的路由

搜索栏

  • 直接使用antd-mobileSearchBar
import React from 'react'
import { SearchBar } from 'antd-mobile'
import { Link } from 'react-router-dom'
import { Wrapper } from './style'

export default function Search() {
  return (
    <Wrapper>
      {/* 点击搜索框跳转搜索页面 */}
      <Link to='/homesearch'>
        <SearchBar className='searchbar' placeholder='请输入内容' showCancelButton />
      </Link>
    </Wrapper>
  )
}

数据请求

  • 前端页面数据的展示不能写死在代码里面,需要数据请求,fastmock则走入了我的视野,在线接口Mock工具fastmock 在线模拟ajax请求(fastmock在没有后端程序的情况下可以实现ajax请求,有需要的小伙伴可以去尝试)
  1. api文件夹下的request.js进行axios数据请求
import axios from 'axios'

export const getBanners = () =>
   axios.get('https://www.fastmock.site/mock/d42a33041be6d65c4184abbecade8d1c/beers/flter')
  1. 主页面useEffect使用async + await实现同步展示数据,拉取到数据后可以将数据作为变量传入相应的组件
...
const [banners, setBanners] = useState([])
const [movies, setMovies] = useState([])
useEffect(() => {
  (async () => {
     let { data: bannerData } = await getBanners()
     let { data: moviesData } = await getMovies()
     setBanners(bannerData)
     setMovies(moviesData)
  })()
})
...
return (
    <div>
      {/* 轮播图 */}
      <Banners banners={banners} />
      ...
      <MoviesList movies={movies}>
        <MoviePlay movies={movies}/>
      </MoviesList>
    </div>
  )

图标轮播

  • 本组件设计使用的是swiper组件库的,如果为了追求方便快捷也可以直接使用antd-mobilswiper
import React, { useEffect } from 'react'
import { BannersWrapper } from './style'
import propTypes from 'prop-types'
import Swiper from 'swiper'
import { Link } from 'react-router-dom'

export default function Banners({ banners }) {
    let swiper = null;
    useEffect(() => {
        if (swiper) {
            return
        }
        swiper = new Swiper('.btn-banners', {
            loop: true,
            pagination: {
                el: '.swiper-pagination'
            }
        })
    }, [])
    const renderBtnBannersPage1 = () => {
        let items = banners.slice(0, 10);
        return items.map(item => {
            return (
                <Link
                    to="/eleme/all"
                    className="swiper-item"
                    key={item.id}
                >
                    <div>
                        <p>
                            <i className={`iconfont ${item.icon_name}`}></i>
                        </p>
                        <span>
                            {item.title}
                        </span>
                    </div>
                </Link>
            )
        })
    }
    const renderBtnBannersPage2 = () => {
        let items = banners.slice(10);
        return items.map(item => {
            return (
                <Link
                    to="/eleme/all"
                    className="swiper-item"
                    key={item.id}
                >
                    <div>
                        <p>
                            <i className={`iconfont ${item.icon_name}`}></i>
                        </p>
                        <span>
                            {item.title}
                        </span>
                    </div>
                </Link>
            )
        })
    }
    return (
        <BannersWrapper>
            {/* 幻灯片 npm i [email protected] */}
            <div className="btn-banners swiper-container">
                <div className="swiper-wrapper">
                    <div className="swiper-slide">
                        {renderBtnBannersPage1()}
                    </div>
                    <div className="swiper-slide">
                        {renderBtnBannersPage2()}
                    </div>
                </div>
                {/* 分页 */}
                <div className="swiper-pagination"></div>
            </div>
        </BannersWrapper>
    )
}
Banners.propTypes = {
    banners: propTypes.array.isRequired
}

  • 通过两个函数renderBtnBannersPage1renderBtnBannersPage2实现20个图标的轮播

  • 通过定义一个变量swiper,实现每次轮播图都定位在第一面

let swiper = null;
useEffect(() => {
   if (swiper) {
       return
   }
    ...
})

注意:useEffect需要传一个空数组当第二个参数,如果不传,组件稍有变化,轮播图就会改变,导致轮播图的自动播放鬼畜,传空数组则表示轮播图的更新什么都不依赖。

  • 引入prop-types规范父子组件之间传值
import propTypes from 'prop-types'
...
Banners.propTypes = {
    banners: propTypes.array.isRequired
}

轮播图

chrome-capture-2022-5-25 (1).gif

  • 代码实现
import React, { useEffect } from 'react'
import { Wrapper } from './style'
import Swiper from 'swiper'

export default function Adverte() {
  useEffect(() => {
    // 幻灯片可能用的很多,取第一个类名 home_info_banners swiper-container
    new Swiper('.home_info_banners', {
      loop: true,
      autoplay: {
        delay: 1000
      }
    })
  },[])
  return (
    <Wrapper>
      <div className="home_info_banners swiper-container">
          <div className="swiper-wrapper">
            <div className="swiper-slide">
              <p>
                <img width="100%" src="https://shared.ydstatic.com/at/2.0.6hg/styles/indexb/banners/banner-2022-june_300.png" alt="" />
              </p>
            </div>
            <div className="swiper-slide">
              <p>
                <img width="100%" src="https://shared.ydstatic.com/at/2.0.6hg/styles/indexb/banners/banner-2020-1_300.jpg" alt="" />
              </p>
            </div>
            <div className="swiper-slide">
              <p>
                <img width="100%" src="https://shared.ydstatic.com/at/2.0.6hg/styles/indexb/banners/banner-2020-3_300.jpg" alt="" />
              </p>
            </div>
            <div className="swiper-slide">
              <p>
                <img width="100%" src="https://shared.ydstatic.com/at/2.0.6hg/styles/indexb/banners/banner-2020-5_300.jpg" alt="" />
              </p>
            </div>
          </div>
          <div className="swiper-pagination"></div>
        </div>
    </Wrapper>
  )
}
  • 设置Swiperautoplaydelay,从而实现自动轮播的效果
new Swiper('.home_info_banners', {
   loop: true,
   autoplay: {
      delay: 1000
   }
})

底部栏

import React, { useState, useEffect } from 'react'
import { Link, useLocation } from 'react-router-dom'
import { FooterWrapper } from './style'
import classnames from 'classnames'

export default function Footer(props) {
  const { pathname } = useLocation()
  
  return (
    <FooterWrapper>
      <Link to="/home" className={classnames({ active: pathname == '/home' || pathname == '/' })}>
        <i className='iconfont icon-shouye1'></i>
        <span>首页</span>
      </Link>
      <Link to="/movie" className={classnames({ active: pathname == '/movie' })}>
        <i className='iconfont icon-shipin'></i>
        <span>视频</span>
      </Link>
      <Link to="/study" className={classnames({ active: pathname == '/study' })}>
        <i className='iconfont icon-w_xuexi'></i>
        <span>学习</span>
      </Link>
      <Link to="/translate" className={classnames({ active: pathname == '/translate' })}>
        <i className='iconfont icon-shuyi_fanyi-105'></i>
        <span>翻译</span>
      </Link>
      <Link to="/vip" className={classnames({ active: pathname == '/vip' })}>
        <i className='iconfont icon-huiyuan'></i>
        <span>会员</span>
      </Link>
    </FooterWrapper>
  )
}
  • 通过classnames动态获取路径

结束

  • 模模糊糊慢慢悠悠简简单单普普通通的网易有道翻译主页面的样子大概模样就出来了,react组件和业务逻辑的一些细节问题还未完善,等学习完antd-mobile页面将会完全展示出来。

项目源代码(gitee)
项目源代码(github)

猜你喜欢

转载自juejin.im/post/7113079208850489358