我也写了个低仿网易云音乐播放器,这是我的感受
开发一个基于Vue的低仿mac网易云音乐web播放器及开后感
前言
感谢大佬提供的api
项目简介
技术栈
webpack4(打包工具, 这个项目中我并没有用vue-cli, 因为想体验下自己搭建webpack有多痛苦:( )
element-ui (用到了其中轮播图, 表格等部分组件)
sass (css预处理器)
Vue全家桶
辅助工具 & 插件
better-scroll(歌词滚动)
xgplayer (西瓜视频播放器)]
postcss-pxtorem (px转rem工具, 自己搭webpack 加这玩意儿实在太费劲了)
charles (抓包工具)
axios
项目功能
登录(账号密码 & 网易云Id)
音乐播放
视频播放
歌单 & 专辑页
搜索结果, 搜索面板
播放记录 & 播放列表
排行榜 & 最新音乐 & 个性推荐
我的收藏歌单列表
歌词, 评论, 相关推荐
有些功能相较于网易云音乐是残疾版, 因为提供的接口是2年前的, 所以有些不支持现在的业务逻辑
项目预览
跑一下
cnpm i
npm run start 本地预览
npm run build 打包
npm run analyz 打包文件分析
npm run release 部署到服务器
webpack
这个项目写到目前为止, 我花费精力最多是webpack相关以及打包优化相关的内容(这里的精力 = 花费时间 / 代码量). 脚手架 很方便, 但是我还是想体验下从0搭建一个小项目的webpack配置
个人觉得自己配置webpack起手式, 就是碰到问题去搜, 逐个击破, 像我这样的小白千万不要代码还没开始写就想撘出个脚手架级别的配置, 像这样...
搜着搜着 就这样了
简述打包优化历程
先上一张啥也没有优化时的图片
呵呵呵呵... 一个破音乐播放器 6.1M 48.9s
开始优化
在生产环境的配置文件中, 加上(
mode: production
), 有了这句话, webpack会自动帮你压缩代码, 且效果非常显著
2. 使用gzip
, 这一步需要在webpack使用compression-webpack-plugin
插件
plugins: [
...
new CompressionWebpackPlugin({
algorithm: 'gzip',
test: /\.js(\?.*)?$/i,
threshold: 10240,
minRatio: 0.8
}),
以及nginx配置文件中配置
http{
....
gzip on;
gzip_comp_level 6;
gzip_types text/xml text/plain text/css application/javascript application/x-javascript application/rss+xml;
gzip_disable "MSIE[1-6]\.";
使用过程中我发现webpack不配置gzip压缩仅配置nginx, 在最终访问项目时, 拿到的文件也是gzip格式的. 查阅后,才知道 gzip 服务端也能进行压缩, 但是如果客户端直接把压缩好的gzip文件传到服务端 可以节省服务端在收到请求后对文件进行的压缩的性能损耗
webpack端配置gzip压缩
webpack端不配置gzip压缩
使用
ParallelUglifyPlugin
, 开启多个子进程并行压缩 节省压缩时间, 并且去除调试日志
plugins:[
...
new ParallelUglifyPlugin({
cacheDir: '.cache/',
uglifyJS:{
output: {
comments: false
},
warnings: false,
compress: {
drop_debugger: true, // 去除生产环境的 debugger 和 console.log
drop_console: true
}
}
}),
将一些依赖 用cdn链接引入, 并且使用dns预解析
// webpack.prod.conf.js
externals:{
vue: 'Vue',
'vue-router': 'VueRouter',
vuex: 'Vuex',
axios: 'axios',
},
// index.html
<head>
//使用dns预解析(将域名解析成ip是很耗时的)
<link rel="dns-prefetch" href="//cdn.bootcss.com">
<link rel="dns-prefetch" href="//cdnjs.cloudflare.com">
</head>
...
<body>
//这串奇怪的代码html-webpack-plugin插件会解析的
<% if ( process.env.NODE_ENV === 'production' ) { %>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.runtime.min.js"></script>
<script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.1.2/vuex.min.js"></script>
<%} %>
使用
splitChunks
, 这个插件不需要install, 直接使用即可, 它的作用是将公共依赖单独提取出来,避免被重复打包, 具体细节可以看这
splitChunks: {
chunks: 'all',
cacheGroups: {
xgplayer: {
test: /xgplayer/,
priority: 0,
name: 'xgplayer'
},
vendor: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
name: 'vendors',
minChunks: 10
}
}
}
注意下
'xgplayer'
, 这是个视频播放器库, 我这里单独配置也是为了优化打包, 第7点会说
至此, 我的初步优化已经完成了, 那还有没有优化空间呢, 这里可以先用下打包分析工具
webpack-bundle-analyzer
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
plugins: [
// 打包分析
new BundleAnalyzerPlugin(
{
analyzerMode: 'server',
analyzerHost: '127.0.0.1',
analyzerPort: 8888,
reportFilename: 'report.html',
defaultSizes: 'parsed',
openAnalyzer: true,
generateStatsFile: false,
statsFilename: 'stats.json',
statsOptions: null,
logLevel: 'info'
}
),
],
从图中可以清晰的看到打包后代码的结构,
moment
这个库中有很多的语言包, 可以用webpack自带的ContextReplacementPlugin
插件进行过滤
//过滤moment其他语言包 打包体积缩小200kb
new webpack.ContextReplacementPlugin(
/moment[/\\]locale$/,
/zh-cn/,
),
xgplayer
也占用了很大的体积, 那如何优化呢? 这里引入一个'prefetching'
概念, 其思想就是将一些文件在浏览器资源空闲时去分配资源下载, 从业务逻辑考虑, 在用户初次访问项目时, 是不需要用到视频库的资源的, 所以可以把浏览器资源分配给首屏需要的文件. 在业务逻辑中这样配置
watch: {
url: {
handler(newV, oldV) {
if (newV && newV !== oldV) {
if (!this.player) {
import(/* webpackPrefetch:true */'xgplayer').then((module) => {
xyPlayer = module.default;
this.initVideo()
//这里这样写的目的是,如果有用户通过url直接打开视频页, 那我也可以同步加载完视频库文件后, 再初始化视频组件
})
} else {
this.player.src = newV
this.player.reload()
}
}
},
immediate: !0
}
}
至至至此, 我的第二步优化已经完成了, 那还有没有优化空间呢, 这里可以用下chrome浏览器的调试工具
coverage
, 这个工具可以帮你分析出文件利用率(即加载的文件中, 真正用到的代码有哪些), 附上一张我优化好的截图
首屏加载的文件利用率只有35%,该部分优化的核心思想就是将首屏看不见的资源全部异步导入, 例如采用component: () => import('xxxx')
路由懒加载, 将需要用户交互才会用到的逻辑代码单独封装,按需加载,例如
//click.js
function click() {
....
}
export default click
//main.js
document.addEventListener('click', () => {
import('./click').then(({ default: click }) => {
click()
})
})
当然这样做会很繁琐, 不过对于追求极致体验的应用来说, 也是个路子...
附上两张优化完状态, 当然 这不是还不是最佳的状态...
总结
不用脚手架从0搭webpack及优化打包能让自己接触到很多业务代码以外的东西, 这些东西也是前端职责中很重要的但也常常被忽视的模块, 过程很艰难但也充满意义.
作者:stormsprit
来源:juejin.cn/post/6844904045765722125