Vue2.5 去哪儿app实战总结

原文总结很全面,排版在原文基础上改动,部分自己实践的部分与原文有差异,在原文的基础上已修改

技术栈:

Vue:Vue Vue-router Vuex Vue-cli

插件:vue-awesome-swiper better-scroll axios

Css: stylus

Api: 静态json数据

项目结构

首页部分:

  • iconfont引入和使用

  • 图片轮播组件

  • 图标区域轮播组件的使用

  • axios获取接口数据

  • 组件间数据传递

城市选择的部分

  • 字母表布局

  • better-scroll的使用

  • 函数节流实现列表性能优化

  • 搜索逻辑实现

  • Vuex实现数据共享

  • LocalStorage实现页面数据存储

  • keep-alive 优化路由性能

详情页部分

  • Banner布局

  • 动态路由配置

  • 公用画廊组件拆分

  • 实现fixed header渐隐渐现效果

  • 递归组件实现详情类别

  • transition slot插槽实现animation简单动画效果

项目依赖包

  • fastClick:处理click 300ms延迟

  • npm i fastclick --save

  • 在main.js引入 import FastClick form ‘fastclick’

  • FastClick.attach(document.body) // 使用

  • stylus:css预处理

  • 下载 stylus 和 stylus-loader --save

  • vue-awesome-swiper:实现轮播插件 npm i vue-awesome-swiper --save 本项目使用2.6.7的版本

  • 在main.js引入 import VueAwesomeAwiper form ‘vue-awesome-swiper’

  • axios:第三方交互插件 npm i axios --save

    哪里使用,哪里引入 import Axios form ‘axios’

  • better-scroll:实现滚动插件 npm i better-scroll --save

    哪里使用,哪里引入 import BScroll form ‘better-scroll’

首页
  • HomeSwiper : 使用vue-awesome-swiper轮播插件
  
  • 在data里写swiperOption:{} 根据swiper3的api设置配置项

  • HomeIcons:使用swiper实现多页自动分页功能

computed: {
    pages () {
      const pages = []
      this.iconsList.forEach((item, index) => {
        const page = Math.floor(index / 8)
        if (!pages[page]) {
          pages[page] = []
        }
        pages[page].push(item)
      })
      return pages
    }
  }
index-ajax:使用axios进行ajax请求
  • gitignore设置:添加文件目录,推送到仓库是,忽略添加的文件

  • 设置json数据,开发环境转发代理

  • 设置 config 文件夹下的 index.js

  • 设置 module.exports 下 dev 的 proxyTable 代理

  • webpack-dev-server 工具会自动将 /api 替换成 /static/data

城市页
  • router-link:实现页面跳转
 返回根目录 
  • City-list使用better-scroll插件实现上下滚动效果

  • html结构外层需写 ref=‘wrapper’

  • 在文件里引入

import BScroll from 'better-scroll'

 mounted () {
    this.scroll = new BScroll(this.$refs.wrapper)
  },
  • city-ajax:同home-ajax 获取数据,并在其他组件中使用

  • 获取数据分布等于data中定义的cities{} hotCities:[]

  • 并在各个组件中绑定数据





  • 在各个组件中使用props介绍这些数据 在html中使用这些接收的数据 以list.vue为例
热门城市
{ {item.name}}
{ {key}}
{ {item.name}}
  • City-alphabet:26个字母,要获取 city-list的数据(兄弟组件间的联动)

  • 子组件Alphabet.vue数据传递给父组件city.vue,通过父组件city.vue传递给子组件list.vue


  • 在city.vue中箭头change事件

在 methods 中定义事件 handleLetterClick,传递 letter 参数。

  methods: {
    handleLetterChange (letter) {
      this.letter = letter
    }
  },

并在 data 中定义数据 letter。

  data () {
    return {
      cities: {},
      hotCities: [],
      letter: ''  // Alphabet 通过 change 事件传递过来的数据
    }
  }

并传递给list.vue


然后在list.vue子组件props中接收letter

 props: {
    cities: Object,
    hotCities: Array,
    letter: String
  },

并通过watch监听letter的变化

 watch: {
    letter () {
      this.scroll.scrollToElement(this.$refs[this.letter][0])
    }
  }
alphabet滑动逻辑:
  • 上下滑动时,取字母位置的逻辑

  • 获取A字母距离顶部高度

  • 滑动时,取当前位置距离顶部高度

  • 计算差值,得到当前手指位置与A字母顶部差值

  • 差值除以每个字母的高度,得出当前的字母,触发change事件给外部




