咱們繼續前兩節的開發。本節教程實現的效果以下:javascript
效果很簡單,可是實現起來卻要用到Vue的不少知識,下面咱們將一步一步的實現這個效果。css
首先這些城市的信息都是從後臺的server裏面獲取的,因此咱們須要一個後臺,後臺的代碼能夠從前端
https://github.com/EzrealDeng/Taopiaopiao裏面的server 文件夾獲取,這個server端具體怎麼實現的咱們暫時不用關心,只須要知道這是一個能夠返回咱們須要的數據的後臺服務便可。vue
下載完後進入文件夾執行:java
npm install //安裝全部的包
npm run start //啓動後臺服務
便可啓動服務,(若是啓動過程當中出錯,可使用npm run start:strict 啓動,或者升級node版本,默認的是9090端口,能夠手動進行修改)。node
成功啓動的話咱們就有了一個可使用的數據後臺了。那麼Vue如何訪問這個接口的呢,咱們這裏使用vue-resource(相似於Jquery裏的ajax的功能)進行訪問後臺接口。vue-resource的使用方式相似下面的例子:webpack
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以下git
.....//省略 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' }, ...//省略
到如今爲止,接口的訪問問題已經解決了。github
還有最後一個準備知識須要介紹下,那就是Vuex,這是個啥呢?就是個狀態管理機,因爲Vue好多個組件可能會共用同一個狀態信息,例如咱們的淘票票,選擇城市的這個組件須要知道當前的城市信息,而每一個城市會有當前不一樣的上映的電影,這個反應當前熱映電影的組件也須要知道這個信息,如何保持數據的同步,光靠組件之間的通訊將會十分複雜,因此有了Vuex這個東西,具體如何使用能夠去看https://vuex.vuejs.org/zh-cn/官網的介紹。本例中用到的地方我會寫上註釋。接下來開始正式的編碼吧。web
上面咱們介紹了這麼多的東西,使用起來固然得先一一的安裝一遍。
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; width: 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; width: 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/ ,轉載請註明出處。