我们继续前两节的开发。本节教程实现的效果如下:
效果很简单,但是实现起来却要用到 Vue 的很多知识,下面我们将一步一步的实现这个效果。
首先这些城市的信息都是从后台的 server 里面获取的,所以我们需要一个后台,后台的代码可以从
https://github.com/EzrealDeng/Taopiaopiao 里面的 server 文件夹获取,这个 server 端具体怎么实现的我们暂时不用关心,只需要知道这是一个可以返回我们需要的数据的后台服务即可。
下载完后进入文件夹执行:
- 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 如下:
- if="$store.state.city.show">
- 选择城市
- ×
- for="city in cityList" key="city.id">for="name in city.data" key="name.id">//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:returnrandomCity
- }
- },//选择城市事件
- 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]
- })
- })
- })
- }
- }
- .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;
- }
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: '',
- components: { App }
- })
然后再使用我们刚才建立的 city 组件,修改 views/movie.vue 如下:
- {{ $store.state.city.name }}
- 正在热映
- 即将上映
- import city from '../components/home/city.vue'
- import { mapMutations } from 'vuex'
- export default{
- data(){
- return {
- }
- },
- components:{
- city
- },
- methods:{
- ...mapMutations([
- 'showCityList'
- ])
- }
- }
- .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);
- }
所有文件修改完成之后重新启动项目,不出意外的话应该就会完成我们开头的效果了。
这里面具体的 city.vue 的实现可能有些地方看不太清楚,没关系,再后面的章节后我们会一步步自己实现简单的类似的组件。
谢谢阅读,如果有问题欢迎在评论区一起讨论。
来源: http://www.cnblogs.com/mdengcc/p/7079754.html