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

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

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

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

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

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

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

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

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

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

  1. .....//省略
  2. dev: {
  3. env: require('./dev.env'),
  4. port: 8080,
  5. autoOpenBrowser: true,
  6. assetsSubDirectory: 'static',
  7. assetsPublicPath: '/',
  8. proxyTable: {
  9. '/movie/coming': 'http://localhost:9090',
  10. '/movie/hot': 'http://localhost:9090',
  11. '/movie/info': 'http://localhost:9090',
  12. '/movie/evaluation': 'http://localhost:9090',
  13. '/movie/cinema': 'http://localhost:9090',
  14. '/movie/cinema_detail': 'http://localhost:9090',
  15. '/movie/swiper': 'http://localhost:9090',
  16. '/movie/city': 'http://localhost:9090'
  17. },
  18. ...//省略

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

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

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

  

  1. npm install vuex --save //vuex
  2. npm install vue-resource --save //vue-resource

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

  1. npm install mint-ui //mint-ui

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

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

依次修改city.vue如下:

  1. <template>
  2. <section ref="city" id="select-city" class="pf fadeInDown" v-if="$store.state.city.show">
  3. <header class="city-header mint-1px-b pr">
  4. <span class="fb">选择城市</span>
  5. <span class="close-city pa" @click="cancelCityList">×</span>
  6. </header>
  7. <div ref="city" @click="selectCity">
  8. <mt-index-list>
  9. <mt-index-section :index="city.sort" v-for="city in cityList" key="city.id">
  10. <mt-cell :title="name.regionName" v-for="name in city.data" key="name.id"></mt-cell>
  11. </mt-index-section>
  12. </mt-index-list>
  13. </div>
  14. </section>
  15. </template>
  16.  
  17. <script>
    //mapActions,mapMutations可以获取我们在store里面的所有actions,mutations方法,
  18. import { mapActions, mapMutations } from 'vuex'
  19.  
  20. export default{
  21. data () {
  22. return {
  23. showCityList: true,
  24. cityList: []
  25. }
  26. },
  27. methods: {
  28. ...mapActions([
  29. 'updateCityAsync'
  30. ]),
  31. ...mapMutations([
  32. 'pushLoadStack',
  33. 'completeLoad'
  34. ]),
         //封装了一下vue-resource的请求方法
  35. requestData (url, fn) {
  36. this.pushLoadStack()
  37. this.$http.get(url).then(fn).then(this.completeLoad)
  38. },
  39. changeCityData (data) {
  40. this.pushLoadStack()
  41. this.$refs.city.className = "pf fadeOutTop"
  42. this.$store.dispatch('updateCityAsync', data).then(this.completeLoad)
  43. },
  44. matchCityStr (str) {
  45. let randomList = ['bj', 'sh', 'gz']
  46. let randomCity = randomList[Math.floor(3*Math.random())]
  47. switch (str) {
  48. case '北京': return 'bj'
  49. case '上海': return 'sh'
  50. case '广州': return 'gz'
  51. default: return randomCity
  52. }
  53. },
        //选择城市事件
  54. selectCity (event) {
  55. let ele = event.target
  56. let className = ele.className
  57. let name = ''
  58. if (className === "mint-indexsection-index" || className ==="mint-indexlist-nav" || className === "mint-indexlist-navitem") {
  59. name = ''
  60. } else if (className === 'mint-cell-wrapper') {
  61. name = ele.children[0].children[0].innerHTML
  62. } else if (className === "mint-cell-title") {
  63. name = ele.children[0].innerHTML
  64. } else {
  65. name = ele.innerHTML
  66. }
  67. if (name) {
  68. this.changeCityData({
  69. city: {
  70. name: name,
  71. rN: this.matchCityStr(name)
  72. }
  73. })
  74. } else {
  75. return false
  76. }
  77. },
  78. cancelCityList () {
  79. this.changeCityData({city: {}})
  80. }
  81. },
  82. created () {
  83. //this.$store.dispatch('updateCityAsync', {city: {}})
  84. this.requestData('/movie/city', (response) => {
  85. // let data = JSON.parse(response.data)
  86. let data = response.data
  87. let cityObj = data.data.data.returnValue
  88. let citySort = Object.keys(cityObj)
  89. this.cityList.push({ //先push进去三个热门城市
  90. sort: '热门',
  91. data: [{
  92. regionName: '北京',
  93. id: 1,
  94. rN: 'bj'
  95. }, {
  96. regionName: '上海',
  97. id: 2,
  98. rN: 'sh'
  99. }, {
  100. regionName: '广州',
  101. id: 3,
  102. rN: 'gz'
  103. }]
  104. })
  105. citySort.forEach((item) => { //获取后台的城市信息并且按分类信息进行排序
  106. this.cityList.push({
  107. sort: item,
  108. data: cityObj[item]
  109. })
  110. })
  111. })
  112. }
  113. }
  114. </script>
  115.  
  116. <style>
  117. .mint-indicator-wrapper {
  118. z-index: 1000
  119. }
  120. #select-city {
  121. top: 0;
  122. bottom: 0;
  123. left: 0;
  124. right: 0;
  125. z-index: 9999999;
  126. background-color: #fff;
  127. }
  128. .city-header {
  129. height: 46px;
  130. line-height: 46px;
  131. text-align: center;
  132. color: #000;
  133. font-size: 16px;
  134. background-color: #fff;
  135. }
  136. .close-city {
  137. font-size: 28px;
  138. color: #666;
  139. display: inline-block;
  140. height: 46px;
  141. width: 50px;
  142. line-height: 38px;
  143. text-align: center;
  144. right: 0px;
  145. }
  146. @-webkit-keyframes fadeInDown {
  147. 0% {
  148. opacity: .7;
  149. -webkit-transform: translateY(-50px);
  150. transform: translateY(-50px)
  151. }
  152. 100% {
  153. opacity: 1;
  154. -webkit-transform: translateY(0);
  155. transform: translateY(0)
  156. }
  157. }
  158. @keyframes fadeInDown {
  159. 0% {
  160. opacity: .7;
  161. -webkit-transform: translateY(-50px);
  162. -ms-transform: translateY(-50px);
  163. transform: translateY(-50px)
  164. }
  165. 100% {
  166. opacity: 1;
  167. -webkit-transform: translateY(0);
  168. -ms-transform: translateY(0);
  169. transform: translateY(0)
  170. }
  171. }
  172. .fadeInDown {
  173. -webkit-animation: fadeInDown .3s;
  174. animation: fadeInDown .3s;
  175. }
  176. @-webkit-keyframes fadeOutTop {
  177. 0% {
  178. opacity: 1;
  179. -webkit-transform: translateY(0);
  180. transform: translateY(0)
  181. }
  182. 100% {
  183. opacity: 0;
  184. -webkit-transform: translateY(-50px);
  185. transform: translateY(-50px)
  186. }
  187. }
  188. @keyframes fadeOutTop {
  189. 0% {
  190. opacity: 1;
  191. -webkit-transform: translateY(0);
  192. -ms-transform: translateY(0);
  193. transform: translateY(0)
  194. }
  195. 100% {
  196. opacity: 0;
  197. -webkit-transform: translateY(-50px);
  198. -ms-transform: translateY(-50px);
  199. transform: translateY(-50px)
  200. }
  201. }
  202. .fadeOutTop {
  203. -webkit-animation: fadeOutTop .35s;
  204. animation: fadeOutTop .35s;
  205. }
  206. </style>

