成果展示
因為之前沒玩把握,是以先玩了幾天再決定記錄下,現在已經完成首頁的60%左右的功能。還原度95%左右。
等項目完成得差不多我會上傳到 git 給小夥伴參考。
下半部分還有2個子產品沒完成。
共享元件設計方面
把大多數頁面都用到布局抽離成共享元件:
頭部元件
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>
)
})
底部元件
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>
)
})
歌曲封面元件
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 風格與元件穩定符合項目要求,如果真實開發元件穩定考慮優先,除非風格完全不比對,才考慮原生。
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>
)
})