乐趣区

关于前端:基于React全家桶开发网易云音乐PC项目实战四

前言

前言

hello 大家好我是「风不识途」,如果首次浏览本系列请点击,正在学习 React 的小伙伴能够克隆该我的项目,参考学习,尝试做一些小性能,上面咱们开始实现本系列最重要的 音乐播放器列表▶须要实现内容如下↓;

我的项目预览和源码

  • 在线预览地址👉:点我跳转到云音乐
  • 我的项目 Gihub 地址👉:Musci 163 (如果感觉我的项目还不错的话 👏,就给个 star ⭐ 激励一下吧~)

    • 没有翻墙的小伙伴👉:Gitee 仓库

最近更新

更新性能

  • 登录性能:

    • 临时只反对“163 邮箱”或“手机号”登录
    • 每日举荐歌单(只有登录胜利能力查看)
    • 个人主页 & 集体珍藏歌单 & 评论歌曲 & 点赞歌曲评论 & 创立歌单
  • 本地存储歌曲列表:

    • 不论之后是否刷新浏览器,只有在歌曲列表中就会长久化存储
    • (刷新浏览器,歌曲列表仍然存在)
  • 歌曲列表:

    • 对歌曲列表反对拖拽排序,并会对播放程序进行扭转
  • 搜寻音乐框:

    • 优化在搜寻歌曲时,反对键盘 ”↑”+”↓” 来切换搜寻歌曲内容
  • 头部进度条:

    • 在页面路由跳转 & 网络申请时 ” 增加头部进度条 ” 显示
  • 404 页:

    • 增加 404 页,在路由没有匹配的页面时,会显示 404 页面

批改 BUG&ToDoList

点击查看👉近期优化调整

点击查看👉TO-DO-LIST

界面性能展现(新开发)

歌曲搜寻(↑↓抉择)

反对对歌曲列表进行拖拽排序

登录演示

每日举荐

个人主页

音乐列表

上面开始实现本节(稍简单),歌曲列表播放组件,反对性能如下:

  • 反对点击某一项播放歌曲
  • 歌词实时滚动
  • 和首页的歌词组件互斥成果
  • 革除全部歌曲
  • 拖拽更改程序

1. 页面布局搭建

  1. 页面布局搭建

  1. 点击按钮显示和暗藏(播放列表) 和 增加过渡成果

  1. 和歌词展现控件互斥.(歌词列表显示, 敞开歌词展现)

  1. Conten 内容搭建

  1. 实现点击一项,播放对应的音乐,并且背景高亮

    • 在点击以后项后,将 id 传递函数,依据 id 派发action,申请播放列表详情信息
    • 将播放音乐函数传递给子组件
    • 实现当音乐播放完结或手动点击上一首或后下一首:

      • 对应 item 背景高亮跟着切换 (依据保留在 redux 中的currentSongIndex)

  1. 实现点击删除按钮,阻止事件冒泡和革除点击播放歌曲
  2. 点击革除全副按钮,革除全副音乐,并播放下一首音乐

2. 歌词高亮成果

歌词接口

  • 申请接口:

    • http://39.102.36.212:3000/lyr…
    • http://39.102.36.212:3000/lyr…
  • 对申请下来的歌词进行解析

    • 在上一节咱们曾经寄存到 redux 当中,参考👉歌词格式化
  • 实现以后播放的歌词高亮显示,获取以后是否播放状态滚动歌词

3. 歌词滚动成果

  • 依据以后播放歌词的 index,实现滚动成果
  • 获取DOM,计算scrollTop,实现滚动

音乐列表拖拽排序 sortablejs

阐明

  • <font color=’red’>当须要对某些表格的行或者列进行拖拽排序时</font>

    • 并不局限表格
    • 只有是你想拖拽的某一行都能够

应用拖拽排序步骤

  1. 装置
npm install sortablejs --save
yarn add sortablejs
  1. 导入
import Sortable from 'sortablejs';
  1. 配置参数
import React, {useRef} from 'react';

// other hook
const playlistRef = useRef();// 用于获取 DOM 元素

useEffect(() => {
  // 获取列表项父元素
  const el = playlistRef.current.querySelector('.main-playlist');
  new Sortable(el, {
    sort: true,
    animation: 200,
    onEnd: function (evt) {  // 拖拽完结产生该事件
      let tempPlayList = playList
      tempPlayList.splice(evt.newIndex, 0, playList.splice(evt.oldIndex, 1)[0]);
      // 更改播放列表程序
      dispatch(changePlayListAction(tempPlayList))
    },
  });
}, [playList, dispatch]);
  1. 实现成果

本地存储音乐列表

思考 immutableredux-persise互相不能兼容,次要起因是 immutable 的数据格式,redux-persist不意识,导致咱们想把 redux 中保留的状态不能进行本地存储,尝试了 redux-persist-transform-immutable 进行转换还是不行,可能应用的姿态不对,有应用过的大佬请领导一下,搞了两三次,最初只能撤销代码,本人还是太菜了,哭了😥

那就换一种思路,手动的只将 歌曲列表进行本地存储 (只存储歌曲 id),将歌曲歌曲列表中所有歌曲id 保留到 local Storage

默认歌曲列表

在咱们每次申请歌曲的时候,其实只须要晓得歌曲 id 就能够了,这样咱们只须要将在第一次初始化的的时候将 歌曲 id进行存储在本地就能够了,不过须要对本地存储的 歌曲 id进行去重就能够了(反复的 歌曲 id不进行本地存储)

实现成果

歌曲程序(异步问题)

一个小问题:在咱们遍历歌曲列表数组 id 发送网络申请时,因为发送网络申请是异步的,导致咱们的音乐列表的歌曲程序并不是依照发送数组设定好的程序,有没有一种方法能够 只有上一次网络申请胜利之后再进行发送这次网络申请呢?

答案是必定的,首先想到 promise 的小伙伴加鸡腿🍗,当然也能够应用async await

思路

(1)解决方案:promise + setinterval(定时器)(2)可能有同学会问,为什么应用定时器呢?(不肯定应用要应用定时器 +promise 这种计划)(3)这是因为在咱们发送 ajax 时,不能很好的进行管制,应用一个标识变量来进行管制 ajax 是否发送(默认为 true)(4)在每次开始定时器时,首先判断标识变量是否为 true 如果为 true 就发送 ajax,在本次申请 ajax 时设置标识变量为 false(即在定时器中不会再发送网络申请),在本次 ajax 实现时(即异步操作胜利时),扭转标识变量为 true
  这样就能进行很好的管制,简略的总结一下:就是必须管制本次 ajax 发送申请胜利时,能力进行下一次 ajax(外围在于应用标识变量,来管制 ajax 申请,且只有上次 ajax 申请胜利,能力进行下一次 ajax)

代码

export const getSongDetailArrayAction = (listId) => {return (dispatch, getState) => {
    // 1. 获取歌曲列表
    const playList = getState().getIn(['player', 'playList'])
    let i = 0
    let timer = null
    let excuteRun = true // 管制 ajax
    timer = setInterval(() => {let idx = listId[i]
      new Promise((resolve, reject) => {
        excuteRun &&
          getSongDetail(idx).then((res) => {
            excuteRun = false
            // console.log(res.songs[0])
            // (0)歌曲 ID 增加到本地存储
            addPlaylistId(idx)
            const song = res.songs && res.songs[0]
            // console.log(song)
            if (!song) return
            // (1)增加到播放列表中
            playList.push(song)
            dispatch(changePlayListAction(playList))
            // (2)更改以后播放的索引
            const songIndex = playList.length - 1
            dispatch(changeSongIndexAction(songIndex))
            // (3)更改以后播放歌曲
            dispatch(changeCurrentSongAction(song))
            // (4)申请歌曲的歌词
            dispatch(getLyricAction(idx))
            // (5)更新歌曲数量
            dispatch(changePlayListCount(playList.length))
            resolve(i)
          })
      }).then((value) => {excuteRun = true})
      i++
      if (i >= listId.length) {clearInterval(timer)
      }
    })
  }
}

成果

  • 这下不论刷新多少次,都会依照咱们的本地存储中歌曲列表 id 数组程序进行申请了

以优化

  • 反对记忆歌曲列表

    • 刷新页面后,音乐播放列表能够记忆上次播放程序
  • 记忆在敞开页背后播放的音乐

    • 刷新页面后,敞开页背后记忆以后播放的歌曲,再次关上时默认歌曲是敞开前播放的歌曲

到当初咱们曾经实现「网易云音乐 PC」基本功能,置信你对 React 全家桶曾经是比拟纯熟了,接下来想往哪方面扩大能够自行补充欠缺性能;

如果文章中有哪局部不明确的或写的不好或是有什么倡议欢送大家提出🤗,心愿和大家共同进步;

感激

  • 非常感谢王红元老师的 React 核心技术实战 让我学习到很多 react 的常识。
  • 非常感谢后盾提供者 Binaryify,接口很稳固,文档很欠缺
退出移动版