store/city/actions.js如下:

  1. import Vue from 'vue'
  2. export default {
    //异步的更新城市信息
  3. updateCityAsync ({ commit, state }, {city}) { //commit对象可以用来触发mutations里面的同步更新城市方法
  4. if (!city.name) {
  5. city.name = state.name
  6. city.rN = state.rN
  7. }
  8. return Vue.http.get(`/movie/hot/?city=${city.rN}`).then((response) => {
  9. let data = response.data
  10. let lists = data.data.data.returnValue
  11. //模拟索引数据的id号
  12. lists.forEach((item, index) => {
  13. item.mID = index
  14. })
  15. city.data = lists
  16. commit('UPDATE', { city }) // 更新城市信息
  17. })
  18. }
  19. }

mutations.js如下:

  1. export default{
  2. UPDATE (state , { city }){ //根据传入的city对象来改变状态
  3. if(city.name){
  4. state.name = city.name;
  5. state.data = city.data;
  6. state.rN = city.rN;
  7. }
  8. state.show = false;
  9. },
  10. showCityList (state) { //显示城市选择
  11. state.show = true
  12. }
  13. }

store/loading/mutations.js如下:

  1. //loading组件
    import { Indicator } from 'mint-ui';
  2. export default {
  3. pushLoadStack (state) {
  4. Indicator.open({
  5. text: 'loading...',
  6. spinnerType: 'snake'
  7. });
  8. state.stack.push(1)
  9. },
  10. completeLoad (state) { //完成加载
  11. let stack = state.stack
  12. stack.pop()
  13. if (!stack.length) {
  14. //延时为了更好显示loading效果
  15. setTimeout(() => {
  16. Indicator.close()
  17. }, 500)
  18. }
  19. }
  20. }

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

  1. import Vue from 'vue'
  2. import cityMutations from './city/mutations'
  3. import cityAcions from './city/actions'
  4. import loadingMutations from './loading/mutations'
  5. import Vuex from 'vuex'
  6. Vue.use(Vuex) //vue插件只需要在这里use一次
  7.  
  8. const cityGetters = {
  9. movies: state => state.data,
  10. cityName: state => state.name
  11. }
  12.  
  13. const city = {
  14. state: {
  15. name: '北京',
  16. show: true,
  17. rN: 'bj',
  18. data: []
  19. },
  20. actions: cityAcions,
  21. mutations: cityMutations,
  22. getters: cityGetters
  23. }
  24.  
  25. const loading = {
  26. state: {
  27. stack: []
  28. },
  29. mutations: loadingMutations
  30. }
  31.  
  32. export default new Vuex.Store({
  33. modules: {
  34. city,
  35. loading
  36. }
  37. })

