天天看点

React.js hooks 练习案例-网易云音乐【day2】成果展示

成果展示

因为之前没玩把握,所以先玩了几天再决定记录下,现在已经完成首页的60%左右的功能。还原度95%左右。

等项目完成得差不多我会上传到 git 给小伙伴参考。

React.js hooks 练习案例-网易云音乐【day2】成果展示

下半部分还有2个模块没完成。

React.js hooks 练习案例-网易云音乐【day2】成果展示

共享组件设计方面

把大多数页面都用到布局抽离成共享组件:

头部组件

React.js hooks 练习案例-网易云音乐【day2】成果展示
import React, { memo } from 'react'; // 导入React功能组件
import { headerLinks } from '@/common/local-data'; // 导入页面内部数据
import { NavLink }  from 'react-router-dom'; // 导入路由组件
import { Input } from 'antd'; // 导入蚂蚁金服输入框组件
import { SearchOutlined } from '@ant-design/icons'; // 导入蚂蚁金服图标
import { HeaderWrapper, HeaderLeft, HeaderRight } from './style'; // 导入js-css组件

// 顶部导航
export default memo(function RTAppHeader() {

    // UI布局逻辑代码
    const showSelectItem = (item,index) => {
        if(index < 3){
            return ( 
            <NavLink to={item.link}>
                {item.title}
                <i className="sprite_01 icon"></i>
            </NavLink> 
            )
        } else {
            return ( <a href={item.link}>{item.title}</a> )
        }
    }

    // UI布局模块
    return (
        <HeaderWrapper>
            <div className="content wrap-v1">
                <HeaderLeft>
                    <a href="#/" target="_blank" rel="external nofollow"  className="logo sprite_01">网易云音乐</a>
                    <div className="select-list">
                        {
                            headerLinks.map((item,index) => {
                                return (
                                    <div key = {item.title} className="select-item">
                                        {showSelectItem(item,index)}
                                    </div>
                                )
                            })
                        }
                    </div>
                </HeaderLeft>
                <HeaderRight>
                    <Input className="search" placeholder="音乐/视频/电台/用户" prefix={<SearchOutlined/>} />
                    <div className="center" >创作者中心</div>
                    <div >登录</div>
                </HeaderRight>
            </div>
            <div className="divider"></div>
        </HeaderWrapper>
    )
})
           

底部组件

React.js hooks 练习案例-网易云音乐【day2】成果展示
import React, { memo, Fragment } from 'react'; // 引入React功能组件
import { footerLinks, footerImages } from '@/common/local-data'; // 引入数据组件
import { AppFooterWrapper, FooterLeft, FooterRight, } from './style'; // 引入js-css组件

// 底部导航
export default memo(function RTAppFooter() {
  return (
  <AppFooterWrapper>
    <div className="wrap-v2 content">
      <FooterLeft className="left">
        <div className="link">
          {
            footerLinks.map(item => {
              return (
                <Fragment key={item.title}>
                  <a href={item.link} target="_blank" rel="noopener noreferrer">{item.title}</a>
                  <span className="line">|</span>
                </Fragment>
              )
            })
          }
        </div>
        <div className="copyright">
          <span>网易公司版权所有©1997-2020</span>
          <span>
            杭州乐读科技有限公司运营:
            <a href="https://p1.music.126.net/Mos9LTpl6kYt6YTutA6gjg==/109951164248627501.png" target="_blank" rel="external nofollow"  rel="noopener noreferrer" target="_blank">浙网文[2018]3506-263号</a>
          </span>
        </div>
        <div className="report">
          <span>违法和不良信息举报电话:0571-89853516</span>
          <span>
            举报邮箱:
            <a href="mailto:[email protected]" target="_blank" rel="external nofollow"  target="_blank" rel="noopener noreferrer">[email protected]</a>
          </span>
        </div>
        <div className="info">
          <span>粤B2-20090191-18</span>
          <a href="http://www.beian.miit.gov.cn/publish/query/indexFirst.action" target="_blank" rel="external nofollow"  rel="noopener noreferrer" target="_blank">
            工业和信息化部备案管理系统网站
          </a>
        </div>
      </FooterLeft>
      <FooterRight className="right">
        {
          footerImages.map((item, index) => {
            return (
              <li className="item" key={item.link}>
                <a className="link" href={item.link} rel="noopener noreferrer" target="_blank"> </a>
                <span className="title">{item.title}</span>
              </li>
            )
          })
        }
      </FooterRight>
    </div>
  </AppFooterWrapper>
  )
})

           

