整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

HTML5+js开发自己的音乐播放器,千万粉丝围观!

HTML5+js开发自己的音乐播放器,千万粉丝围观!

天小编我博客时,看到了一位大神分享的自己的音乐播放器,小编我也COPY了一份分享给头条上的小伙伴,能搞定这个音乐播放器找份工作还是没压力的,毕竟这个项目还是挺好的,正在学习web前端网页制作的伙伴们福利来了,小编我我项目整理了一下,顺便写下这篇文章(项目地址文末有)

做完的项目如图所示:

实现的功能

1、首页

2、底部播放控件

3、播放页面

4、播放列表

5、排行榜

6、音乐搜索

输入搜索关键词,点击放大镜图标

7、侧边栏

API

感谢作者把api整理的这么好(点个赞)

https://binaryify.github.io/NeteaseCloudMusicApi/#/?id=%e6%90%9c%e7%b4%a2%e9%9f%b3%e4%b9%90

目录结构

1、轮播图

首先感谢作者ShanaMaid/vue-image-scroll开源的代码,我把代码copy下来自己进行了一点修改(没有手指滑动效果),因为这是移动端,少不了的手指滑动切换,所以添加了vue-touch(偷偷告诉你,vue-touch的next分支还是支持vue2.0的)。

2、歌曲操作(喜欢,分享,加入播放列表)动画、播放列表展开与删除歌曲动画

transition-group

一组过度动画,这里有个小坑的,之前看官网列表过渡的栗子,给每一项设置唯一的key值,一般都会用index。所以在做的时候就把index传给key,结果过渡老是不对,后来换成对应的item

就正常了(生无可恋脸)。

3、直线进度条、弧形进度条

西班牙建筑大师曾说过:“直线属于人类,曲线则归于上帝”。在这里我大胆的使用了弧形来作为进度条,(几大热门音乐APP貌似还没有弧形进度条)。

这里我用到了Vue的绑定内联样式

5、图片懒加载

使用了vue-lazyload插件

用法:

6、歌词滚动与高亮

因为api提供的歌词包括时间,如:

[03:57.280]原谅我这一生不羁放纵爱自由

所以首先要进行字符串切割:

然后在播放的监听事件中与播放的当前做对比:

到这就ok了

7、vuex状态管理

推荐官方调试工具 devtools extension

想进一步理解vuex,可以看这篇博客vuex学习实践笔记

之前看到好多人写的vuex,把整个项目的数据放到了一个state里,导致应用的所有状态集中到一个很大的对象。但是,当应用变得很大时,store 对象会变得臃肿不堪。

所以我建议(个人见解,轻喷):将 store 分割到模块(module)。每个模块拥有自己的 state、mutation、action、getters。这样方便管理与后期的维护。

车已到站??。

入门只是学习的开始,这时你可以运用你的技能,实现各种需求。 在实现各种需求时,你会遇到更多各种各样的问题。有些问题可能已经不是单纯写JS代码能很好解决的了。 你可能需要配合一些框架,或一些工具,或一些设计模式, 或一些业务知识。

随着解决更多问题, 你也会积累更多的知识。之后会是漫长的积累过程, 通过项目经验、看书、看博客、思考、讨论等等,积累你的经验和知识。

你需要持之以恒和刨根问底。总之,之后就看你自己的了。还是和前提差不多的那句话:你需要主动。

最后再来提点建议:

  1. 写JS的最好办法..是不停的重复练习....其实很多人反映...学JS的时候很难..但是有些人认为..它没有服务端的语言难.或者说..根本就不屑的学这种语言...什么破语言..老出各种各样的错误...浏览器还不兼容..但是...学好JS对你未来的web开发有至关重要的作用....我觉得.JS是奠定RIA的基础...或者说..客户端的开发RIA的基础.

  2. 这个音乐播放器项目到这里就算是做完了,想要完整代码自己学习练手的小伙伴进我的群自助领取,已经上传到群文件里了:640633433,欢迎初学和进阶中的小伙伴。

如果项目有哪些缺陷,欢迎在评论区指正!

