继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

使用vue-cli脚手架工具创建一个简单的单页应用

慕莱坞1975833
关注TA
已关注
手记 8
粉丝 90
获赞 468

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为样式重置文件,根据需要自行编写。
至此,我们的实例项目就完成了,表述不好,敬请见谅。
预览图:
图片描述

图片描述
图片描述

打开App,阅读手记
10人推荐
发表评论
随时随地看视频慕课网APP

热门评论

文章内容有误:1、数据缓存可以用vue的<keep-live></keep-live>2、组件间通信只允许父组件向子组件传递数据,子组件向父组件发射事件http://img1.mukewang.com/5a5f68aa0001275807900646.jpg

有git地址,想参考一下,谢谢

//img.mukewang.com/59eedb60000191c706420271.jpg

楼主这是什么原因  dev-server.js 没有配置吗?????

查看全部评论