天小編我博客時,看到了一位大神分享的自己的音樂播放器,小編我也COPY了一份分享給頭條上的小伙伴,能搞定這個音樂播放器找份工作還是沒壓力的,畢竟這個項目還是挺好的,正在學(xué)習(xí)web前端網(wǎng)頁制作的伙伴們福利來了,小編我我項目整理了一下,順便寫下這篇文章(項目地址文末有)
做完的項目如圖所示:
實現(xiàn)的功能
1、首頁
2、底部播放控件
3、播放頁面
4、播放列表
5、排行榜
6、音樂搜索
輸入搜索關(guān)鍵詞,點擊放大鏡圖標
7、側(cè)邊欄
API
感謝作者把api整理的這么好(點個贊)
https://binaryify.github.io/NeteaseCloudMusicApi/#/?id=%e6%90%9c%e7%b4%a2%e9%9f%b3%e4%b9%90
目錄結(jié)構(gòu)
1、輪播圖
首先感謝作者ShanaMaid/vue-image-scroll開源的代碼,我把代碼copy下來自己進行了一點修改(沒有手指滑動效果),因為這是移動端,少不了的手指滑動切換,所以添加了vue-touch(偷偷告訴你,vue-touch的next分支還是支持vue2.0的)。
2、歌曲操作(喜歡,分享,加入播放列表)動畫、播放列表展開與刪除歌曲動畫
transition-group
一組過度動畫,這里有個小坑的,之前看官網(wǎng)列表過渡的栗子,給每一項設(shè)置唯一的key值,一般都會用index。所以在做的時候就把index傳給key,結(jié)果過渡老是不對,后來換成對應(yīng)的item
就正常了(生無可戀臉)。
3、直線進度條、弧形進度條
西班牙建筑大師曾說過:“直線屬于人類,曲線則歸于上帝”。在這里我大膽的使用了弧形來作為進度條,(幾大熱門音樂APP貌似還沒有弧形進度條)。
這里我用到了Vue的綁定內(nèi)聯(lián)樣式
5、圖片懶加載
使用了vue-lazyload插件
用法:
6、歌詞滾動與高亮
因為api提供的歌詞包括時間,如:
[03:57.280]原諒我這一生不羈放縱愛自由
所以首先要進行字符串切割:
然后在播放的監(jiān)聽事件中與播放的當前做對比:
到這就ok了
7、vuex狀態(tài)管理
推薦官方調(diào)試工具 devtools extension
想進一步理解vuex,可以看這篇博客vuex學(xué)習(xí)實踐筆記
之前看到好多人寫的vuex,把整個項目的數(shù)據(jù)放到了一個state里,導(dǎo)致應(yīng)用的所有狀態(tài)集中到一個很大的對象。但是,當應(yīng)用變得很大時,store 對象會變得臃腫不堪。
所以我建議(個人見解,輕噴):將 store 分割到模塊(module)。每個模塊擁有自己的 state、mutation、action、getters。這樣方便管理與后期的維護。
車已到站??。
入門只是學(xué)習(xí)的開始,這時你可以運用你的技能,實現(xiàn)各種需求。 在實現(xiàn)各種需求時,你會遇到更多各種各樣的問題。有些問題可能已經(jīng)不是單純寫JS代碼能很好解決的了。 你可能需要配合一些框架,或一些工具,或一些設(shè)計模式, 或一些業(yè)務(wù)知識。
隨著解決更多問題, 你也會積累更多的知識。之后會是漫長的積累過程, 通過項目經(jīng)驗、看書、看博客、思考、討論等等,積累你的經(jīng)驗和知識。
你需要持之以恒和刨根問底。總之,之后就看你自己的了。還是和前提差不多的那句話:你需要主動。
最后再來提點建議:
寫JS的最好辦法..是不停的重復(fù)練習(xí)....其實很多人反映...學(xué)JS的時候很難..但是有些人認為..它沒有服務(wù)端的語言難.或者說..根本就不屑的學(xué)這種語言...什么破語言..老出各種各樣的錯誤...瀏覽器還不兼容..但是...學(xué)好JS對你未來的web開發(fā)有至關(guān)重要的作用....我覺得.JS是奠定RIA的基礎(chǔ)...或者說..客戶端的開發(fā)RIA的基礎(chǔ).
這個音樂播放器項目到這里就算是做完了,想要完整代碼自己學(xué)習(xí)練手的小伙伴進我的群自助領(lǐng)取,已經(jīng)上傳到群文件里了:640633433,歡迎初學(xué)和進階中的小伙伴。
如果項目有哪些缺陷,歡迎在評論區(qū)指正!
過閱讀這篇文章,可以學(xué)習(xí)到如何使用DefinePlugin插件使得前端項目更加工程化,說清晰點就是如何使用這個插件,在編譯階段根據(jù)NODE_ENV自動切換配置文件,提升前端開發(fā)效率。
DefinePlugin中的每個鍵,是一個標識符或者通過.作為多個標識符。
這些值將內(nèi)聯(lián)到代碼中,壓縮減少冗余。
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不是直接的文本值替換,它的值在字符串內(nèi)部必須包括實際引用。典型的情況是用雙引號或者JSON.stringify()進行引用,'"production"',JSON.stringify('production')。
重點:在vue-cli創(chuàng)建的項目中,凡是src下的文件,都可以訪問到VERSION這個變量,例如main.js,App.vue等等
我們現(xiàn)在看一下上面的幾種類型的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"},
在代碼中,我們一般會有以下幾種用途:
可以控制新特性和實驗特性的開關(guān)。
new webpack.DefinePlugin({
'NICE_FEATURE': JSON.stringify(true),
'EXPERIMENTAL': JSON.stringify(false),
})
process: {
env: {
NODE_ENV: JSON.stringify('production')
}
}
評價:非常不好,會overwrite整個process對象,僅僅保留新的NODE_ENV,破壞進程。 原始的process對象包含如下內(nèi)容 ,包含了當前進程的很多信息。
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對象,破壞進程環(huán)境,導(dǎo)致破壞兼容性。 原始的process.env對象包含如下內(nèi)容 ,包含了當前進程的很多信息。
{ 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值進行修改,不會破壞完整進程,也不會破壞兼容性。
情景:開發(fā)階段的接口地址往往與生產(chǎn)階段的接口地址是不一致的。例如開發(fā)時是development.foo.com,而生產(chǎn)時是production.foo.com,如果需要打包發(fā)布,那么需要手動去替換域名或者是一個分支維護一個專門的配置文件,這兩種方式是非常笨重的。
文件效率低下,每次在development和production間切換都需要進行配置文件的更新,容易出錯
相對手動替換高級一些,但是不能一次性查看development和production的全部配置信息,需要在分支間切換,效率低下,且不適用于多種環(huán)境的配置
全局配置文件,自動檢測環(huán)境變化,效率高效。
webpack的DefinePlugin正是為我們解決這樣一個問題,它維護一個全局的配置文件,在編譯期間會自動檢測process.env.NODE_ENV,根據(jù)當前的環(huán)境變量去替換我們的接口域名。
下面我將以一個實例來介紹如何正確使用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函數(shù),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
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環(huán)境,但是實際上這3個環(huán)境已經(jīng)足以滿足大多數(shù)的開發(fā)情況。
在源碼文件base.js中,有下面的代碼:
webpackConfig
.plugin('define')
.use(require('webpack/lib/DefinePlugin'), [
resolveClientEnv(options)
])
這一點很關(guān)鍵!我們在vue.config.js中拿到的config.plugin('define'),實際上時vue-service內(nèi)部創(chuàng)建的webpack.DefinePlugin實例的引用 ! 明確了這一點,我們在以后增強webpack默認插件配置時,需要先到vue-service的源碼中尋找一番,看看有沒有對應(yīng)plugin的引用,若有,必須根據(jù)vue-service定義的名字直接引用,否則會修改失敗。
期待和大家交流,共同進步,歡迎大家加入我創(chuàng)建的與前端開發(fā)密切相關(guān)的技術(shù)討論小組:
SegmentFault專欄:趁你還年輕,做個優(yōu)秀的前端工程師
知乎專欄:趁你還年輕,做個優(yōu)秀的前端工程師
Github博客: 趁你還年輕233的個人博客
微信公眾號: 大大大前端 / excellent_developers
努力成為優(yōu)秀前端工程師!
sPDF 是一個使用Javascript語言生成PDF的開源庫。你可以在Firefox插件,服務(wù)端腳本或是瀏覽器腳本中使用它。
IE 10, Firefox 3+, Chrome, Safari 3+, Opera,未來將兼容 IE 10 以下版本,對于 IE10 以下的版本會使用 Downloadify 來實現(xiàn)文件下載功能。
*請認真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。