然后再修改src下的main.js如下:

  1. // The Vue build version to load with the `import` command
  2. // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
  3. import Vue from 'vue'
  4. import App from './App'
  5. import router from './router'
  6. import Mint from 'mint-ui';
  7. import store from './store'
  8. import VueResource from 'vue-resource'
  9. import 'mint-ui/lib/style.css'
  10. Vue.config.productionTip = false
  11.  
  12. Vue.use(Mint)
  13. Vue.use(VueResource)
  14.  
  15. /* eslint-disable no-new */
  16. new Vue({
  17. el: '#app',
  18. router,
  19. store,
  20. template: '<App/>',
  21. components: { App }
  22. })

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

  1. <template>
  2. <div>
  3. <header class="home-header border-bottom">
  4. <city></city>
  5. <div class="top-section">
  6. <div class="" @click="showCityList">
  7. {{ $store.state.city.name }}
  8. <i class="icon-arrow"></i>
  9. </div>
  10. <div>
  11. 正在热映
  12. </div>
  13. <div>
  14. 即将上映
  15. </div>
  16. </div>
  17.  
  18. </header>
  19. </div>
  20. </template>
  21. <script>
  22. import city from '../components/home/city.vue'
  23. import { mapMutations } from 'vuex'
  24. export default{
  25. data(){
  26. return {
  27.  
  28. }
  29. },
  30. components:{
  31. city
  32. },
  33. methods:{
  34. ...mapMutations([
  35. 'showCityList'
  36. ])
  37. }
  38. }
  39. </script>
  40. <style>
  41. .top-section{
  42. display: flex;
  43. justify-content: space-around;
  44. }
  45. .icon-arrow{
  46. height: 12px;
  47. display: inline-block;
  48. }
  49. .icon-arrow:after{
  50. content: "";
  51. display: block;
  52. width: 6px;
  53. height: 6px;
  54. border: 1px solid #50505a;
  55. border-top: 0 none;
  56. border-left: 0 none;
  57. margin-left: 2px;
  58. transform: rotate(45deg);
  59. }
  60. </style>

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

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

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

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

  https://github.com/EzrealDeng/Taopiaopiao

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

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

Vue 项目实战系列 (三)的更多相关文章

  1. Vue 项目实战系列 (一)

    最近一直在学习Vue,基本的文档看完后就需要进行具体的项目进行练手了,本系列文章主要是将我学习过程记录下来,和大家一起学习交流. 我在git上找到了一个淘票票的Vue项目,项目地址: https:// ...

  2. Spring Boot +Vue 项目实战笔记(三):数据库的引入

    这一篇的主要内容是引入数据库并实现通过数据库验证用户名与密码. 一.引入数据库 之前说过数据库的采用是 MySQL,算是比较主流的选择,从性能和体量等方面都比较优秀,当然也有一些弊端,但数据库不是我们 ...

  3. Vue 项目实战系列 (二)

    上一章节我们已经把项目的初始化工作完成了,接下来我们再来进行具体的代码编写.这一节我们将完成如下的页面. 我们在src/目录下新建一个views文件夹,存放我们的主要页面文件.目录结构如下: cine ...

  4. 从零开始Vue项目实战(三)-项目结构

    目录结构 ├── README.md 项目介绍 ├── index.html 入口页面 ├── build 构建脚本目录 │ ├── build-server.js 运行本地构建服务器,可以访问构建后 ...

  5. webpack+vue项目实战(四,前端与后端的数据交互和前端展示数据)

    地址:https://segmentfault.com/a/1190000010063757 1.前言 今天要做的,就是在上一篇文章的基础上,进行功能页面的开发.简单点说呢,就是与后端的数据交互和怎么 ...

  6. Vue2+VueRouter2+webpack 构建项目实战(三):配置路由,运行页面

    制作.vue模板文件 通过前面的两篇博文的学习,我们已经建立好了一个项目.问题是,我们还没有开始制作页面.下面,我们要来做页面了. 我们还是利用 http://cnodejs.org/api 这里公开 ...

  7. WCF开发实战系列三:自运行WCF服务

    WCF开发实战系列三:自运行WCF服务 (原创:灰灰虫的家 http://hi.baidu.com/grayworm)上一篇文章中我们建立了一个WCF服务站点,为WCF服务库运行提供WEB支持,我们把 ...

  8. Selenium Web 自动化 - 项目实战(三)

    Selenium Web 自动化 - 项目实战(三) 2016-08-10 目录 1 关键字驱动概述2 框架更改总览3 框架更改详解  3.1 解析新增页面目录  3.2 解析新增测试用例目录  3. ...

  9. Linux运维项目实战系列

    Linux运维项目实战系列 项目实战1-LNMP的搭建.nginx的ssl加密.权限控制的实现 项目实战2-项目实战2-实现基于LVS负载均衡集群的电商网站架构 2.1项目实战2.1-nginx 反向 ...

随机推荐

  1. C# 创建压缩文件

    在程序中对文件进行压缩解压缩是很重要的功能,不仅能减小文件的体积,还能对文件起到保护作用.如果是生成用户可以下载的文件,还可以极大的减少网络流量并提升下载速度.最近在一个 C# 项目中用到了创建压缩文 ...

  2. IOS安装CocoaPods详情过程

    一.简介 什么是CocoaPods CocoaPods是OS X和iOS下的一个第三类库管理工具,通过CocoaPods工具我们可以为项目添加被称为“Pods”的依赖库(这些类库必须是CocoaPod ...

  3. Redux-saga

    Redux-saga学习笔记 概述 Redux-saga在Redux应用中扮演'中间件'的角色,主要用来执行数据流中的异步操作.主要通过ES6中的generator函数和yield关键字来以同步的方式 ...

  4. TreeSet源码分析

    第1部分 TreeSet介绍 TreeSet 是一个有序的集合,它的作用是提供有序的Set集合.它继承于AbstractSet抽象类,实现了NavigableSet<E>, Cloneab ...

  5. 【wannacry病毒之暗网】-如何访问"暗网"(慎入)

    心里能力不强的人,请别看. 有些事情还是不要接触比较好, 社会最恶一面不是随随便便就能接触到的, 也不是你能理解的 你想要用暗网做什么是你考虑的一个问题 什么是暗网? 所谓的"暗网" ...

  6. Bash Excercises

    1. cat <<EOF #!/bin/bash function printHelp { cat<<EOF Run the Dash vector tests. Usage: ...

  7. 【CSS Cookbook】笔记摘要(二)

     页面元素 使用text-align性质可以居中显示块级元素中的文字.把margin-left和margin-right设为auto时,该元素则会相对于父元素居中显示.但是现在流行的一些较低版本的浏览 ...

  8. 互联网金融P2P主业务场景自动化测试

            互联网金融P2P行业,近三年来发展迅速,如火如荼.         据不完全统计,全国有3000+的企业.  “互联网+”企业,几乎每天都会碰到一些奇奇怪怪的bug,作为在互联网企业工 ...

  9. C语言之循环结构

    程序结构: 顺序结构 条件结构(分支结构) if结构,if-else结构 ,多重if分支结构,switch结构 循环结构:做重复的事情 while循环,do..while循环和for循环. 写循环结构 ...

  10. 测试工具——JMeter

    本学期新学的课程,软件测试,上机的实验用到了C++Test,QTP,还有JMeter.今天针对JMeter做一次总结,方便以后用到,知道步骤会更加方便. 首先,对Jmeter进行一个大致的了解,包括对 ...