过阅读这篇文章,可以学习到如何使用DefinePlugin插件使得前端项目更加工程化,说清晰点就是如何使用这个插件,在编译阶段根据NODE_ENV自动切换配置文件,提升前端开发效率。

  • DefinePlugin的正确用法
  • 如何使用DefinePlugin添加配置文件,构建期间自动检测环境变化,也就是如何根据NODE_ENV引入配置文件?

DefinePlugin的正确用法

DefinePlugin中的每个键,是一个标识符或者通过.作为多个标识符。

  • 如果value是一个字符串,它将会被当做code片段
  • 如果value不是字符串,它将会被stringify(包括函数)
  • 如果value是一个对象,则所有key的定义方式相同。
  • 如果key有typeof前缀,它只是对typeof 调用定义的。

这些值将内联到代码中,压缩减少冗余。

new webpack.DefinePlugin({
    PRODUCTION: JSON.stringify(true),
    VERSION: JSON.stringify('5fa3b9'),
    BROWSER_SUPPORTS_HTML5: true,
    TWO: '1+1',
    'typeof window': JSON.stringify('object'),
    'process.env': {
         NODE_ENV: JSON.stringify(process.env.NODE_ENV)
     }
});
console.log('Running App version' + VERSION);

plugin不是直接的文本值替换,它的值在字符串内部必须包括实际引用。典型的情况是用双引号或者JSON.stringify()进行引用,'"production"',JSON.stringify('production')。

重点:在vue-cli创建的项目中,凡是src下的文件,都可以访问到VERSION这个变量,例如main.js,App.vue等等

我们现在看一下上面的几种类型的key值,在代码中的输出。

console.log(PRODUCTION, VERSION, BROWSER_SUPPORTS_HTML5, TWO, typeof window, process.env);
PRODUCTION: true,
VERSION: "5fa3b9",
BROWSER_SUPPORTS_HTML5: true,
TWO: 2,
typeof window: "object",
process.env: {NODE_ENV: "development"},

在代码中,我们一般会有以下几种用途:

  • 根据process.env.NODE_ENV区分环境
  • 引入配置文件
  • 根据NODE_ENV引入配置文件(这个很重要,后面会讲到)

Feature Flag

可以控制新特性和实验特性的开关。

new webpack.DefinePlugin({
    'NICE_FEATURE': JSON.stringify(true),
    'EXPERIMENTAL': JSON.stringify(false),
})

process.env.NODE_ENV的正确配置方式是什么?

process: {
    env: {
        NODE_ENV: JSON.stringify('production')
    }
}

评价:非常不好,会overwrite整个process对象,仅仅保留新的NODE_ENV,破坏进程。 原始的process对象包含如下内容 ,包含了当前进程的很多信息。

process {
  title: 'node',
  version: 'v8.11.2',
  moduleLoadList: 
   [ 'Binding contextify',],
  versions: 
   { http_parser: '2.8.0'},
  arch: 'x64',
  platform: 'darwin',
  release: 
   { name: 'node' },
  argv: [ '/usr/local/bin/node' ],
  execArgv: [],
  env: 
   { TERM: 'xterm-256color'},
  pid: 14027,
  features: 
   { debug: false},
  ppid: 14020,
  execPath: '/usr/local/bin/node',
  debugPort: 9229,
  _startProfilerIdleNotifier: [Function: _startProfilerIdleNotifier],
  _stopProfilerIdleNotifier: [Function: _stopProfilerIdleNotifier],
  _getActiveRequests: [Function: _getActiveRequests],
  _getActiveHandles: [Function: _getActiveHandles],
  reallyExit: [Function: reallyExit],
  abort: [Function: abort],
  chdir: [Function: chdir],
  cwd: [Function: cwd],
  umask: [Function: umask],
  getuid: [Function: getuid],
  geteuid: [Function: geteuid],
  setuid: [Function: setuid],
  seteuid: [Function: seteuid],
  setgid: [Function: setgid],
  setegid: [Function: setegid],
  getgid: [Function: getgid],
  getegid: [Function: getegid],
  getgroups: [Function: getgroups],
  setgroups: [Function: setgroups],
  initgroups: [Function: initgroups],
  _kill: [Function: _kill],
  _debugProcess: [Function: _debugProcess],
  _debugPause: [Function: _debugPause],
  _debugEnd: [Function: _debugEnd],
  hrtime: [Function: hrtime],
  cpuUsage: [Function: cpuUsage],
  dlopen: [Function: dlopen],
  uptime: [Function: uptime],
  memoryUsage: [Function: memoryUsage],
  binding: [Function: binding],
  _linkedBinding: [Function: _linkedBinding],
  _events: 
   { newListener: [Function],
     removeListener: [Function],
     warning: [Function],
     SIGWINCH: [ [Function], [Function] ] },
  _rawDebug: [Function],
  _eventsCount: 4,
  domain: [Getter/Setter],
  _maxListeners: undefined,
  _fatalException: [Function],
  _exiting: false,
  assert: [Function],
  config: {},
  emitWarning: [Function],
  nextTick: [Function: nextTick],
  _tickCallback: [Function: _tickDomainCallback],
  _tickDomainCallback: [Function: _tickDomainCallback],
  stdout: [Getter],
  stderr: [Getter],
  stdin: [Getter],
  openStdin: [Function],
  exit: [Function],
  kill: [Function],
  _immediateCallback: [Function: processImmediate],
  argv0: 'node' }