city-search搜索功能逻辑
  • 使用v-model做双向绑定

  • 在data中定义keyword(搜索的内容)keywordList(要显示的内容)、timer(做节流优化)




使用Vuex实现数据共享npm i vuex --save
  • 创建文件夹 store,建index.js,state里放置全局公用数据city
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({

  state: {

    city: '上海'

  },

  mutations: {

    changeCity (state, city) {

      state.city = city

    }

  }

})

在main.js中引入store

import store from './store'  //引入 store

new Vue({

  el: '#app',

  router: router,

  store: store,  //传递进入根实例的 store

  components: { App },

  template: ''

})

在list.vue和search.vue组件中的城市选项绑定click事件handleCityClick

@click="handleCityClick(item.name)

在methods中:

  methods: {
    handleCityClick (city) {
      console.log(city)
      this.$store.commit('changeCity', city) // 通过commit提交mutation
     // this.changeCity(city)
      this.$router.push('/')   // 点击之后跳到home页
    },
   // ...mapMutations(['changeCity'])
  },
localStorage的使用 store index.js
export default new Vuex.Store({
  state: {
	localStorage.city || '上海'
  },
  mutations: {
    changeCity (state, city) {
      state.city = city
      localStorage.city = city
    }
  }
})

有可能当用户使用隐身模式或禁用 localStorage,会导致浏览器报错。所以建议使用 try catch 进行优化

let defalutCity = '上海'

try {
  if (localStorage.city) {
    defaultCity = localStorage.city
  }
} catch (e) {}

export default new Vuex.Store({
  state: {
    city: defaultCity
  },
  mutations: {
    changeCity (state, city) {
      state.city = city
      try {
        localStorage.city = city
      } catch (e) {}
    }
  }
})
Keep-alive 优化 :作用是把数据放到内存中,下次使用是无需重新加载组件,从内存中拿出以前的内容显示就可以了
在本项目中,中包裹这意思是路由内的内容被加载一次之后,把路由的内容放到了内存中,下次使用无需再次加载(导致页面切换时,不同城市,请求的数据是一样的,在network中可以查看)
  • 两种解决方法:1、使用activated生命周期构造
    在home.vue中,定义lastCity:‘’
  data () {
    return {
      swiperList: [],
      iconsList: [],
      weekendList: [],
      recommendList: [],
      lastCity: ''
    }
  },
  mounted () {
    this.lastCity = this.city
    this.getHomeInfo()
  },
 activated () {
    if (this.lastCity !== this.city) {
      this.lastCity = this.city
      this.getHomeInfo()
    }
  },

方法2、

   // exclude =“”表示那个页面不被缓存
      
  
详情页 :to实现动态路由

全局画廊组件

画廊组件:

  • 新建common 用来放置全局组件,建立gallary.vue画廊组件,并在build/webpack.base.conf.js 中进行路径别名(alias)执行的设置
  • 在banner.vue中引入画廊组件调用
  • gallary.vue,banner.vue,header.vue
'common': resolve('src/common')
header.vue渐隐渐现效果实现
解决exclude带来的bug

在app.vue中使用了exclude,那么在Detail下的Header.vue中就不会执行activated构造,但是会执行created钩子。这是header的渐隐渐现效果就不显示了,所以在监听scroll的事件中,把scroll写到created中,就可以解决这个bug了

created () {
   window.addEventListener('scroll', this.handleScroll)
 }

经过测试,使用这种方法时切换到其它页面时,滚动事件仍然生效,暂时在created()和destroyed()里同时增加了监听scroll的事件,但是只在电脑测试中有效,手机端依然无效

递归组件

之所以在组件当中需要一个 name 属性,也是为了方便在组件自身调用自身出现递归的时候便于调用。下面可以看到,在下一个 div 标签中做一个 v-if 判断,如果存在 item.children。就把 item.children 当做 list 再传递给自身,进行递归调用。



 

  • 在其他组件使用的时候直接引入此组件,并在中包裹需要animation的内容即可 如:

      

接下来接口联调
真机测试:

  • 运行的时候把localhost改成电脑的ip地址,同一局域网内的手机可以通过这个地址访问
  • 在package.json中找到–host 0.0.0.0 去掉后pc只有通过localhost才正常运行

你可能感兴趣的:(前端,vue,去哪儿,vue练习,vue总结)