Vue.Js开源:bilibili-vue-前端 vue + 后端 koa,全栈式开发 bilibili 首页
wangjunji
8年前
<h2>前言</h2> <p>为什么是写哔哩哔哩(俗称B站)呢?其一是因为本人是B站的重度使用者,每周基本上都会在B站上看看火影忍者、暴走大事件等。另外一个原因呢,就是B站首页很美观大方,而且处处充满了细节。所以对于实践确实是一个不错的项目。目前的技术栈主要的采用的是前端Vue2+后端Koa2的前后端分离的方式,语法全部使用ES6/7。关于数据也来自真实的数据接口。</p> <h2>技术栈</h2> <p>前端:vue2 + vuex + webpack + ES6/7 + stylus + nginx</p> <p>后端:koa2</p> <h2>项目运行</h2> <p>源码地址: <a href="/misc/goto?guid=4959747982184304088" rel="nofollow,noindex">https://github.com/lybenson/bilibili-vue</a></p> <p>如何运行</p> <p>运行前请先安装 nodejs</p> <p>clone 项目到本地</p> <pre> git clone https://github.com/lybenson/bilibili-vue.git</pre> <p>前端运行</p> <pre> cd bilibili-vue npm install npm run dev</pre> <p>后端运行</p> <pre> cd bilibili-vue/bilibili-api npm install npm run dev</pre> <p>为了确保运行正确,请先运行后端服务。再运行前端,之后访问 <a href="/misc/goto?guid=4958965640789895086" rel="nofollow,noindex">http://localhost:8080</a></p> <h2>组件</h2> <p>根据首页的各模块的功能不同,划分出了共20多个可复用的组件。具体请看下方</p> <pre> ├── banner //轮播组件 │ ├── Banner.vue │ └── BannerItem.vue ├── common // 公共组件 │ ├── BHeader.vue │ ├── BMenu.vue │ ├── BMenuItem.vue │ ├── PostMaterial.vue │ ├── Search.vue │ └── TopContainer.vue ├── content // 主内容组件 │ └── BContent.vue ├── contentRow // 单个分类的组件 │ ├── BContentRow.vue │ ├── BRowBody.vue │ ├── BRowHead.vue │ ├── BRowItem.vue │ ├── BRowRank.vue │ └── BRowRankBody.vue ├── contentTop // 页面顶部组件 │ ├── BContentTop.vue │ └── BContentTopItem.vue ├── live //直播所在的组件 │ ├── BLive.vue │ ├── BLiveItem.vue │ ├── BLiveRank.vue │ └── BLiveRankItem.vue ├── nav //右侧导航条组件 │ ├── BNavSide.vue │ └── smooth-scroll.js └── promote // 推广组件 ├── BPromote.vue └── BPromoteItem.vue</pre> <p>组件的原则就是尽量将复杂的UI布局划分成单个小的UI组件,逻辑处理也被划分成更多的单个小的逻辑。数据流动采用的单向数据流动。子组件的数据更多的是来自于父组件,父组件的数据主要是其本身发起的 ajax 请求。本项目中 ajax 请求库使用的是 axios 。</p> <h2>状态管理</h2> <p>目前的状态管理采用 vuex 。由于 vuex 可以分多个子 module 。所以在不同模块下单独维护一份状态,同时对于一些公共的状态,比如发起网络请求的 requesting ,错误时的状态 error 则保存在根状态中,之后可以通过子模块中的 rootState.requesting 获取来赋值。</p> <p>在子模块中发送网络请求获取数据是一个异步的过程,所以将网络请求放在 actions 。每次发起网络需要经历下面的步骤</p> <ol> <li> <p>请求中</p> <pre> rootState.requesting = true //请求状态更新为true,表示请求中 commit(TYPE.XX_REQUEST) // 发送同步操作,请求中的数据处理</pre> </li> <li> <p>请求成功</p> <pre> rootState.requesting = false //请求状态更新为false,表示请求结束 commit(TYPE.XX_SUCCESS, response) //发送同步操作,处理请求成功后数据</pre> </li> <li> <p>请求失败</p> <pre> rootState.requesting = false //请求状态更新为false,表示请求结束 commit(TYPE.XX_SUCCESS, response) //发送同步操作,处理请求失败</pre> </li> </ol> <h2>动画</h2> <p>B站首页充斥着大量的动画效果。目前动画使用的主要有三种方式:</p> <ol> <li> <p>以 hover 效果触发。</p> </li> <li> <p>通过 js 触发的效果,如点击轮播图的原点时,主要通过设置css属性 transition: .2s; ,再通过 js 设置css属性 this.$refs.banner.style.marginLeft = left 。</p> </li> <li> <p>通过 vue 提供的动画方式。</p> <pre> <transition name="fade"> <div v-if="isSort"> <div class="tip"></div> <div class="custom-bg"></div> </div> </transition></pre> </li> </ol> <h2>样式</h2> <p>样式文件使用的是 stylus ,暂未使用任何 vue 现成的UI组件。需要安装 stylus-loader , stylus ,由于 vue-cli 中的 webpack 已经配置好了 stylus 了,所以只需要安装就可以了。</p> <pre> npm install stylus-loader --save-dev npm install stylus --save-dev</pre> <h2>性能优化</h2> <ul> <li> <p>图片懒加载</p> </li> <li> <p>压缩 js 、 css</p> </li> <li> <p>服务器开启 gzip</p> </li> <li> <p>浏览器缓存</p> </li> <li> <p>...</p> </li> </ul> <h2>发布</h2> <p>完成项目后将发布到自己的服务器上,首先确保服务器已安装 nodejs ,具体安装步骤不再赘述。</p> <p>后端发布</p> <p>后端采用 pm2 做项目管理</p> <p>安装 pm2</p> <pre> npm install pm2 -g</pre> <p>启动项目</p> <pre> cd bilibili-api && npm install pm2 start index.js</pre> <p>前端发布</p> <ol> <li>webpack 打包</li> </ol> <pre> npm run build</pre> <p>需要注意的是,直接运行此命令可能会导致资源无法找到的问题。所以需要对 webpack 配置做一定的修改。</p> <p>在 webpack.base.conf.js 文件中修改 publicPath 如下</p> <pre> output: { path: config.build.assetsRoot, publicPath: './', //资源的公共路径 // publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath, filename: '[name].js' }</pre> <p>这样打包后还会出现 stylus 中 background-image 资源无法找到的问题,我采用的方式修改 ExtractTextPlugin 配置,在 webpack.prod.conf.js 中修改</p> <pre> new ExtractTextPlugin('[name].[contenthash].css')</pre> <p>将分离的 css 打包路径分离到 static 文件夹之外。</p> <p>打包完成后上传到服务器 /var/www/html/bilibili 目录下。</p> <ol start="2"> <li> <p>配置nginx服务器。</p> <pre> location /bilibili { root /var/www/html; index index.html; }</pre> </li> </ol> <h2>总结</h2> <p>目前主要功能都已经完成的差不多。主要还差一个预览视频与弹幕的功能尚未完成,希望能把B站首页写完,并且会持续更新中,后面可能会加上直播等功能。</p> <p>相关截图:</p> <p>首页:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/fb850b67fbd9769eacadf7d94278c3ed.png"></p> <p>轮播:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/ca02fd0fd7471161db497f7d3c21df8f.png"></p> <p>直播:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/7e583509ff80b1605f22f2fafe3e87d8.png"> <img src="https://simg.open-open.com/show/d3513f362ca3736f6288118e9885937b.png"></p> <p>排行:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/ec07aa40833a44199ae390346386be67.png"></p> <p>游戏:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/5fc79cf642484ef8ee429e2a5c574a0c.png"></p> <p>拖拽排序与滚动效果:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/420e9d774543833d976bcfb58a6c33ec.png"></p> <p> </p> <p>项目主页:<a href="http://www.open-open.com/lib/view/home/1493172369974">http://www.open-open.com/lib/view/home/1493172369974</a></p> <p> </p>