'process.env': {
    NODE_ENV: JSON.stringify('production')
}

评价:不好,会overwrite整个process.env对象,破坏进程环境,导致破坏兼容性。 原始的process.env对象包含如下内容 ,包含了当前进程的很多信息。

{ TERM: 'xterm-256color',
  SHELL: '/bin/bash',
  TMPDIR: '/var/folders/lw/rl5nyyrn4lb0rrpspv4szc3c0000gn/T/',
  Apple_PubSub_Socket_Render: '/private/tmp/com.apple.launchd.dEPuHtiDsx/Render',
  USER: 'frank',
  SSH_AUTH_SOCK: '/private/tmp/com.apple.launchd.MRVOOE7lpI/Listeners',
  __CF_USER_TEXT_ENCODING: '0x1F5:0x19:0x34',
  PATH: '/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Applications/Wireshark.app/Contents/MacOS',
  PWD: '/Users/frank/Desktop/corporation/weidian-crm',
  XPC_FLAGS: '0x0',
  XPC_SERVICE_NAME: '0',
  SHLVL: '1',
  HOME: '/Users/frank',
  LOGNAME: 'frank',
  LC_CTYPE: 'zh_CN.UTF-8',
  _: '/usr/local/bin/node' }
'process.env.NODE_ENV': JSON.stringify('production')

评价:好。因为仅仅对NODE_ENV值进行修改,不会破坏完整进程,也不会破坏兼容性。

如何使用DefinePlugin添加配置文件,构建期间自动检测环境变化,也就是如何根据NODE_ENV引入配置文件?

情景:开发阶段的接口地址往往与生产阶段的接口地址是不一致的。例如开发时是development.foo.com,而生产时是production.foo.com,如果需要打包发布,那么需要手动去替换域名或者是一个分支维护一个专门的配置文件,这两种方式是非常笨重的。

  • 手动替换

文件效率低下,每次在development和production间切换都需要进行配置文件的更新,容易出错

  • 配置文件

相对手动替换高级一些,但是不能一次性查看development和production的全部配置信息,需要在分支间切换,效率低下,且不适用于多种环境的配置

  • webpack.DefinePlugin()

全局配置文件,自动检测环境变化,效率高效。

webpack的DefinePlugin正是为我们解决这样一个问题,它维护一个全局的配置文件,在编译期间会自动检测process.env.NODE_ENV,根据当前的环境变量去替换我们的接口域名。

下面我将以一个实例来介绍如何正确使用webpack.DefinePlugin。

/config/api.js

const NODE_ENV=process.env.NODE_ENV;
const config={
     production: {
        FOO_API: 'production.foo.api.com',
        BAR_API: 'production.bar.api.com',
        BAZ_API: 'production.baz.api.com',
     },
     development: {
        FOO_API: 'development.foo.api.com',
        BAR_API: 'development.bar.api.com',
        BAZ_API: 'development.baz.api.com',
     },
     test: {
        FOO_API: 'test.foo.api.com',
        BAR_API: 'test.bar.api.com',
        BAZ_API: 'test.baz.api.com',
     }
}
module.exports=config[NODE_ENV];

webpack.dev.conf.js/webpack.prod.conf.js/webpack.test.conf.js

const apiConfig=require('./config/api');
const webpackConfig={
    plugins: [
        new webpack.DefinePlugin({
            API_CONFIG: JSON.stringify(apiConfig);
        })
    ]
}
...

custom.component.vue

<template>
...
</template>
<script>
// 这里也可以访问到API_CONFIG
export default {
    // 这里无论是data函数,methods对象,computed对象,watch对象,都可以访问到API_CONFIG;
   data() {
       return {
           fooApi: API_CONFIG.FOO_API,
           user:{
               id: '',
               name: '',
           },
           hash: '',
        } 
    },
    computed: {
        userAvator() {
            return `${API_CONFIG.BAR_API}?id=${user.id}&name=${user.name}`
        }
    },
    methods: {
        uploadImage() {
            api.uploadImage({user: `${API_CONFIG.BAZ}\${hash}`})
                 .then(()=>{})
                 .catch(()=>{})
        }
    }
}
</script>

上述仅仅适用于vue-cli2.0时代,vue-cli3.0引入了webpack-chain,配置方式大大不同,下文将给出示例。

如何在vue.config.js中,使用使用DefinePlugin添加配置文件,构建期间自动检测环境变化,也就是如何根据NODE_ENV引入配置文件?

vue.config.js

const apiConfig=require('./config/api');

module.exports={
    chainWebpack: config=> {
        config
            .plugin('define')
            .tap(args=> { 
                args[0].API_CONFIG=JSON.stringify(apiConfig)
                return args
            })
    }
}

需要注意的是,在vue-cli3.0中,我们不能直接SET NODE_ENV=production或者EXPORT NODE_ENV=production。 因为vue-cli-servive有3种模式,serve默认为development,build为production,若想修改vue-cli-service包中的NODE_ENV,需要通过vue-cli-service serve --mode production进行切换。 就像下面这样:

{
  "scripts": {
    "dev": "vue-cli-service serve", // mode默认为development 
    "production": "vue-cli-service serve --mode production", 
  },
}

注意:我们只能在development, production或者test 3个模式下进行切换,不能引入类似preproduction之类的自定义node环境,但是实际上这3个环境已经足以满足大多数的开发情况。

为什么vue-cli 3.0中的DefinePlugin可以用config.plugin('define')修改入参?

在源码文件base.js中,有下面的代码:

    webpackConfig
      .plugin('define')
        .use(require('webpack/lib/DefinePlugin'), [
          resolveClientEnv(options)
        ])

这一点很关键!我们在vue.config.js中拿到的config.plugin('define'),实际上时vue-service内部创建的webpack.DefinePlugin实例的引用 ! 明确了这一点,我们在以后增强webpack默认插件配置时,需要先到vue-service的源码中寻找一番,看看有没有对应plugin的引用,若有,必须根据vue-service定义的名字直接引用,否则会修改失败。

期待和大家交流,共同进步,欢迎大家加入我创建的与前端开发密切相关的技术讨论小组:

SegmentFault专栏:趁你还年轻,做个优秀的前端工程师

知乎专栏:趁你还年轻,做个优秀的前端工程师

Github博客: 趁你还年轻233的个人博客

微信公众号: 大大大前端 / excellent_developers

努力成为优秀前端工程师!

sPDF 是一个使用Javascript语言生成PDF的开源库。你可以在Firefox插件,服务端脚本或是浏览器脚本中使用它。

浏览器兼容性:

IE 10, Firefox 3+, Chrome, Safari 3+, Opera,未来将兼容 IE 10 以下版本,对于 IE10 以下的版本会使用 Downloadify 来实现文件下载功能。

支持文件格式:

  • 文本,数字,图形,图片,同时你可以自由的编辑标题或者其它类型元素。
  • 支持互动的内容制作,例如,你可以输入文字或者数字,然后jsPDF帮助生成最后的PDF内容。
  • 支持现代浏览器,如果是老式浏览器的话,可以很好的使用flash来实现兼容。不过貌似支持Firefox不是很好,如果要查看演示,使用Chrome吧!

官网地址:http://jspdf.com https://parall.ax/products/jspdf

项目地址:https://github.com/MrRio/jsPDF