GVKun编程网logo

React音乐播放器(react+redux+react-router+webpack)(react 音乐播放器)

1

在本文中,我们将带你了解React音乐播放器(react+redux+react-router+webpack)在这篇文章中,我们将为您详细介绍React音乐播放器(react+redux+react

在本文中,我们将带你了解React音乐播放器(react+redux+react-router+webpack)在这篇文章中,我们将为您详细介绍React音乐播放器(react+redux+react-router+webpack)的方方面面,并解答react 音乐播放器常见的疑惑,同时我们还将给您一些技巧,以帮助您实现更有效的''react-router-redux''到''connected-react-router''、./node_modules/react-router-dom/react-router-dom.js尝试导入错误:未从“ react-router”导出“ Navigate”、645 webpack常用plugins:clean-webpack-plugin,html-webpack-plugin,webpack.DefinePlugin,copy-webpack-plug、react - redux/react-redux/redux-saga/mobx

本文目录一览:

React音乐播放器(react+redux+react-router+webpack)(react 音乐播放器)

React音乐播放器(react+redux+react-router+webpack)(react 音乐播放器)

技术:React16,Redux,React-Router,WebPack

项目演示

项目github地址

会点ps,所以自己设计了页面和交互。现在的数据都是是自己mock的,开发后台的时候会对接真实数据,由于搜索、收藏歌单、新建歌单、删除歌单、收藏单曲、移除收藏单曲等功能需要后台配合,所以现在都是纯前端展示,不具备功能

部分页面:

项目描述

项目基于四个业务模块:播放器、曲库、发现、个人。这些模块中包含了一些基础组件来支撑业务逻辑:

  • 歌曲列表组件 :(应用:曲库-曲库好歌、每日推荐、轮播图二级页面)点击歌曲播放并且高亮,收藏歌曲,将歌曲添加到播放列表;
  • 可编辑歌曲列表组件 :(应用:我的-收藏的单曲、收藏的歌单,创建的歌单-歌单详情)歌曲列表组件的升级版,并在原有功能上添加了一键顺序播放、移除歌曲;
  • 歌单列表组件 :(应用:我的-我创建的歌单、我收藏的歌单)点击进入歌单详情、删除歌单;
  • 抛物线小球组件 :(应用:歌曲列表组件、可编辑歌曲列表组件)点击添加歌曲到播放列表图标时,跳出一个加号代表已经添加进播放列表;
  • 收藏歌曲组件 :(应用:歌曲列表组件)点击收藏歌曲,弹出选框,用户可选择收藏到单曲列表还是收藏到歌单或者创建歌单(收藏的数据交互待完善);

业务模块

  • 播放器内核模块

    • 上一曲,下一曲,播放,暂停
    • 点击或拖动进度条实现播放进度的调整
    • 播放列表的展开收缩
    • 列表内歌曲的删除
    • 页面内歌曲添加到播放列表
    • 播放详情页的展开与收缩
    • 播放模式选择(顺序播放,随机播放,单曲循环)
    • 封面图片旋转
    • 当前播放歌曲高亮显示
    • 歌词同步(待开发)
  • 曲库模块

    • redux内请求数据
    • banner,每日推荐,曲库好歌
    • banner与每日推荐可点击进入二级页页面,二级页面基于歌曲列表组件构建,展示歌曲列表,实现列表内歌曲点击播放与添加进播放器播放列表
    • 曲库好歌为直接的列表展示,功能同上
    • 列表歌曲进场动效展示
    • 收藏歌曲弹出选项
  • 发现

    • 搜索歌曲(只前端展示静态的搜索结果,待完善)
    • 排行榜
    • 风格模块
  • 个人模块

    • 我的资料(待开发)
    • 修改资料(待完善)
    • 收藏的单曲
    • 收藏的歌单
    • 新建歌单、删除歌单
    • 移除收藏的单曲
  • 登录注册(待开发)

Redux设计

首先,这次设计的redux貌似不合理。。有几个没必要放到store里共享的状态也放进去了。大家当反面教材吧。。引入redux-thunk中间件,大部分axios请求都放到了redux中。
store内的状态分为8个模块:

  • album :专辑模块信息(专辑列表、专辑详情、专辑内歌曲、专辑介绍);
  • comment :评论模块(评论内容、是否已经请求回来,收到的回复(暂未开发));
  • discovery :发现模块(排行榜列表,风格);
  • personal :个人模块(用户信息、收藏歌单,收藏的歌曲数量,歌单数量);
  • repertoire :曲库模块(轮播图、每日推荐数据,曲库好歌);
  • player :播放器内核模块(当前播放的歌曲,待播放歌曲列表,是否应该渲染);
  • publicSongs :公共的歌曲列表,可编辑歌曲列表和播放器内核共用,这样设计的原因是可编辑播放列表内的一键播放功能需要和播放器内核关联起来,如果点击一键播放,那么顺序播放歌单内歌曲,否则播放完成后播放待播放歌曲列表内歌曲;
  • song-list-detail :歌单详情;

项目结构

总结

这项目是在我写的一个播放器上扩展来的,同时为了练习一下react全家桶开发的模式。通过几个react实战再加上这个练习,这种开发模式已经基本熟悉了,但是也还停留在写业务代码的阶段,这远远不够。还要学习从0开始构建一个完整的项目。redux这次用的比较多但是还需要深入学习(这个项目使用create-react-app一键创建的)。现在前端开发基本完成了,性能还未做优化。接下来慢慢的要开发后台,提供数据了,优化性能。

''react-router-redux''到''connected-react-router''

''react-router-redux''到''connected-react-router''

背景:
redux和react-router两者协同工作会出现,路由变化而store无法感知到,的问题。

react-router-redux :
一. 作用:
react-router-redux 是 redux 的一个中间件,加强了React Router库中history这个实例,以允许将history中接受到的变化反应到state中去。

使用方法1:

使用syncHistoryWithStore包裹browserHistory,当url改变时,会自动触发 LOCATION_CHANGE action,更改store中维护的 locationBeforeTransitions 状态对象,实现store状态的更新。

// 只需要传入react-router中的history以及redux中的store,就可以获得一个增强后的history对象。
// 将这个history对象传给react-router中的Router组件作为props,就给应用提供了观察路由变化并改变store的能力。
import { syncHistoryWithStore, routerReducer } from ''react-router-redux''
const store = createStore(
  combineReducers({
    ...reducers,
    routing: routerReducer
  })
)
const history = syncHistoryWithStore(browserHistory, store)
ReactDOM.render(
  <Provider store={store}>
    <Router history={history}>
      <Route path="/" component={App} />
    </Router>
  </Provider>,
  document.getElementById(‘app'')
)

// 对应的reducer.js
export const LOCATION_CHANGE = ''@@router/LOCATION_CHANGE''

const initialState = {
  locationBeforeTransitions: null
}
  1. 使用方法2:

    手动触发路由的跳转,同时需要。

    直接store.dispatch(push(''/foo'')),会触发 CALL_HISTORY_METHOD 这个action,调用中间件,等同于调用 browserHistory上相应的push方法。

// 触发路由跳转(使用redux action的方式来触发)
import { createStore, combineReducers, applyMiddleware } from ''redux'';
import { routerMiddleware, push } from ''react-router-redux''
// Apply the middleware to the store
const middleware = routerMiddleware(browserHistory)
const store = createStore(
  reducers,
  applyMiddleware(middleware)
)
// 使用dispatch手动触发 push 等操作
store.dispatch(push(''/foo''))

源码的具体实现:

// import 的 push, replace, go, goBack, goForward之类的方法出自: action.js
export const push = updateLocation(''push'')

function updateLocation(method) {
  return (...args) => ({
    type: CALL_HISTORY_METHOD,
    payload: { method, args }
  })
}

//中间件代码
export default function routerMiddleware(history) {
  return () => next => action => {
    if (action.type !== CALL_HISTORY_METHOD) {
      return next(action)
    }

    const { payload: { method, args } } = action
    history[method](...args)       //这里直接改变browserHistory
  }
}

二. react-router-redux原理图

CALL_HISTORY_METHOD,这类 action 一般会在组件内派发,它不负责 state 的修改,通过 routerMiddleware 后,会被转去调用 history。

面临的问题:
react-router-redux 只兼容 react-router 2.x and 3.x,所以要改成使用 connected-react-router 来兼容 react-router 4.x。

解决:

把之前 react-router-redux 中 store.dispatch(push(''/foo'')) 这么使用的,直接改成 history.push(''/foo'') 之类的。

参考文章:
https://blog.csdn.net/weixin_... React 的路由状态管理

./node_modules/react-router-dom/react-router-dom.js尝试导入错误:未从“ react-router”导出“ Navigate”

如何解决./node_modules/react-router-dom/react-router-dom.js尝试导入错误:未从“ react-router”导出“ Navigate”

./node_modules/react-router-dom/react-router-dom.js Attempted import error: ''Navigate'' is not exported from ''react-router''.

react-router-dom的版本是6.0.0-alpha.2,而react-router是5.2.0。 两者均已正确安装。我不确定如何解决此错误。有人可以给我任何可能的解决方法吗?

我的代码中甚至没有<Navigate to=?>行。

解决方法

为什么只安装两个都需要,这可以工作

  1. 执行npm删除react-router
  2. 删除node_modules
  3. 纱线安装或npm安装和
  4. 启动纱线或启动npm

645 webpack常用plugins:clean-webpack-plugin,html-webpack-plugin,webpack.DefinePlugin,copy-webpack-plug

645 webpack常用plugins:clean-webpack-plugin,html-webpack-plugin,webpack.DefinePlugin,copy-webpack-plug

-

  • 前端小菜鸟,喜欢前端,不断学习
  • 微信:jie178463596
  • 微信小群:纯粹讨论技术、面试、工作为主,划水少,拒绝广告

认识Plugin


CleanWebpackPlugin


HtmlWebpackPlugin


生成的index.html分析


自定义HTML模板


自定义模板数据填充


DefinePlugin的介绍


DefinePlugin的使用


copyWebpackPlugin


目录结构


wk.config.js

const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { DefinePlugin } = require('webpack'); // DefinePlugin是webpack内置插件
const copyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
  entry: "./src/main.js",
  output: {
    filename: "js/bundle.js",
    // 必须是一个绝对路径
    path: path.resolve(__dirname, "./build"),
    // assetmodulefilename: "img/[name].[hash:6][ext]"
  },
  module: {
    rules: [
      {
        // 规则使用正则表达式
        test: /\.css$/, // 匹配资源
        use: [
          // { loader: "css-loader" },
          // 注意: 编写顺序(从下往上, 从右往做, 从后往前)
          "style-loader",
          {
            loader: "css-loader",
            options: {
              importLoaders: 1
            }
          },
          "postcss-loader"
        ],
        // loader: "css-loader"
      },
      {
        test: /\.less$/,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              importLoaders: 2
            }
          },
          "postcss-loader",
          "less-loader"
        ]
      },
      {
        test: /\.(png|jpe?g|gif|svg)$/,
        // type: "asset/resource", file-loader的效果
        // type: "asset/inline", url-loader
        type: "asset",
        generator: {
          filename: "img/[name].[hash:6][ext]"
        },
        parser: {
          dataUrlCondition: {
            maxSize: 100 * 1024
          }
        }
      },
      {
        test: /\.ttf|eot|woff2?$/i,
        type: "asset/resource",
        generator: {
          filename: "font/[name].[hash:6][ext]"
        }
      }
    ]
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: "哈哈 webpack",
      template: "./public/index.html"
    }),
    new DefinePlugin({
      // 要包裹两层引号
      BASE_URL: '"./"'
    }),
    new copyWebpackPlugin({
      patterns: [
        {
          // to: xxx, // 不用写,默认会使用output.path
          from: "public",
          globOptions: {
            ignore: [
              "**/index.html",
              "**/.DS_Store",
              "**/abc.txt"
            ]
          }
        }
      ]
    })
  ]
}


