天天看點

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>
  )
})