zoukankan      html  css  js  c++  java
  • Vue 项目实战系列 (三)

      我们继续前两节的开发。本节教程实现的效果如下:

      效果很简单,但是实现起来却要用到Vue的很多知识,下面我们将一步一步的实现这个效果。

      首先这些城市的信息都是从后台的server里面获取的,所以我们需要一个后台,后台的代码可以从

    https://github.com/EzrealDeng/Taopiaopiao里面的server 文件夹获取,这个server端具体怎么实现的我们暂时不用关心,只需要知道这是一个可以返回我们需要的数据的后台服务即可。

    下载完后进入文件夹执行:

    npm install //安装所有的包
    npm run start //启动后台服务

    即可启动服务,(如果启动过程中出错,可以使用npm run start:strict 启动,或者升级node版本,默认的是9090端口,可以手动进行修改)。

      成功启动的话我们就有了一个可以使用的数据后台了。那么Vue如何访问这个接口的呢,我们这里使用vue-resource(类似于Jquery里的ajax的功能)进行访问后台接口。vue-resource的使用方式类似下面的例子:

    this.$http.get('/movie/swiper').then(function(res){ //返回的是promise对象,res为返回的对象
        console.log(res);
        this.images = res.body.data.data.returnValue;
        console.log(this.images);
    })

      有了这个我们就可以和后台进行数据交互了,但是还有一个问题,我们的开发是vue脚手架自动搭建的一个基于Express的服务,我们的前端代码实际上直接访问的都是这个前端项目的后台,想要直接访问我们刚才搭建的后台会有跨域问题的,怎么办呢?幸好有一个叫做http-proxy的东西,可以帮我们实现代理,将我们要访问的接口都映射到真正的服务器上,后端进行跨域问题的解决。而且Vue脚手架也帮我们集成了这个插件,只要在配置文件里修改即可。这个文件在:config/index.js里,修改这个文件里的proxyTable如下

    .....//省略
    dev: {
        env: require('./dev.env'),
        port: 8080,
        autoOpenBrowser: true,
        assetsSubDirectory: 'static',
        assetsPublicPath: '/',
        proxyTable: {
            '/movie/coming': 'http://localhost:9090',
            '/movie/hot': 'http://localhost:9090',
            '/movie/info': 'http://localhost:9090',
            '/movie/evaluation': 'http://localhost:9090',
            '/movie/cinema': 'http://localhost:9090',
            '/movie/cinema_detail': 'http://localhost:9090',
            '/movie/swiper': 'http://localhost:9090',
            '/movie/city': 'http://localhost:9090'
        },
    ...//省略

    到现在为止,接口的访问问题已经解决了。

      还有最后一个准备知识需要介绍下,那就是Vuex,这是个啥呢?就是个状态管理机,由于Vue好多个组件可能会共用同一个状态信息,例如我们的淘票票,选择城市的这个组件需要知道当前的城市信息,而每个城市会有当前不同的上映的电影,这个反应当前热映电影的组件也需要知道这个信息,如何保持数据的同步,光靠组件之间的通信将会十分复杂,所以有了Vuex这个东西,具体如何使用可以去看https://vuex.vuejs.org/zh-cn/官网的介绍。本例中用到的地方我会写上注释。接下来开始正式的编码吧。

      上面我们介绍了这么多的东西,使用起来当然得先一一的安装一遍。

      

    npm install vuex --save //vuex
    npm install vue-resource --save //vue-resource

    另外我们选择城市的组件用到了mint-ui的Vue组件库,所以要也要安装。

    npm install mint-ui //mint-ui

    接下来新建文件和目录如下:

     主要是新建立home/city.vue和store文件夹。city.vue是我们的选择城市的组件,store存放的是Vuex状态管理文件。

    依次修改city.vue如下:

    <template>    
        <section ref="city" id="select-city" class="pf fadeInDown" v-if="$store.state.city.show">
            <header class="city-header mint-1px-b pr">
                <span class="fb">选择城市</span>
                <span class="close-city pa" @click="cancelCityList">×</span>
            </header>
            <div ref="city" @click="selectCity">
                <mt-index-list>
                  <mt-index-section :index="city.sort" v-for="city in cityList" key="city.id">
                    <mt-cell :title="name.regionName" v-for="name in city.data" key="name.id"></mt-cell>
                  </mt-index-section>
                </mt-index-list>
            </div>
        </section>
    </template>
    
    <script>
    //mapActions,mapMutations可以获取我们在store里面的所有actions,mutations方法, import { mapActions, mapMutations } from 'vuex' export default{ data () { return { showCityList: true, cityList: [] } }, methods: { ...mapActions([ 'updateCityAsync' ]), ...mapMutations([ 'pushLoadStack', 'completeLoad' ]),
         //封装了一下vue-resource的请求方法 requestData (url, fn) {
    this.pushLoadStack() this.$http.get(url).then(fn).then(this.completeLoad) }, changeCityData (data) { this.pushLoadStack() this.$refs.city.className = "pf fadeOutTop" this.$store.dispatch('updateCityAsync', data).then(this.completeLoad) }, matchCityStr (str) { let randomList = ['bj', 'sh', 'gz'] let randomCity = randomList[Math.floor(3*Math.random())] switch (str) { case '北京': return 'bj' case '上海': return 'sh' case '广州': return 'gz' default: return randomCity } },
        //选择城市事件 selectCity (event) { let ele
    = event.target let className = ele.className let name = '' if (className === "mint-indexsection-index" || className ==="mint-indexlist-nav" || className === "mint-indexlist-navitem") { name = '' } else if (className === 'mint-cell-wrapper') { name = ele.children[0].children[0].innerHTML } else if (className === "mint-cell-title") { name = ele.children[0].innerHTML } else { name = ele.innerHTML } if (name) { this.changeCityData({ city: { name: name, rN: this.matchCityStr(name) } }) } else { return false } }, cancelCityList () { this.changeCityData({city: {}}) } }, created () { //this.$store.dispatch('updateCityAsync', {city: {}}) this.requestData('/movie/city', (response) => { // let data = JSON.parse(response.data) let data = response.data let cityObj = data.data.data.returnValue let citySort = Object.keys(cityObj) this.cityList.push({ //先push进去三个热门城市 sort: '热门', data: [{ regionName: '北京', id: 1, rN: 'bj' }, { regionName: '上海', id: 2, rN: 'sh' }, { regionName: '广州', id: 3, rN: 'gz' }] }) citySort.forEach((item) => { //获取后台的城市信息并且按分类信息进行排序 this.cityList.push({ sort: item, data: cityObj[item] }) }) }) } } </script> <style> .mint-indicator-wrapper { z-index: 1000 } #select-city { top: 0; bottom: 0; left: 0; right: 0; z-index: 9999999; background-color: #fff; } .city-header { height: 46px; line-height: 46px; text-align: center; color: #000; font-size: 16px; background-color: #fff; } .close-city { font-size: 28px; color: #666; display: inline-block; height: 46px; 50px; line-height: 38px; text-align: center; right: 0px; } @-webkit-keyframes fadeInDown { 0% { opacity: .7; -webkit-transform: translateY(-50px); transform: translateY(-50px) } 100% { opacity: 1; -webkit-transform: translateY(0); transform: translateY(0) } } @keyframes fadeInDown { 0% { opacity: .7; -webkit-transform: translateY(-50px); -ms-transform: translateY(-50px); transform: translateY(-50px) } 100% { opacity: 1; -webkit-transform: translateY(0); -ms-transform: translateY(0); transform: translateY(0) } } .fadeInDown { -webkit-animation: fadeInDown .3s; animation: fadeInDown .3s; } @-webkit-keyframes fadeOutTop { 0% { opacity: 1; -webkit-transform: translateY(0); transform: translateY(0) } 100% { opacity: 0; -webkit-transform: translateY(-50px); transform: translateY(-50px) } } @keyframes fadeOutTop { 0% { opacity: 1; -webkit-transform: translateY(0); -ms-transform: translateY(0); transform: translateY(0) } 100% { opacity: 0; -webkit-transform: translateY(-50px); -ms-transform: translateY(-50px); transform: translateY(-50px) } } .fadeOutTop { -webkit-animation: fadeOutTop .35s; animation: fadeOutTop .35s; } </style>

    store/city/actions.js如下:

    import Vue from 'vue'
    export default {
    //异步的更新城市信息 updateCityAsync ({ commit, state }, {city}) { //commit对象可以用来触发mutations里面的同步更新城市方法
    if (!city.name) { city.name = state.name city.rN = state.rN } return Vue.http.get(`/movie/hot/?city=${city.rN}`).then((response) => { let data = response.data let lists = data.data.data.returnValue //模拟索引数据的id号 lists.forEach((item, index) => { item.mID = index }) city.data = lists commit('UPDATE', { city }) // 更新城市信息 }) } }

    mutations.js如下:

    export default{
        UPDATE (state , { city }){ //根据传入的city对象来改变状态
            if(city.name){
                state.name = city.name;
                state.data = city.data;
                state.rN = city.rN;
            }
            state.show = false;
        },
        showCityList (state) { //显示城市选择
          state.show = true
      }
    }

    store/loading/mutations.js如下:

    //loading组件
    import { Indicator } from 'mint-ui'; export default { pushLoadStack (state) { Indicator.open({ text: 'loading...', spinnerType: 'snake' }); state.stack.push(1) }, completeLoad (state) { //完成加载 let stack = state.stack stack.pop() if (!stack.length) { //延时为了更好显示loading效果 setTimeout(() => { Indicator.close() }, 500) } } }

    然后再修改store下的index.js,这个文件是所有的mutations和actions的总出口。

    import Vue from 'vue'
    import cityMutations from './city/mutations'
    import cityAcions from './city/actions'
    import loadingMutations from './loading/mutations'
    import Vuex from 'vuex'
    Vue.use(Vuex) //vue插件只需要在这里use一次
    
    const cityGetters = {
        movies: state => state.data,
        cityName: state => state.name
    }
    
    const city = {
        state: {
            name: '北京',
            show: true,
            rN: 'bj',
            data: []
        },
        actions: cityAcions,
        mutations: cityMutations,
        getters: cityGetters
    }
    
    const loading = {
        state: {
            stack: []
        },
        mutations: loadingMutations
    }
    
    export default new Vuex.Store({
         modules: {
             city,
             loading
         }
    })

    然后再修改src下的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 App from './App'
    import router from './router'
    import Mint from 'mint-ui';
    import store from './store'
    import VueResource from 'vue-resource'
    import 'mint-ui/lib/style.css'
    Vue.config.productionTip = false
    
    Vue.use(Mint)
    Vue.use(VueResource)
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      router,
      store,
      template: '<App/>',
      components: { App }
    })

    然后再使用我们刚才建立的city组件,修改views/movie.vue如下:

    <template>
        <div>
            <header class="home-header border-bottom">
                <city></city>
                <div class="top-section">
                    <div class="" @click="showCityList">
                        {{ $store.state.city.name }}
                        <i class="icon-arrow"></i>
                    </div>
                    <div>
                        正在热映
                    </div>
                    <div>
                        即将上映
                    </div>
                </div>
                
            </header>
        </div>
    </template>
    <script>
    import city from '../components/home/city.vue'
    import { mapMutations } from 'vuex'
    export default{
        data(){
            return {
    
            }
        },
        components:{
            city
        },
        methods:{
            ...mapMutations([
            'showCityList'
          ])
        }
    }    
    </script>
    <style>
        .top-section{
            display: flex;
            justify-content: space-around;
        }
        .icon-arrow{
            height: 12px;
            display: inline-block;
        }
        .icon-arrow:after{
            content: "";
        display: block;
         6px;
        height: 6px;
        border: 1px solid #50505a;
        border-top: 0 none;
        border-left: 0 none;
        margin-left: 2px;
        transform: rotate(45deg);
        }
    </style>

    所有文件修改完成之后重新启动项目,不出意外的话应该就会完成我们开头的效果了。

    这里面具体的city.vue的实现可能有些地方看不太清楚,没关系,再后面的章节后我们会一步步自己实现简单的类似的组件。

    谢谢阅读,如果有问题欢迎在评论区一起讨论。

     更新: 由于写一篇教程还是需要费一定的时间的,工作繁忙后续可能没有时间更新这个系列的文章了,但是整个淘票票的代码我是有更新的,需要的话可以在我的gitHub上查看:

      https://github.com/EzrealDeng/Taopiaopiao

      不能继续更新博客还请大家原谅,有问题的话可以留言一起讨论

    注:本文出自博客园 https://home.cnblogs.com/u/mdengcc/ ,转载请注明出处。

  • 相关阅读:
    codeforces round 512 F. Putting Boxes Together 树状数组维护区间加权平均数
    sgu 110 射线关于球的反射光线
    快速读模板
    HDU-3506 二维四边形不等式
    BZOJ 1563 四边形不等式
    HIT Summer Day17 计算几何初步 题解
    IME Starters Try-outs 2018 C. China Adventures
    IME Starters Try-outs 2018 J. JHADCBEIGF
    ACM International Collegiate Programming Contest, Amman Collegiate Programming Contest (2018) GYM 100810 K. League of Demacia
    Codeforces Round #493 (Div. 1) C. Sky Full of Stars
  • 原文地址:https://www.cnblogs.com/mdengcc/p/7079754.html
Copyright © 2011-2022 走看看