vue2.0已经出来很久了,但是相关的实战项目却很少,以下是我个人练手项目,分享给大家,希望大家可以相互交流,若有错误,请告知。
首先我们需要用到猫眼电影提供的数据api接口,接口详情可以参照这篇文章:http://www.jianshu.com/p/9855610eb1d4。
现在开始我们的项目:
1、首先你需要安装node.js,webpack,默认读者已经安装完毕,在以上条件下,我们安装vue-cli(npm install vue-cli -g),或者使用淘宝镜像npm install -g cnpm --registry=http://registry.npm.taobao.org,对应npm改为cnpm就行。
2、所有需要的工具安装完后,我们使用vue-cli构建一个初始化的项目,打开node命令行窗口,切换到你的项目目录,然后使用 vue init webpack maoyan创建初始化的项目,目录结构如下:
我们主要的操作都在src目录下,接下来我们修改src目录如下:
在组件文件夹中增加如图所示文件,并增加一个pages文件夹,home.vue为我们的首页。
3、接下来为各组件代码编写:
a、header.vue
<template>
<div class="header">{{title}}</div>
</template>
<script>
export default{
data(){
return {
title:'猫眼电影'
};
}
}
</script>
<style scoped>
.header{
height: 1.25rem;
line-height: 2.5;
background: #e54847;
color: #fff;
text-align: center;
font-size: 0.5rem;
}
</style>
b、tab.vue
<template>
<div class="tab-group">
<ul class="tab">
<li v-bind:class="{active:!isChange}"><router-link to="/movieList">影片</router-link></li>
<li v-bind:class="{active:isChange}"><router-link to="/cinemaList">影院</router-link></li>
</ul>
<div class="active-bar" v-bind:class="{activeChange:isChange}"></div>
</div>
</template>
<script>
export default{
name:'tab-group',
watch:{
'$route':'tabActiveSlider'
},
data(){
return {
'isChange':false
};
},
methods:{
'tabActiveSlider':function(){
this.isChange=!this.isChange;
}
},
created:function(){
if(this.$route.path=='/cinemaList'){
this.isChange=true;
}
}
}
</script>
<style scoped>
.tab-group{
position: relative;
background: #fff;
margin-bottom: 0.125rem;
}
.tab{
display: box;
display: -webkit-box;
}
.tab li{
box-flex:1;
-webkit-box-flex:1;
text-align: center;
height: 1.09375rem;
line-height: 1.09375rem;
text-align: center;
font-size: 0.4375rem;
}
.tab li.active a{
color: #e54847;
}
.tab li a{
display: block;
}
.active-bar{
position: absolute;
height: 0.09375rem;
background: #e54847;
width: 50%;
left: 0;
bottom: -0.09375rem;
transition: all .3s;
}
.activeChange{
left: 50%;
}
</style>
c、movieList.vue
<template>
<div>
<ul class="movie-list">
<li class="item" v-for="item in movieList">
<a href="#">
<div class="movie-cover">
<img v-bind:src="item.img" alt="">
</div>
<div class="movie-content">
<div class="box-row">
<span class="movie-name">{{item.nm}}</span>
<span class="movie-version" v-bind:class="{v2d:!item['2d'],v3d:item['3d']}">max</span>
<span class="score" v-if="item['preSale']==1">{{item.wish}}想看</span>
<span class="score" v-else>{{item.sc}}分</span>
</div>
<p>{{item.cat}}</p>
<p>主演:{{item.star}}</p>
<p class="movie-show">{{item.showInfo}}</p>
</div>
</a>
<a href="#" class="movie-btn movie-btn-presale" v-if="item['preSale']==1">预售</a>
<a href="#" class="movie-btn" v-else>购票</a>
</li>
</ul>
<get-more></get-more>
</div>
</template>
<script>
import getMore from './getMore'
import axios from 'axios'
export default{
name:"movieList",
props:['movieList'],
components:{
getMore
}
}
</script>
<style scoped>
.movie-list{
background: #fff;
padding: 0 0.3125rem;
}
.movie-list .item{
position: relative;
border-bottom: 1px solid #E6E6E6;
padding: 0.3125rem 0;
font-size: 0.375rem;
overflow: hidden;
}
.movie-cover{
width: 21.7%;
float: left;
}
.movie-content{
width: 75%;
float: right;
}
.box-row{
margin-bottom: 0.3125rem
}
.movie-version{
position: relative;
top: 0.09375rem;
color: #8BB7CE;
border: 1px solid #8BB7CE;
border-radius: 5px;
height: 0.3125rem;
display: inline-block;
line-height: 0.28125rem;
padding: 0.0625rem;
overflow: hidden;
transform: scale(0.9);
}
.v2d:before,.v3d:before{
content: '2d';
background: #8BB7CE;
color: #fff;
padding: 0 0.09375rem;
margin-left: -0.0625rem;
margin-right: 0.0625rem;
}
.v3d:before{
content: '3d';
}
.score{
float: right;
color: #FFB400;
}
.movie-content p{
color: #666;
margin-bottom: 0.15625rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 70%;
}
.movie-content p.movie-show{
color: #999;
}
.movie-name{
font-size: 0.4375rem;
}
.movie-btn{
position: absolute;
right: 0;
bottom: 0.375rem;
background: #EF4238;
color: #fff;
border-radius: 3px;
padding: 0.09375rem 0.15625rem;
}
.movie-btn-presale{
background: #52B0EB;
}
</style>
d、getMore
<template>
<div class="more" v-bind:class="{disabled:isMore}" v-on:click="getMore">{{msg}}</div>
</template>
<script>
import axios from 'axios'
export default{
name:'getMore',
props:['movieList'],
data(){
return {
start:26,
msg:'加载更多',
isMore:false
};
},
methods:{
getMore:function(){
var that=this;
if(this.isMore){
return false;
}
axios.get('/movie/list.json',{
params:{
type:'hot',
offset:that.calStart(),
limit:5
}
},).then(function (response) {
console.log(response.data.data)
response.data.data.movies.forEach(function(value,index,array){
that.movieList.push(value);
});
if(!response.data.data.hasNext){
that.msg="没有了~";
that.isMore=true;
}
})
.catch(function (error) {
console.log(error);
});
},
calStart:function(){
return this.start=this.start+5;
}
}
}
</script>
<style scoped>
.more{
width:90%;
margin: 10px auto;
font-size: 0.375rem;
background: #EF4238;
border-radius: 3px;
color:#fff;
height: 40px;
line-height: 40px;
text-align: center;
}
.disabled{
background: #bfbbbb;
cursor: not-allowed;
}
</style>
e、cinemaList.vue
<template>
<ul class="cinema-list">
<template v-for="area in cinemaList">
<li class="cinema-container" v-for="item in area">
<a href="#">
<div class="cinema-name">{{item.area}}:{{item.nm}}</div>
<div class="price">
<span class="num">{{item.sellPrice}}</span><span>元起</span>
</div>
<div class="mtb8">
<p class="address text-ellipsis">{{item.addr}}</p>
<p class="dis">{{item.distance}}km</p>
</div>
<div class="tag-list">
<div class="seat">座</div>
<div class="hall" v-if="item['imax']==1">IMAX厅</div>
</div>
</a>
</li>
</template>
</ul>
</template>
<script>
import axios from 'axios'
export default{
name:"cinemaList",
props:['cinemaList']
}
</script>
<style scoped>
.cinema-list {
background: #fff;
padding: 0 0.3125rem;
font-size: 0.4375rem;
}
.cinema-container{
padding: 0.3125rem 0;
border-bottom: 1px solid #E6E6E6;
}
.cinema-name{
display: inline-block;
}
.price{
display: inline-block;
color: #e54847;
font-size: 0.375rem;
}
.price .num{
font-size: 0.4375rem;
}
.address,.dis{
color: #666;
font-size: 0.375rem;
display: inline-block;
overflow: hidden;
}
.dis{
color: #999;
}
.address{
width: 80%;
text-overflow: ellipsis;
white-space: nowrap;
}
.mtb8{
margin: 0.25rem 0;
}
.seat,.hall{
display: inline-block;
color: #8BB7CE;
font-size: 0.375rem;
padding: 0 0.0625rem;
border-radius: 3px;
margin-right: 0.15625rem;
border: 1px solid #8BB7CE;
}
</style>
以上代码编写完后,我们接着编写home.vue
<template>
<div id="app">
<home-title></home-title>
<tab></tab>
<transition name="shake">
<router-view :cinemaList="cinemaList" :movieList="movieList"></router-view>
</transition>
</div>
</template>
<script>
import homeTitle from '../components/header'
import tab from '../components/tab'
import axios from 'axios'
export default{
data(){
return {
cinemaList:[],
movieList:[]
};
},
components:{
homeTitle,
tab
},
mounted:function(){ //主页装载完毕后请求数据避免tab切换重复请求
var that=this;
//影院列表
axios.get('/cinemas.json')
.then(function (response) {
that.cinemaList=response.data.data;
})
.catch(function (error) {
console.log(error);
});
//电影列表
axios.get('/movie/list.json?type=hot&offset=0&limit=25')
.then(function (response) {
that.movieList=response.data.data.movies;
})
.catch(function (error) {
console.log(error);
});
}
}
</script>
如果数据是在各自组件获取的,可以考虑使用localStorage缓存数据,避免tab切换需要重新请求数据
<style>
.shake-enter-active {
animation: shake .3s;
}
.shake-leave-active {
}
@keyframes shake {
0%,to {
transform: translateZ(0)
}
10%,90% {
transform: translate3d(-10px,0,0)
}
80% {
transform: translate3d(10px,0,0)
}
}
</style>
至此,我们完成了大部分代码编写,需要注意的是vue-cli构建的项目并没有安装axios,你需要自行安装。
一切就绪后我们编写我们的路由文件index.js:
内容如下:
import Vue from 'vue'
import Router from 'vue-router'
import Header from '@/components/header'
import movieList from '@/components/movieList'
import cinemaList from '@/components/cinemaList'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'movieList',
component: movieList
},
{
path: '/movieList',
name: 'movieList',
component: movieList
},
{
path: '/cinemaList',
name: 'cinemaList',
component: cinemaList
}
],
history: true
})
最后修改入口文件main.js:
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import Home from './pages/home'
import router from './router'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
template: '<Home/>',
components: { Home }
})
注意事项:因为我们使用的是猫眼的数据api接口,在我们自己的环境下是存在跨域的,所以我们需要给我们的ajax请求设置代理,配置文件在下面的目录中:
打开index.js,修改proxyTable配置项:
index.html内容:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<title>maoyan</title>
<link rel="stylesheet" href="./static/css/common.css">
</head>
<body>
<div id="app"></div>
<script>
(function () {
var newRem = function () {
var html = document.documentElement;
html.style.width = '100%';
if (html.getBoundingClientRect().width > 1024) {
html.style.fontSize = 80 + 'px';
html.style.width = 1024 + 'px';
html.style.margin = '0 auto';
return true;
} else {
html.style.fontSize = html.getBoundingClientRect().width / 10 + 'px';
}
};
window.addEventListener('resize', newRem, false);
newRem();
})();
</script>
</body>
</html>
common.css为样式重置文件,根据需要自行编写。
至此,我们的实例项目就完成了,表述不好,敬请见谅。
预览图:
热门评论
文章内容有误:1、数据缓存可以用vue的<keep-live></keep-live>2、组件间通信只允许父组件向子组件传递数据,子组件向父组件发射事件
有git地址,想参考一下,谢谢
楼主这是什么原因 dev-server.js 没有配置吗?????