publuc/index.html

<!DOCTYPE html>
<html lang="">
  <head>
    <Meta charset="utf-8">
    <Meta http-equiv="X-UA-Compatible" content="IE=edge">
    <Meta name="viewport" content="width=device-width,initial-scale=1.0">
    
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

build/index.html

<!DOCTYPE html>
<html lang="">
  <head>
    <Meta charset="utf-8">
    <Meta http-equiv="X-UA-Compatible" content="IE=edge">
    <Meta name="viewport" content="width=device-width,initial-scale=1.0">
    
    <link rel="icon" href="./favicon.ico">
    
    <title>杰帅的webpack</title>
  <script defer src="js/bundle.js"></script></head>
  <body>
    <noscript>
      <strong>We're sorry but 杰帅的webpack doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

react - redux/react-redux/redux-saga/mobx

react - redux/react-redux/redux-saga/mobx

redux 用法

1、安装 redux

yarn add redux

2、创建一个store.js文件

import { createStore } from ''redux''
// 创建reducer
function couter (state, action) {
  switch(action.type){
    case ''INCREMENT'': return state + 1;
    case ''DRCREMENT'': return state - 1;
    default: return state;
  }
}
// 创建 store
const store = createStore(couter)
// 暴露 store
export default store

3、index.js

import React from ''react'';
import ReactDOM from ''react-dom'';
import ''./index.css'';
import App from ''./App'';
import * as serviceWorker from ''./serviceWorker'';
import store from ''./store''