歌曲封面组件

React.js hooks 练习案例-网易云音乐【day2】成果展示
import React, { memo } from 'react'; // 导入recat组件
import { getCount, getSizeImage } from '@/utils/format-utils'; // 导入点播量计算工具,图片大小设置工具
import { SongsCoverWrapper } from './style'; //导入js-css样式

// 歌曲封面组件
export default memo(function RTSongsCover(props) {
  // 传入数据
  const { info } = props;

  // UI布局模块
  return (
    <SongsCoverWrapper>
      <div className="cover-top">
        <img src={getSizeImage(info.picUrl, 140)} alt={info.name} />
        <div className="cover sprite_cover">
          <div className="info sprite_cover">
            <span>
              <i className="sprite_icon erji" />
              {getCount(info.playCount)}
            </span>
            <i className="sprite_icon play"/>
          </div>
        </div>
      </div>
      <div className="cover-bottom text-nowrap">
        {info.name}
      </div>
      <div className="cover-source text-nowrap">
        by {info.copywriter || info.creator.nickname}
      </div>
    </SongsCoverWrapper>
  )
})
           

插件使用

轮播图使用:antd 风格与组件稳定符合项目要求,如果真实开发组件稳定考虑优先,除非风格完全不匹配,才考虑原生。

React.js hooks 练习案例-网易云音乐【day2】成果展示
import React, { memo, useEffect, useRef, useCallback, useState } from 'react'; // 引入react组件
import { useDispatch, useSelector, shallowEqual } from 'react-redux'; // 引入共享数据管理hooks
import { getTopBannerAction } from '../../store/actionCreators'; // 引入网络请求接口
import { Carousel } from 'antd'; // 导入 antdesign 轮播图组件
import { BannerWrapper, BannerLeft, BannerRight, BannerControl } from './style'; // 导入js-css样式

// 轮播图组件
export default memo(function RTTopBanner() {

  /* 组件内部数据 */ 
  // 使用 hooks:useState 保存轮播图操作数据
  const [currentIndex, setCurrentIndex] = useState(0);

  /* 组件功能模块 */
  // hooks不做浅层比较只做引用比较,所以会有性能浪费情况。性能优化添加浅层比较:shallowEqual 
  // 使用 hooks:useSelector 获取 state(immutable对象)
  // immutable 对象需要使用 get()方法取值, 多层嵌套时可以使用 getIn() 
  const { topBanners } = useSelector(state => ({
    topBanners: state.getIn(["recommend", "topBanners"])
  }), shallowEqual);

  // 使用 hooks:useDispatch 获取dispatch函数
  const dispatch = useDispatch();
  // 发送请求(组件被渲染是发送请求)
  useEffect(() => {
    dispatch(getTopBannerAction())
  },[dispatch]); // 当对象更改将会重新渲染

  // 使用 hooks:useRef 操作真实dom元素
  const bannerRef = useRef();
  // 使用 hooks:useCallback 可以保存函数到缓存,下次直接引用,多次生成引起组件刷新
  const bannerChange = useCallback((from, to) => {
    setCurrentIndex(to)
  },[]); // []:不做任何对比,不会刷新

  /* 组件逻辑模块 */
  // 获取当前图片 url 拼接参数,获取该图片的高斯模糊背景图 url
  const bgImage = topBanners[currentIndex] && (topBanners[currentIndex].imageUrl + "?imageView&blur=40x20")

  // UI布局模块
  return (
    // bgImage:向 js-css 传入背景图片url 地址
    <BannerWrapper bgImage={bgImage}>
      <div className="banner wrap-v2">
        <BannerLeft>
        <Carousel effect="fade" autoplay ref={bannerRef} beforeChange={bannerChange}>
          {
            topBanners.map ((item, index) =>{
                return (
                  <div className="banner-item" key={item.imageUrl}>
                      <img className="image" src={item.imageUrl} alt={item.typeTitle}></img>
                  </div>
                )
            })
          }
        </Carousel>
        </BannerLeft>
        <BannerRight></BannerRight>
        <BannerControl>
          <button className="btn left" onClick={ e => bannerRef.current.prev()}></button>
          <button className="btn right" onClick={ e => bannerRef.current.next()}></button>
        </BannerControl>
      </div>
    </BannerWrapper>
  )
})