前言
想必大家都看过斗鱼直播吧?这次在下使用从github上面摸下来的API,为大家重现一下斗鱼网站的搭建,使用vue-cli-webpack来实现。
文章内容较多,可以慢慢看
声明
本文章所用API均从网络获取,本文作者不承担任何法律责任,请阅读本文的小伙伴们用于学习用途,不能用于商业!
如有侵权行为,请与作者联系,作者将于2日内删除。
开始之前
本文假设您学习了以下相关知识
nodejs
webpack
vue
vue-cli
vue-router
效果
pc端
移动端
开始
好,扯了这么久的淡,该开始构建项目了
项目初始化
初始化文件夹
打开一个新文件夹,在命令行输入:
vue init webpack
如果显示vue not found,那么该去下载vue-cli,如果webpack未找到就去下载webpack
到这一步以后就ctrl + c ,退出终端
安装依赖
退出命令行之后,输入以下指令:
npm install chromedriver --chromedriver_cdnurl=http://cdn.npm.taobao.org/dist/chromedriver
chromedriver 是安装必备的包,镜像好像有问题,我们提前装一下。
npm install
GFW不是吹的,外网真的很慢,大家泡杯茶慢慢等
趁着等的时候,我们来下载几个样式和图片,运行
git clone https://github.com/YexChen/douyu_assets.git
来下载assets文件,覆盖 assets文件夹到 项目文件/src 中
npm i -S lib-flexible
npm i -S axios
npm i -S vue-axios
我们还需要lib-flexible来解决移动端适配的问题,axios和vue-axios来方便请求我们的数据
引入安装的包
大家可以进入到src目录下,这里简要介绍下各个文件的功能
assets 放静态内容的地方,但是支持预编译
components 放组件的地方,当然也可以别具一格随便创个文件夹代替之
router/index.js router文件夹是放路由的地方,index.js是我们的根路由
app.vue vue-cli帮我们生成好的一个组件(根组件),没什么好稀奇的
main.js webpack的入口文件,聚合vue应用里面的东西
我们来修改main.js,参照下图:
红线区域我们引入了移动适配的lib-flexible,和font-awesome,style公共样式
接下来,我们来引入axios和vue-axios,请看下图:
这里我们引入了vue-axios和axios,并通过vue.use来进行绑定
跑起项目
准备工作已经做完了,接下来可以跑起项目了:
npm run dev
根据命令行的提示打开网页即可看到效果:
好的,我们的项目初始化就到这里了。
配置映射和测试斗鱼API
配置映射
来到根目录下的 config/index.js 这里是配置开发,构建,及路由映射的地方
如图修改proxyTable中内容,这里解释一下几个参数:
target : 目标地址,
changeOrigin : 是否跨域,
pathRewrite : 键值对中用值替换键的值,其中^是正则中表示开始的符号
随手请求一个API
进入src/App.vue,如下修改文件:
created是我们的生命钩子函数,vue实例在created阶段会执行里面的代码。
this.$http相当于this.axios,$http的具体实现可以去node_modules里面看,很简单的
重启webpack服务,看下效果
ctrl+c
npm run dev
看到以上效果的话,证明数据请求成功了。
思维导图解析
我们要写的应用较为复杂,写vue的项目就是这样,需要清晰的思想,不然很容易崩溃,最后重来
好,接下来为大家讲解一下我们的组件:
Root是根组件,一切的源(废话)
App,应用组件,对应src/App.vue
Side-menu :侧边栏,因为较为容易且不需要改变单页路由来显示不同内容,所以直接放在app组件里边
router-view : 这是vue-router的子路由显示面板,通过src/router/index.js来控制
home : 主页视图文件
public : 公用组件,亦可在其他页面使用,降低工作量
AppHeader : 应用头部组件
Loading : 加载中的组件,就一张gif
侧边栏SideMenu组件
在src/components目录中新建一个文件,名为SideMenu.vue,修改内容为:
<template lang="html">
<div class="side-menu" @click = "hideSide">
<ul>
<router-link v-for = "(item,index) in list" :to="item.url" :key = "index">
{{item.title}}
<i class = "icon-chevron-right"></i>
</router-link>
</ul>
</div>
</template>
<script>
export default {
data(){
return {
list : [
{title : "首页",url : "/"},
{title : "全部分类",url : "/category"}
]
}
},
methods : {
hideSide(){
this.$emit("hide")
}
}
}
</script>
<style lang="css">
.side-menu {
background: rgba(10,10,10,.3);
height: 100%;
position: fixed;
width: 100%;
top: 0;
padding-top: 44px;
z-index: 11;
}
.side-menu ul {
width: 70%;
background: #282828;
height: 100%;
border-top: 1px solid #222;
}
.side-menu ul li {
height: 50px;
border-bottom: 1px dotted #333;
font-size: 14px;
line-height: 50px;
padding: 0 30px 0 20px;
color: #9a9a9a;
}
.side-menu ul li i {
float: right;
line-height: 50px;
}
</style>
这里解释一下文件里面的内容:
文件分为三大块
template
script
style
这些内容通过script中node的export方法推出去
其中template渲染了几个router-link,用来跳转路由
script定义了data和method
style写了样式
然后打开src/App.vue,修改里面的内容,追加下图内容:
好的,我们的SideMenu组件就注册完成了。
搭建router-view内容
好的,我们接下来做router-view的内容
bus-中央总线
在做之前,我们需要了解一个新的概念-bus,又称中央总线
好的,又是之前那张思维导图,不过是不是多出了三台车呢?
没错,这就是我们的bus。
当appheader想加载侧边栏时,是不能穿越徒步穿越山和大海的,老司机还是要开车的是不是
这个时候我们坐公交就行了,告诉app,把我给拉出来
当然,side-menu和app之间相距不远,父子组件是可以直接绑定的
在src目录下创建bus.js,内容为
这是我们的bus,说白了就是一个对象,只不过借用了vue的消息管道,大家也可以自己写个管道
制造home主页路由
在src目录下创建pages目录,这个目录我们用来存放router-vue的内容
然后我们在src/pages/下创建一个home.vue组件,用来做home的内容,写下以下内容:
<template lang="html">
<div class="mr-root">
<app-header>
<p class = "title">斗鱼TV</p>
</app-header>
<loading v-if="showLoading"></loading>
</div>
</template>
<script>
import Public from "../public"
export default {
mixins : [
Public
],
data(){
return {
showLoading : true
}
}
}
</script>
<style lang="css" scoped>
</style>
解释一下,这里使用了app-header和loading组件,由Public导入(等会写)。
mixins是一个混合物,能够自动把模组分析,加载到当前实例中。
data中 showLoading和v-if配合使用,用来关闭loading效果
如果不清楚的话可以看下思维导图
public公用模组
public是一个模组集合,我们在开发的时候可能不同页面要使用相同的组件,这时就需要public打包处理了。
在src中新建public.js,内容如下:
import AppHeader from './components/AppHeader'
import Loading from './components/Loading'
export default{
components: {
AppHeader,
Loading
}
}
上文我们导入了AppHeader和Loading模块,并设置了默认导出
好,那么我们来写两个子模组,
AppHeader
在components中新建一个文件AppHeader.vue,代码如下
<template lang="html">
<header>
<i class = "icon-reorder" @click = "showSlide"></i>
<slot></slot>
<i class = "icon-user"></i>
</header>
</template>
<script>
import bus from "../bus"
export default {
methods : {
showSlide(){
bus.$emit('showSide')
}
}
}
</script>
<style lang="css" scoped>
header {
height: 44px;
background: #333;
position: fixed;
left: 0;
right: 0;
top: 0;
z-index: 100;
padding: 0 15px;
color: #fff;
line-height: 44px;
font-size: 16px;
}
header i {
color: #999;
}
.title {
margin-left: 15px;
display: inline-block;
}
.icon-user {
float: right;
line-height: 44px;
}
</style>
定义了基本的头部,给加载更多绑定了一个事件,通过bus进行传递,由app.vue来实现
Loading组件
src/components/里面新建一个Loading.vue,代码如下:
<style lang="css">
.loading {
height: 100%;
position: fixed;
z-index: 10;
width: 100%;
background: #062734;
opacity: .4;
}
.loading img {
width: 100%;
height: auto;
position: absolute;
top: calc(50% - 140px);
}
</style>
就添加了一张gif图而已,非常简单的
bus事件的处理
好的,既然我们的appheader已经发车了,那么应该在app.vue根路由里面开个公交车站,来接收巴士:
修改App.vue:
<template>
<div id="app">
<transition name = "side">
<side-menu v-show = "show" @hide = "hideSide"></side-menu>
</transition>
<router-view/>
</div>
</template>
<script>
import SideMenu from "./components/SideMenu"
import bus from "./bus"
export default {
name: 'app',
components : {
SideMenu
},
created(){
this.$http.get(`/douyuapi/RoomApi/live?offset=1&limit=20`).then(res=>{
console.log(res.data.data);
})
},
data(){
return {
show : false
}
},
mounted () {
bus.$on("showSide",this.side)
},
methods : {
side(){
this.show = !this.show
},
hideSide(){
this.show = false
}
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
修改路由
修改根路由/src/router/index.js为:
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/pages/Home'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
}
]
})
增加HomeItem
好的,我们有了以上功能以后呢,还需要在斗鱼主页中增加聊天室列表,在components目录中新建文件HomeItem.vue
<template lang="html">
<div class="mr-item">
<router-link :to="'/room/'+room.room_id">
<img :src="room.room_src" alt="">
<div class="room-info">
<span class = "nickname">{{room.nickname}}</span>
<span class = "count">
<i class = "icon-group"></i>
{{room.online | number}}
</span>
</div>
<div class="room-title">
<i class = "icon-desktop"></i>
{{room.room_name | message}}
</div>
</router-link>
</div>
</template>
<script>
export default {
props : ["room"]
}
</script>
<style lang="css" scoped>
.mr-item {
margin-top: 10px;
float: left;
width: 4.4rem;
margin-right: .3rem;
position: relative;
}
.mr-item img {
width: 100%;
height: 2.6rem;
border-radius: 5px;
}
.room-info {
position: absolute;
bottom: 33px;
color: #fff;
padding: 0 5px;
left: 0;
right: 0;
overflow: hidden;
background: rgba(10,10,10,.5);
line-height: 24px;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
.room-info .count {
float: right;
}
.room-title {
line-height: 30px;
}
</style>
上文中我们定义了两个过滤器,接下来我们在main.js中定义几个过滤器
main.js定义过滤器
打开main.js,在Vue.config.productionTip = false后,如图写下过滤器代码
home.vue中加载homeitem
我们需要在Home.vue中加载HomeItem,修改home.vue为
<template lang="html">
<div class="mr-root">
<app-header>
<p class = "title">斗鱼TV</p>
</app-header>
<loading v-if="showLoading"></loading>
<home-item v-for = "(room,index) in roomList" :room = "room" :key = "index">
</home-item>
<p v-if = "error">加载失败,请稍后再试...</p>
<div class="clear"></div>
<div class="load-more">
<span @click = "loadMore">点击加载更多</span>
</div>
</div>
</template>
<script>
import Public from "../public"
import HomeItem from "../components/HomeItem"
export default {
mixins : [
Public
],
data(){
return {
showLoading : true,
error : false,
roomList : [],
page : 0,
pageSize : 20
}
},
components : {
HomeItem
},
created(){
this.getInfo(this.page)
},
methods : {
getInfo(page){
this.$http.get(`/douyuapi/RoomApi/live?offset=${page*this.pageSize}&limit=${this.pageSize}`)
.then(res=>{
this.error = false
this.roomList = this.roomList.concat(res.data.data)
setTimeout(()=>{
this.showLoading = false
},1000)
})
.catch(err=>{
this.error = true
this.showLoading = false
})
},
loadMore(){
this.page++
this.getInfo(this.page)
}
}
}
</script>
<style lang="css">
.mr-content {
padding: 44px 0 0 .3rem;
overflow: hidden;
}
.load-more {
margin: 10px;
text-align: center;
}
.load-more span {
display: inline-block;
line-height: 30px;
padding: 0 20px;
border-radius: 10px;
border: 1px solid #000;
}
</style>
现在看下页面,是不是已经出来了呢?
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。