function render (){
  ReactDOM.render(
    <App />,
    document.getElementById(''root'') 
  );
}
// 调整render的方式
render()
// 订阅 store
store.subscribe(render)

serviceWorker.unregister();

4、TestStore.js 使用 store

import React, { Component } from ''react''
import { Button } from ''antd''
import store from ''./store''

export default class TestStore extends Component {
  increment(){
    store.dispatch({
      type: ''INCREMENT''
    })
  }
  drcrement(){
    store.dispatch({
      type: ''DRCREMENT''
    })
  }
  render() {
    return (
      <div>
        <h3>{store.getState()}</h3>
        <Button onClick={this.increment}>+1</Button>
        <Button onClick={this.drcrement}>-1</Button>
      </div>
    )
  }
}

5、在app.js里面引入TestStore

import React, { Component } from ''react'';
import ''./App.css''
import TestStore from ''./TestStore''
class App extends Component {
  render() {
    // let self = this
    return (
      <div>
        <TestStore/>
      </div>
    );
  }
}
export default App;

以上是 redux 的用法,显而易见你每次对 store 进行 dispatch(action) 都会触发 subscribe 注册的函数调用比较麻烦,其实就类似于 vuecomputed
每个应用只能有一个state,但是可以拥有多个reducers,多个reducers可以使用 combineReducers

今天关于React音乐播放器(react+redux+react-router+webpack)react 音乐播放器的介绍到此结束,谢谢您的阅读,有关''react-router-redux''到''connected-react-router''、./node_modules/react-router-dom/react-router-dom.js尝试导入错误:未从“ react-router”导出“ Navigate”、645 webpack常用plugins:clean-webpack-plugin,html-webpack-plugin,webpack.DefinePlugin,copy-webpack-plug、react - redux/react-redux/redux-saga/mobx等更多相关知识的信息可以在本站进行查询。

本文标签: