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;
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/ ,转载请注明出处。
Vue 项目实战系列 (三)的更多相关文章
- Vue 项目实战系列 (一)
最近一直在学习Vue,基本的文档看完后就需要进行具体的项目进行练手了,本系列文章主要是将我学习过程记录下来,和大家一起学习交流. 我在git上找到了一个淘票票的Vue项目,项目地址: https:// ...
- Spring Boot +Vue 项目实战笔记(三):数据库的引入
这一篇的主要内容是引入数据库并实现通过数据库验证用户名与密码. 一.引入数据库 之前说过数据库的采用是 MySQL,算是比较主流的选择,从性能和体量等方面都比较优秀,当然也有一些弊端,但数据库不是我们 ...
- Vue 项目实战系列 (二)
上一章节我们已经把项目的初始化工作完成了,接下来我们再来进行具体的代码编写.这一节我们将完成如下的页面. 我们在src/目录下新建一个views文件夹,存放我们的主要页面文件.目录结构如下: cine ...
- 从零开始Vue项目实战(三)-项目结构
目录结构 ├── README.md 项目介绍 ├── index.html 入口页面 ├── build 构建脚本目录 │ ├── build-server.js 运行本地构建服务器,可以访问构建后 ...
- webpack+vue项目实战(四,前端与后端的数据交互和前端展示数据)
地址:https://segmentfault.com/a/1190000010063757 1.前言 今天要做的,就是在上一篇文章的基础上,进行功能页面的开发.简单点说呢,就是与后端的数据交互和怎么 ...
- Vue2+VueRouter2+webpack 构建项目实战(三):配置路由,运行页面
制作.vue模板文件 通过前面的两篇博文的学习,我们已经建立好了一个项目.问题是,我们还没有开始制作页面.下面,我们要来做页面了. 我们还是利用 http://cnodejs.org/api 这里公开 ...
- WCF开发实战系列三:自运行WCF服务
WCF开发实战系列三:自运行WCF服务 (原创:灰灰虫的家 http://hi.baidu.com/grayworm)上一篇文章中我们建立了一个WCF服务站点,为WCF服务库运行提供WEB支持,我们把 ...
- Selenium Web 自动化 - 项目实战(三)
Selenium Web 自动化 - 项目实战(三) 2016-08-10 目录 1 关键字驱动概述2 框架更改总览3 框架更改详解 3.1 解析新增页面目录 3.2 解析新增测试用例目录 3. ...
- Linux运维项目实战系列
Linux运维项目实战系列 项目实战1-LNMP的搭建.nginx的ssl加密.权限控制的实现 项目实战2-项目实战2-实现基于LVS负载均衡集群的电商网站架构 2.1项目实战2.1-nginx 反向 ...
随机推荐
- ajax分页实现(php)
ajax分页实现(php) 使用jquery.pagination.js插件 引入js文件.css文件 <link rel="stylesheet" href="/ ...
- C# selenium环境配置
1.下载C#selenium selenium官网: http://www.seleniumhq.org/download/ 下载后解压: 打开net35后,将里面的dll文件添 ...
- innobackup增量备份与恢复
一.全备: innobackupex --user=root --password=123 /backup/all 全备之后,去数据库操作,创建新的对象或插入数据 二.完整备份目 ...
- #使用parser获取图片信息,输出Python官网发布的会议时间、名称和地点。
# !/usr/bin/env/Python3 # - * - coding: utf-8 - * - from html.parser import HTMLParser import urllib ...
- vue视频学习笔记05
video 5 vue2.0:bower info vue http://vuejs.org/到了2.0以后,有哪些变化? 1. 在每个组件模板,不在支持片段代码组件中模板:之前:<templa ...
- HiveHbase集成实践
作者:Syn良子 出处:http://www.cnblogs.com/cssdongl/p/6857891.html 转载请注明出处 简单的说就是可以通过Hive SQL直接对hbase的表进行读写操 ...
- SQL server 用命令行更改数据库
(计应154兰家才) CREATE DATABASE 数据库名 ON [PRIMARY] ( <数据文件参数> [,…n] [<文件组参数>] ) [LOG ON] ( ...
- java 上传1(使用java组件fileupload)
使用fileupload要添加以下包
- javaWeb学习总结(8)- JSP原理
一.什么是JSP? JSP全称是Java Server Pages,它和servle技术一样,都是SUN公司定义的一种用于开发动态web资源的技术. JSP这门技术的最大的特点在于,写jsp就像在写h ...
- 对clear float 的理解
之前自己对于清除浮动的用法比较模糊 ,如果用到的话,一般都是采用简单粗暴的方式解决,就是直接用overflow:hidden,但是越用久就会发现其实有BUG,这个BUG正是overflow:hidde ...