原文总结很全面,排版在原文基础上改动,部分自己实践的部分与原文有差异,在原文的基础上已修改
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’
在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
}
}
gitignore设置:添加文件目录,推送到仓库是,忽略添加的文件
设置json数据,开发环境转发代理
设置 config 文件夹下的 index.js
设置 module.exports 下 dev 的 proxyTable 代理
webpack-dev-server 工具会自动将 /api 替换成 /static/data
返回根目录
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:[]
并在各个组件中绑定数据
热门城市
{
{key}}
{
{item.name}}
City-alphabet:26个字母,要获取 city-list的数据(兄弟组件间的联动)
子组件Alphabet.vue数据传递给父组件city.vue,通过父组件city.vue传递给子组件list.vue
-
{
{item}}
在 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])
}
}
上下滑动时,取字母位置的逻辑
获取A字母距离顶部高度
滑动时,取当前位置距离顶部高度
计算差值,得到当前手指位置与A字母顶部差值
差值除以每个字母的高度,得出当前的字母,触发change事件给外部
-
{
{item}}
使用v-model做双向绑定
在data中定义keyword(搜索的内容)keywordList(要显示的内容)、timer(做节流优化)
- {
{item.name}}
-
没有匹配数据
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'])
},
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) {}
}
}
})
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 =“”表示那个页面不被缓存
画廊组件:
'common': resolve('src/common')
在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 再传递给自身,进行递归调用。
{
{item.title}}
接下来接口联调
真机测试: