React實現(Web端)網易雲音樂專案(一),錯過了真的可惜呀

2020-08-10 16:29:47

首先肯定是搭建專案的結構了,通過腳手架安裝這部分我就不說了

首先看專案的目錄結構

在这里插入图片描述

assets:放我們的靜態資源,圖片,字型和公共初始樣式等
common:放我們公共的JS檔案
components:放我們專案的公共元件
pages:放專案的主檢視元件
router:放專案的路由設定
services:網路請求部分
store:放redux的相關設定
utils:放一些處理我們數據的JS方法

一.我們引入檔案的時候經常就是這樣

  ../../../../

感覺太邋遢了
安裝一個外掛吧

yarn add @craco/craco

然後在我們專案的根目錄,也就是src的同級目錄,建立一個craco.config.js

// 匯入檔案可以用@
const path = require("path");

const resolve = dir => path.resolve(__dirname, dir);

module.exports = {
  webpack: {
    alias: {
      "@": resolve("src"),
      "components": resolve("src/components")
    }
  }
}

然後在我們的package.json中,將scripts改成這樣的
在这里插入图片描述

今後匯入我們的任何檔案直接這樣寫

import {  } from '@/common';
import  from '@/components';

二.先定義一些初始樣式比較好,在入口index.js引入,專案主要是用styled-components寫的

yarn add normalize.css

然後在assets中建立一個css檔案,css檔案裏面建立一個reset.css

@import "~normalize.css";

/* 樣式的重置 */
body, html, h1, h2, h3, h4, h5, h6, ul, ol, li, dl, dt, dd, header, menu, section, p, input, td, th, ins {
  padding: 0;
  margin: 0;
}

ul, ol, li {
  list-style: none;
}

a {
  text-decoration: none;
  color: #666;
}

a:hover {
  color: #666;
  text-decoration: underline;
}

i, em {
  font-style: normal;
}

input, textarea, button, select, a {
  outline: none;
  border: none;
}

table {
  border-collapse: collapse;
  border-spacing: 0;
}

img {
  border: none;
  vertical-align: middle;
}

/* 全域性樣式 */
body, textarea, select, input, button {
  font-size: 12px;
  color: #333;
  font-family: Arial, Helvetica, sans-serif;
  background-color: #f5f5f5;
}

.text-nowrap {
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
}

.wrap-v1 {
  width: 1100px;
  margin: 0 auto;
}

.wrap-v2 {
  width: 980px;
  margin: 0 auto;
}

.sprite_01 {
  background: url(../img/sprite_01.png) no-repeat 0 9999px;
}

.sprite_02 {
  background: url(../img/sprite_02.png) no-repeat 0 9999px;
}

.sprite_covor {
  background: url(../img/sprite_cover.png) no-repeat 0 9999px;
}

.sprite_icon {
  background: url(../img/sprite_icon.png) no-repeat 0 9999px;
}

.sprite_icon2 {
  background: url(../img/sprite_icon2.png) no-repeat 0 9999px;
}

.sprite_button {
  background: url(../img/sprite_button.png) no-repeat 0 9999px;
}

.sprite_button2 {
  background: url(../img/sprite_button2.png) no-repeat 0 9999px;
}

.sprite_table {
  background: url(../img/sprite_table.png) no-repeat 0 9999px;
}

.image_cover {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  text-indent: -9999px;
  background: url(../img/sprite_cover.png) no-repeat -145px -57px;
}

我先把這些樣式寫這了,因爲網易雲音樂,它裏面很多的圖片都用的是精靈圖,我提前給他們把位置和樣式確定了一下

這篇部落格就先完成下面 下麪這個,我們先把基本的框架和路由搭好
在这里插入图片描述

三.先在pages裏面建立三個檢視檔案,discover,friend,mine(其他幾個是跳轉外部鏈接的,不需要路由)

import React, { memo } from 'react';

export default memo(function LSHDiscover() {
  return (
    <div>
      <h2>LSHDiscover</h2>
    </div>
  )
})

import React, { memo } from 'react';

export default memo(function LSHFriend() {
  return (
    <div>
      <h2>HYFriend</h2>
    </div>
  )
})

import React, { memo } from 'react';

export default memo(function LSHMine() {
  return (
    <div>
      <h2>LSHMine</h2>
    </div>
  )
})

然後discover有6個子路由,我就不一一寫了

在这里插入图片描述

至於爲什麼用memo?節省效能

元件僅在它的 props 發生改變的時候進行重新渲染

四.設定路由

yarn add react-router-config
yarn add react-router-dom

在我們的router檔案中建立一個index.js

import React from 'react'

import LSHDiscover from "@/pages/discover";
import HYRecommend from "@/pages/discover/c-pages/recommend";
import HYRanking from "@/pages/discover/c-pages/ranking";
import HYSongs from "@/pages/discover/c-pages/songs";
import HYDjradio from "@/pages/discover/c-pages/djradio";
import HYArtist from "@/pages/discover/c-pages/artist";
import HYAlbum from "@/pages/discover/c-pages/album";

import LSHMine from "@/pages/mine";
import LSHFriend from "@/pages/friend";

import {Redirect} from 'react-router-dom'    
Redirect是爲了重定向,當進頁面的時候展示哪一個

下面 下麪是routes

const routes = [
  {
    path: "/",
    exact:true,
    render:()=>(
      <Redirect to="/discover"/>
    )
  },
  {
    path: "/discover",
    component: LSHDiscover,
    routes: [
      {
        path: "/discover",
        exact: true,
        render: () => (
          <Redirect to="/discover/recommend"/>
        )
      },
      {
        path: "/discover/recommend",
        component: HYRecommend
      },
      {
        path: "/discover/ranking",
        component: HYRanking
      },
      {
        path: "/discover/songs",
        component: HYSongs
      },
      {
        path: "/discover/djradio",
        exact: true,
        component: HYDjradio
      },
      {
        path: "/discover/artist",
        component: HYArtist
      },
      {
        path: "/discover/album",
        component: HYAlbum
      }
    ]
  },
  {
    path: "/mine",
    component: LSHMine
  },
  {
    path: "/friend",
    component: LSHFriend
  },
];

同時別忘了導出

export default routes

五.在APP.js中設定

import React, { memo } from 'react'
import {renderRoutes} from 'react-router-config'
import { HashRouter } from 'react-router-dom';
import routes from './router'

import LSHAppHeader from '@/components/app-header'

export default memo(function App() {
  return (
    <HashRouter>
      <LSHAppHeader/>
      {renderRoutes(routes)}
    </HashRouter>
  )
})

在這裏我們用的是hash路由,LSHAppHeader是我們下面 下麪這個,下面 下麪開始寫
在这里插入图片描述
六.開始app-header元件的編寫

在components中建立一個app-header檔案,裏面建立一個index.js和style.js

由於我們這個東西用到了antd這個ui庫,先安裝一下,別忘了匯入樣式,可以在我們定義的reset.css中匯入

@import "~antd/dist/antd.css";

安裝antd

yarn add antd
yarn add @ant-design/icons

index.js

import React, { memo } from 'react';

import { headerLinks } from '@/common/local-data'

import { NavLink } from 'react-router-dom';
import { SearchOutlined } from '@ant-design/icons'
import { Input } from 'antd'
import {
  HeaderWrapper,
  HeaderLeft,
  HeaderRight
} from './style'

export default memo(function LSHAppHeader() {
  //業務程式碼
  const showSelectItem = (item, index) => {
    if (index < 3) {
      return (
        <NavLink to={item.link} exact>
          {item.title}
          <i className="sprite_01 icon"></i>
        </NavLink>
      )
    } else {
      return <a href={item.link}>{item.title}</a>
    }
  }

  //返回到jsx
  return (
    <HeaderWrapper>
      <div className="content wrap-v1">
        <HeaderLeft>
          <a href="#/" 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>
  )
})

headerLinks是我們一些公共js數據,在common中建立一個local-data.js

export const headerLinks = [
  {
    title: "發現音樂",
    link: "/discover"
  },
  {
    title: "我的音樂",
    link: "/mine"
  },
  {
    title: "朋友",
    link: "/friend"
  },
  {
    title: "商城",
    link: "https://music.163.com/store/product"
  },
  {
    title: "音樂人",
    link: "https://music.163.com/nmusician/web/index#/"
  },
  {
    title: "下載用戶端",
    link: "https://music.163.com/#/download"
  }
]

style.js樣式爲,要先安裝我們的styled-components,yarn add styled-components

import styled from "styled-components";

export const HeaderWrapper = styled.div`
  height: 75px;
  font-size: 14px;
  color: #fff;
  background-color: #242424;

  .content {
    height: 70px;

    display: flex;
    justify-content: space-between;
  }

  .divider {
    height: 5px;
    background-color: #C20C0C;
  }
`

export const HeaderLeft = styled.div`
  display: flex;

  .logo {
    display: block;
    width: 176px;
    height: 69px;
    background-position: 0 0;
    text-indent: -9999px;
  }

  .select-list {
    display: flex;
    line-height: 70px;
    
    .select-item {
      position: relative;
      a {
        display: block;
        padding: 0 20px;
        color: #ccc;
      }

      :last-of-type a {
        position: relative;
        ::after {
          position: absolute;
          content: "";
          width: 28px;
          height: 19px;
          background-image: url(${require("@/assets/img/sprite_01.png")});
          background-position: -190px 0;
          top: 20px;
          right: -15px;
        }
      }

      &:hover a, a.active {
        color: #fff;
        background: #000;
        text-decoration: none;
      }
      
      .active .icon {
        position: absolute;
        display: inline-block;
        width: 12px;
        height: 7px;
        bottom: -1px;
        left: 50%;
        transform: translate(-50%, 0);
        background-position: -226px 0;
      }
    }
  }
`

export const HeaderRight = styled.div`
  display: flex;
  align-items: center;
  color: #ccc;
  font-size: 12px;


  .search {
    width: 158px;
    height: 32px;
    border-radius: 16px;

    input {
      &::placeholder {
        font-size: 12px;
      }
    }
  }

  .center {
    width: 90px;
    height: 32px;
    line-height: 32px;
    text-align: center;
    border: 1px solid #666;
    border-radius: 16px;
    margin: 0 16px;
    background-color: transparent;
  }
`

好了,最頂部的完成了

下面 下麪寫這個discover的子路由檢視
在这里插入图片描述
我們去到discover這個檔案的index.js中

import React, { memo } from 'react';
//子路由渲染
import { renderRoutes } from 'react-router-config';

import { dicoverMenu } from "@/common/local-data";

import { NavLink } from 'react-router-dom';

import {
  DiscoverWrapper,
  TopMenu
} from './style';

export default memo(function LSHDiscover(props) {
  // 子路由渲染
  const { route } = props;

  return (
    <DiscoverWrapper>
       <div className="top">
        <TopMenu className="wrap-v1">
          {
            dicoverMenu.map((item, index) => {
              return (
                <div className="item" key={item.title}>
                  <NavLink to={item.link}>{item.title}</NavLink>
                </div>
              )
            })
          }
        </TopMenu>
      </div>
      {renderRoutes(route.routes)}
    </DiscoverWrapper>
  )
})

dicoverMenu這個檔案的東西

export const dicoverMenu = [
  {
    title: "推薦",
    link: "/discover/recommend"
  },
  {
    title: "排行榜",
    link: "/discover/ranking"
  },
  {
    title: "歌單",
    link: "/discover/songs"
  },
  {
    title: "主播電臺",
    link: "/discover/djradio"
  },
  {
    title: "歌手",
    link: "/discover/artist"
  },
  {
    title: "新碟上架",
    link: "/discover/album"
  },
]

style.js

import styled from 'styled-components';

export const DiscoverWrapper = styled.div`
  .top {
    height: 30px;
    background-color: #C20C0C;
  }
`

export const TopMenu = styled.div`
  display: flex;
  padding-left: 180px;
  position: relative;
  top: -4px;

  .item {
    a {
      display: inline-block;
      height: 20px;
      line-height: 20px;
      padding: 0 13px;
      margin: 7px 17px 0;
      color: #fff;

      &:hover, &.active {
        text-decoration: none;
        background-color: #9B0909;
        border-radius: 20px;
      }
    }
  }
`

好了,這個頁面基本就完成了,下一篇部落格就要通過網路請求獲取數據到我們redux中儲存了,這兩個頁面

在这里插入图片描述
github專案地址:https://github.com/lsh555/WY-Music