Vue项目首页实现思路总结

文章目录

  • 1.项目的基本配置
    • 项目结构
    • css初始化和全局样式
    • vue.config.js和.editorconfig
    • TabBar的封装
    • axios封装
  • 首页开发
    • 首页NavBar的开发
    • 请求首页轮播图和推荐的数据
    • 轮播图的展示
    • 推荐部分数据的展示
    • TabControl的封装和使用
    • 请求首页商品数据
    • 展示商品数据
    • 点击TabBar切换对应的内容
    • 使用better-scroll对项目的滚动进行重构
    • 回到顶部backTop组件的封装
    • 点击组件回到顶部的封装
    • backTop组件的显示和隐藏
    • 解决首页中可滚动区域的问题
    • 上拉加载更多功能的实现
    • TabControl吸顶功能的实现
      • 获取到tabControl的offsetTop
    • 监听滚动, 动态的改变tabControl的样式(显示或隐藏)
    • 让Home保持原来的状态

根据b站上coderwhy老师的视频做的Vue项目,在老师视频的基础上完善了项目.
b站视频地址:https://www.bilibili.com/video/BV17j411f74d
视频是2018年年末的,虽然有点老,但是作为入门是绝对够了的,讲的很好也很细.

完成后的项目我部署在了服务器,戳链接:http://39.105.119.76:8082/home

完整的项目地址 : https://github.com/gh2WHY/SuperMall
如果觉得有用,麻烦点个star,拜托了(╥╯^╰╥)。

项目的基本展示:
首页:
Vue项目首页实现思路总结_第1张图片
详情页:
Vue项目首页实现思路总结_第2张图片
分类页:
Vue项目首页实现思路总结_第3张图片
购物车页面:
Vue项目首页实现思路总结_第4张图片
个人中心页面:
Vue项目首页实现思路总结_第5张图片
做这个项目的过程中学习到了很多,对整个vue的知识都进行了一个相对比较完善的复习.

1.项目的基本配置

项目结构

基本的项目结构如下:
Vue项目首页实现思路总结_第6张图片

css初始化和全局样式

normalize.css
base.css

vue.config.js和.editorconfig

vue.config.js->为路径配置别名
.editorconfig->编码格式标准

TabBar的封装

TabBar为我们的项目提供了一个基础的框架,封装完之后我们的项目就有了一个基础的框架,剩下的我们就可以在这个框架的基础上进行各个页面的开发.
关于TabBar的封装,我在之前的一篇文章中已经介绍过,戳链接即可:
https://blog.csdn.net/lhrdlp/article/details/107850371

axios封装

import axios from 'axios'

export function request(config) {
     
  // 1.创建axios的实例
  const instance = axios.create({
     
    baseURL: '最新数据接口,添加老师微信coderwhy003获取',
    timeout: 5000
  })

  // 2.axios的拦截器
  // 2.1.请求拦截的作用
  instance.interceptors.request.use(config => {
     
    return config
  }, err => {
     
    // console.log(err);
  })

  // 2.2.响应拦截
  instance.interceptors.response.use(res => {
     
    return res.data
  }, err => {
     
    console.log(err);
  })

  // 3.发送真正的网络请求
  return instance(config)
}

首页开发

首页NavBar的开发

首页中有一个导航栏,观察首页的导航栏和详情页的导航栏,可以发现,我们的导航栏中不仅仅是中间部分有内容.
在这里插入图片描述
在这里插入图片描述
为了能在各个页面中使用这个组件,我们这个时候就需要用到插槽了,设置左-中-右三个插槽,在具体的页面可以根据不同的需求填充不同的内容.
核心的代码如下:(详细代码可在github下载源码查看)

<template>
  <div class="nav-bar">
    <div class="left"><slot name="left">slot>div>
    <div class="center"><slot name="center">slot>div>
    <div class="right"><slot name="right">slot>div>
  div>
template>

请求首页轮播图和推荐的数据

home.js中先请求一下首页相关的轮播图数据和推荐数据

export function getHomeMultidata() {
     
  return request({
     
    url: '/home/multidata'
  })
}

home.vue中获取数据,我们在created()中获取数据,为了我们的代码更加规范,在 methods中定义getHomeMultidata方法,在created中调用这个方法即可.

定义数据

//存储轮播图数据和推荐部分数据
banners: [],
recommends: [],


methods: {
     
getHomeMultidata() {
     
     getHomeMultidata().then((res) => {
     
       this.banners = res.data.banner.list;
       this.recommends = res.data.recommend.list;
     });
   },
}
created() {
     
getHomeMultidata();
}

轮播图的展示

有关于轮播图的组件已经封装完成,在commpents - common - swiper中已经封装完成.

views - childcomps文件中新建一个swiper.vue,来封装我们的轮播图.咋首页请求的数据通过props在子组件中接收.
我们需要将这个组件引入到swiper.vue中,核心代码如下:

<template>
  <swiper>
  //banners是从父组件中传过来的轮播图相关的数据
    <swiper-item v-for="(item,index) in banners" :key = 'index' >
      <a :href="item.link">
        <img :src="item.image"  @load = 'swiperLoad'>
      </a>
    </swiper-item>
  </swiper>
</template>

推荐部分数据的展示

推荐部分有两个内容,一个是我们本地的图片,直接做个展示即可,还有一部分是我们从服务器请求过来的数据,我们需要封装对应的组件来对这部分数据进行一个相应的展示.
Vue项目首页实现思路总结_第7张图片
views-home - childcomps中新建HomeRecommend组件来展示我们从组件请求过来的数据.
核心代码如下:

<template>
  <div class="recommend">
    <div v-for = '(item,index) in recommends' :key = 'index' class="recommend-item">
      <a :href="item.link">
        <img :src="item.image" alt="">
        <div>{
     {
     item.title}}</div>
      </a>
    </div>
  </div>
</template>
//具体的样式代码见源码

TabControl的封装和使用

观察我们项目中的TabControl,会发现我们不仅仅在首页中使用到了TabControl,在分类页面中也有用到,所以我们在commponents - content - tabbar中封装这个组件
核心代码如下:

<template>
//数据从父组件通过数组的形式中传过来
  <div class="tab-control"  >
    <div class="tab-control-item" 
          v-for="(item,index) in titles" :key = 'index'
          :class ="{active : index === currentIndex}"
          @click = 'tabClick(index)'
    ><span>{
     {
     item}}</span>
    </div>
  </div>
</template>

<script>
export default {
     
  name : 'TabControl',
  props : {
     
    titles : {
     
      type : Array,
      default() {
     
        return [];
      }
    }
  },
  data() {
     
    return {
     
      currentIndex : 0,
    }
  },
  methods : {
     
  //点击哪个那个的颜色就发生变化
    tabClick(index) {
     
      this.currentIndex = index;
      //发射的这个事件在后面会介绍到
      this.$emit('tabClick',index);
    },
  }
}
</script>

<style>
  .tab-control {
     
    display: flex;
    height: 40px;
    background-color: #fff;
    font-size: 15px;
    line-height: 40px;
    text-align : center;
  }
  .tab-control-item {
     
    flex : 1;
  }

  .active {
     
    color: var(--color-high-text);
  }

  .active > span {
     
    padding : 5px;
    border-bottom :3px solid var(--color-high-text);
  }
</style>

在父组件中的使用如下:

      <tab-control
        class="tab-control"
        :titles=" ['流行','新款','精选']"
        @tabClick="tabClick"
        ref="tabControl2"
      ></tab-control>

请求首页商品数据

  • 在home.js中封装getHomeGoods(type, page)
  • 在Home.vue中, 又在methods中getHomeGoods(type)
  • 调用getHomeGoods(‘pop’)/getHomeGoods(‘new’)/getHomeGoods(‘sell’)
    • page: 动态的获取对应的page
  • 获取到数据: res
    • this.goods[type].list.push(…res.data.list)
    • this.goods[type].page += 1

goods: {

pop: page1:/list[30]

new: page1/list[30]

sell: page1/list[30]

}
network下的home.js中请求首页的商品数据,如下:

export function getHomeGoods(type,page) {
     
  return request({
     
    url: '/home/data',
    //配置对应的参数,有三种类型pop.new sell,而且数据不止一页
    params : {
     
      type,
      page,
    }
  })
}

hone.vue中获取响应的数据,和之前获取数据的方法一样,在methods中封装方法,在created中执行.

//data中初始化存储数据的变量
data() {
     
return {
     
 goods: {
     
        pop: {
      page: 0, list: [] },
        new: {
      page: 0, list: [] },
        sell: {
      page: 0, list: [] },
      },
  }
}
 getHomeGoods(type) {
     
 //而且每次重新调用的时候我们的页面都需要+1,所以在data中我们初始化的page为0
      let page = this.goods[type].page + 1;
      getHomeGoods(type, page).then((res) => {
     
        this.goods[type].list.push(...res.data.list);
        this.goods[type].page += 1;
        // 完成上拉加载更多
        this.$refs.scroll.finishPullUp();
      });
    },

  created() {
     
    //获取详情数据
    this.getHomeGoods("pop");
    this.getHomeGoods("new");
    this.getHomeGoods("sell");
  },

展示商品数据

在前面我们已经获取到了首页相关的商品数据,接下来我们要做的就是朵这些数据作业个展示.但是上面的数据有三种类型,分别是new,popsell,先不管其他的功能,设计将 pop相关的数据展示在页面中.

在这里我们封封装两个组件goodsgoodsItem组件,路径如下图所示
Vue项目首页实现思路总结_第8张图片
goodslist组件的核心代码如下:

//goods是从父组件传过来的值,然后把这里的每一项的值再传给goods的子组件,goodsItem
<template>
  <div class="good-list">
    <goods-list-item v-for = '(item,index) in goods' :key = 'index' :goods-item = 'item' ></goods-list-item>
  </div>
</template>

goodsListItem组件的核心代码如下

<template>
  <div class="goods-list-item">
    <img :src="showImg" @load = 'imageOnload' />
    <div class="goods-info">
      <p>{
     {
     goodsItem.title}}</p>
      <span class="price">{
     {
     goodsItem.price}}</span>
      <span class="collect">{
     {
     goodsItem.cfav}}</span>
    </div>
  </div>
</template>
//具体样式见源码

封装好这两个组件之后我们要做的就是在首页中展示这些数据:

 <goods-list :goods="goods"></goods-list>

点击TabBar切换对应的内容


其实这个功能我自己觉得还是蛮难的,我们请求到的数据有三种类型,点击不同的按钮就要展示不同的内容,这个肯定能想到的就是tabcontrol的点击得和我们下面的三种类型有某种绑定.

所以我们现在data中初始化一个变量:

//默认情况下为poo,即流行
currentType : 'pop'

既然我们要点击,那么肯定就要监听tabControl的点击事件,通过点击,改变currentType的值,从而给goods组件传对应类型的数据.

之前在封装tabcontrol`的时候,就有提到他发射出去了一个事件,我们在父组件中接收这个事件

//tabcontrol中的代码,发射事件
  methods : {
     
    tabClick(index) {
     
      this.currentIndex = index;
      this.$emit('tabClick',index);
    },
  }
//在父组件中使用组件时接受这个事件
 <tab-control
        class="tab-control"
        :titles=" ['流行','新款','精选']"
        @tabClick="tabClick"
 ></tab-control>

已经接受到事件之后,我们考虑如何将点击和类型之间进行绑定??

在发射事件的时候我们已经传了一个参数,那就是它们的下标index,那么就可以通过下标来进行绑定,当下标为0的时候currentTypepop,1的时候为new,以此类推,代码如下:

   tabClick(index) {
     
      switch (index) {
     
        case 0:
          this.currentType = "pop";
          break;
        case 1:
          this.currentType = "new";
          break;
        case 2:
          this.currentType = "sell";
      }
    },

然后在给goodslist传递数据的时候就可以点击哪个就传递哪个类型的数据.

 <goods-list :goods="goods[this.currentType].list"></goods-list>

但是呢,我们一般都不希望有太长的表达式,所以一般这种都是通过计算属性,如下:

<goods-list :goods="showGoods"></goods-list>

 computed: {
     
    showGoods() {
     
      return this.goods[this.currentType].list;
    },
  },

使用better-scroll对项目的滚动进行重构

封装scroll.vue

  • 对Better-Scroll进行封装: Scroll.vue
  • Home.vue和Scroll.vue之间进行通信
    • Home.vue将probeType设置为3
    • Scroll.vue需要通过$emit, 实时将事件发送到Home.vue

关于betterscroll的更多内容可以在github上搜索查看,
https://github.com/ustbhuangyi/better-scroll/blob/master/README_zh-CN.md

更多关于betterscroll的内容,[戳链接]

  1. 安装better-scroll
npm install better-scroll --save
  1. 封装一个独立的组件,用于作为滚动组件:Scroll
  2. 代码如下:components - commom - Bscroll - Bscroll.vue
<template>
  <div class="wrapper" ref="wrapper">
    <div class="content">
      <slot></slot>
    </div>
  </div>
</template>

<script>
import BScroll from "better-scroll";

export default {
     
  name: "Scroll",
  props: {
     
  //监听滚动位置
  //0,1都是不侦测实时位置
  //2:只要在滚动过程中侦测,手指离开后的惯性滚动中不侦测
  //3: 只要是滚都,都侦测
    probeType: {
     
      type: Number,
      default: 0,
    },
//这个配置用于做下拉刷新功能,默认为 false。当设置为 true 或者是一个 Object 的时候,可以开启下拉刷新,
    pullUpLoad: {
     
      type: Boolean,
      default: false,
    },
    data: {
     
      type: Array,
      default: () => {
     
        return [];
      },
    },
  },
  data() {
     
    return {
     
      scroll: null,
      message: "哈哈哈",
    };
  },
  mounted() {
     
    // 1.创建BScroll对象
    this.scroll = new BScroll(this.$refs.wrapper, {
     
      click: true,
      probeType: this.probeType,
      pullUpLoad: this.pullUpLoad,
    });

    // 2.监听滚动的位置
    this.scroll.on("scroll", (position) => {
     
      this.$emit("scroll", position);
    });

    // 3.监听上拉事件
    this.scroll.on("pullingUp", () => {
     
      this.$emit("pullingUp");
    });
    console.log(this.scroll);
  },
  //这些方法在后面会介绍到他们具体的用途
  methods: {
     
    scrollTo(x, y, time = 300) {
     
      this.scroll.scrollTo(x, y, time);
    },
    finishPullUp() {
     
      this.scroll.finishPullUp();
    },
    refresh() {
     
      this.scroll && this.scroll.refresh();
    },
  },
};
</script>

<style scoped>
</style>

在home.vue中使用better-scroll

//使用scroll将虽有要滚动的内容包裹起来.
    <scroll
      class="content"
      :probo-type="3"
      :pull-up-load="true"
      @scroll="contentScroll"
      @pullingUp="loadMore"
      ref="scroll"
    >
      <home-swiper :banners="banners" @swiperImageLoad="swiperImageLoad" />
      <home-recommend :recommends="recommends"></home-recommend>
      <feature-view></feature-view>
      <tab-control
        class="tab-control"
        :titles=" ['流行','新款','精选']"
        @tabClick="tabClick"
        ref="tabControl2"
      ></tab-control>
      <goods-list :goods="showGoods"></goods-list>
    </scroll>

通过阅读better-scroll的挂房文档,我们了解到,better-scroll的父元素必须有一个确定的高度,所以我们需要给content设置一个固定的高度.
Vue项目首页实现思路总结_第9张图片
也就是页面的这部分的内容需要是一个固定的高度,我们可以通过定位实现,具体的css代码如下:

.content {
     
  position: absolute;
  top: 44px;
  bottom: 49px;
  left: 0;
  right: 0;
  overflow: hidden;
}

回到顶部backTop组件的封装

在这里插入图片描述
上图展示的红色的向上的箭头就是我们需要封装的backtop组件,对象的图片在我们的img文件夹中有.
component-backTop中封装BackTop组件,详细如下:

<template>
  <div class="back-top">
    <img src="../../../assets/img/common/top.png" alt="">
  </div>
</template>

<script>
  export default {
     
    name : 'BackTop'
  }
</script>

<style scoped>
  .back-top {
     
    position: fixed;
    right : 6px;
    bottom : 50px;
  }

  .back-top img {
     
    width: 43px;
    height: 43px;
  }
</style>

在父组件中直接使用即可.

点击组件回到顶部的封装

直接给组件添加事件是不可行的,我们必须要加.native修饰符,点击组件后需要返回到页面顶部,这个功能该怎么实现呢???

我们之前使用了better-scroll对整个页面进行了重构,在better-scroll中有scrollTo方法,基本使用如下:

scrollTo(x, y, time, easing)
参数:
{Number} x 横轴坐标(单位 px)
{Number} y 纵轴坐标(单位 px)
{Number} time 滚动动画执行的时长(单位 ms)
{Object} easing 缓动函数,一般不建议修改,如果想修改,参考源码中的 ease.js 里的写法
返回值:无

在之前我们封装swiper.vue的时候已经封装好了这个方法,在home.vue中我们只需要直接调用这个方法即可,我们要返回的是页面顶部,所以x,y的值都为0.

<back-top @click.native="backTop"></back-top>
//methods
   backTop() {
     
      console.log(this.$refs.scroll);
      this.$refs.scroll.scrollTo(0, 0, 500);
    },

backTop组件的显示和隐藏

  • isShowBackTop: false(控制是否显示的变量)
  • 监听滚动, 拿到滚动的位置:
    • -position.y > 1000 -> isShowBackTop: true
    • isShowBackTop = -position.y > 1000

当页面滚动的库里大于1000px,组件显示,否则隐藏.
better-scroll中有这个方法可以监听滚动的位置,所以我们在父组件中就可以监听到滚动的距离,然后控制backtop的显示与隐藏.

//监听滚动的位置
   this.scroll.on("scroll", (position) => {
     
     this.$emit("scroll", position);
   });

home.vue中的相关代码如下:

    <scroll
      class="content"
      :probo-type="3"
      :pull-up-load="true"
      @scroll="contentScroll"
      @pullingUp="loadMore"
      ref="scroll"
    ></scroll>

 <back-top @click.native="backTop" v-show="isShowBackTop"></back-top>

data() {
     
	return {
     
 		isShowBackTop: false,
 	}
 }
methods : {
     
	contentScroll(position) {
     
      this.isShowBackTop = -position.y > 1000;
    },
}

解决首页中可滚动区域的问题

在昨晚上面的一系列工作后我们在首页中就可以使用better-scroll对页面进行滚动,但是我们会发现一个bug,当我们把页面滚动到一定的距离之后,再次滚动就不能进行滚动了,到底为什么会产生这个问题呢???

Better-Scroll在决定有多少区域可以滚动时, 是根据scrollerHeight属性决定

  • scrollerHeight属性是根据放Better-Scroll的content中的子组件的高度
    • 但是我们的首页中, 刚开始在计算scrollerHeight属性时, 是没有将图片计算在内的
    • 所以, 计算出来的告诉是错误的(1300+)
    • 后来图片加载进来之后有了新的高度, 但是scrollerHeight属性并没有进行更新.
    • 所以滚动出现了问题

bscroll.vue打印下面的语句就可以找到scrollerHeight

    console.log(this.scroll);

出现了bug我们就要解决bug,那么我们应该如何解决呢??
如何解决这个问题了?

  • 监听每一张图片是否加载完成, 只要有一张图片加载完成了, 执行一次refresh()
    • 如何监听图片加载完成了?
      • 原生的js监听图片: img.onload = function() {}
      • Vue中监听: @load=‘方法’
    • 调用scroll的refresh()

所以分析之后我们现在要做的事情是监听GoodSListItem中图片加载完毕的事件,当图片加载完成之后调用scroll.vue中的refresh()方法.画下面的一张图来表示他们之间的关系
Vue项目首页实现思路总结_第10张图片
我们需要在home.vue中使用上面的两个方法,bscroll.vue中的refresh很好获得,通过在父组件中使用$refs.scroll.refresh就可以拿到,但是监听图片加载完成的组件和home.vue中不是父子组件的关系,那么如何才能在home.vue中获取图片加载完成呢???

如何将GoodsListItem.vue中的事件传入到Home.vue中

  • 因为涉及到非父子组件的通信, 所以这里我们选择了事件总线
    • bus ->总线
    • Vue.prototype.bus = new Vue()
    • this.bus.emit(‘事件名称’, 参数)
    • this.bus.on(‘事件名称’, 回调函数(参数))

具体的代码如下:
mian.js中定义

   Vue.prototype.bus = new Vue();

goodsListItem.vue中将图片的加载事件发布到事件总线

//监听图片的加载
 <img :src="showImg" @load = 'imageOnload' />
//正常发射事件
methods : {
     
	imageOnload() {
     
      this.bus.$emit('itemImageOnload')
    },
}

home.vue设置当图片加载完成之后调用refresh函数

created() {
     
	this.bus.$on('itemImageOnload',this.$refs.scroll.refresh())
}

报错问题一: refresh找不到的问题

  • 第一: 在Scroll.vue中, 调用this.scroll的方法之前, 判断this.scroll对象是否有值
  • 第二: 在mounted生命周期函数中使用 this.$refs.scroll而不是created中
    acroll.vue中的代码
   refresh() {
     
      this.scroll && this.scroll.refresh();
    },

home.vue中的代码

mounted() {
     
	this.bus.$on('itemImageOnload',this.$refs.scroll.refresh())
}

解决问题二: 对于refresh非常频繁的问题, 进行防抖操作
我们监听的是图片加载完成后就调用refresh函数进行刷新,单数图片有很多张,加载一下就要进行一下刷新,所所以这个刷新是非常频繁的,我们需要对刷新进行防抖操作.

防抖函数起作用的过程:

  • 如果我们直接执行refresh, 那么refresh函数会被执行30次.
  • 可以将refresh函数传入到debounce函数中, 生成一个新的函数.
  • 之后在调用非常频繁的时候, 就使用新生成的函数.
  • 而新生成的函数, 并不会非常频繁的调用, 如果下一次执行来的非常快, 那么会将上一次取消掉
//我把它封装在common-utlis.js中
      debounce(func, delay) {
     
        let timer = null
        return function (...args) {
     
          if (timer) clearTimeout(timer)
          timer = setTimeout(() => {
     
            func.apply(this, args)
          }, delay)
        }
      },

home.vue中对刷新函数进行防抖处理,之前mounte总的代码修改为如下代码:

  mounted() {
     
    const refresh = debounce(this.$refs.scroll.refresh, 500);
    this.itemImgListener = ()=>{
     
      refresh();
    }
	this.bus.$on("itemImageOnload",this.itemImgListener);
  },

上拉加载更多功能的实现

之前我们在获取首页商品数据的时候就已经有提到页码的问题,我们是已经获取到了后面页码的数据,但是此时此刻页面上还没有后面页的数据,我们要在页面拉到最底部的时候实现一个加载更多的功能(在很多app和网页上都有这个功能).

我们使用的是better-scroll对页面进行的重构,那么在它的原生方法中就有一个,更多内容戳链接
scroll.vue中的设置如下:

 mounted() {
     
  	 // 3.监听上拉事件
    this.scroll.on("pullingUp", () => {
     
      this.$emit("pullingUp");
    });
    console.log(this.scroll);
  },
  methods: {
     
    finishPullUp() {
     
      this.scroll.finishPullUp();
    },
  },
};

home.vue中监听上拉加载更多

  <scroll
      class="content"
      :probo-type="3"
      :pull-up-load="true"
      @scroll="contentScroll"
      @pullingUp="loadMore"
      ref="scroll"
    ></scroll>

在请求数据的时候调用加载更多的功能

getHomeGoods(type) {
     
      let page = this.goods[type].page + 1;
      getHomeGoods(type, page).then((res) => {
     
        this.goods[type].list.push(...res.data.list);
        this.goods[type].page += 1;
        // 完成上拉加载更多
        this.$refs.scroll.finishPullUp();
      });
    },

loadMore中完成加载更多

  loadMore() {
     
      this.getHomeGoods(this.currentType);
    },

TabControl吸顶功能的实现

先来看一下我们要实现的效果:

获取到tabControl的offsetTop

  • 必须知道滚动到多少时, 开始有吸顶效果, 这个时候就需要获取tabControl的offsetTop
  • 但是, 如果直接在mounted中获取tabControl的offsetTop, 那么值是不正确.
  • 如何获取正确的值了?
    • 监听HomeSwiper中img的加载完成.
    • 加载完成后, 发出事件, 在Home.vue中, 获取正确的值.
    • 补充:
      • 为了不让HomeSwiper多次发出事件,
      • 可以使用isLoad的变量进行状态的记录.
    • 注意: 这里不进行多次调用和debounce的区别

因为TabControl上面总共有三部分的内容,除了轮播图剩下的两张图片很小,当轮播图加载完毕的时候其他的应该也已经加载完成了,所以我们只需要监听轮播图的架子啊完成,然后加载完成之后获取tabcontrol 的高度.

监听HomeSwiperimg的加载完成.

<swiper>
    <swiper-item v-for="(item,index) in banners" :key = 'index' >
      <a :href="item.link">
        <img :src="item.image"  @load = 'swiperLoad'>
      </a>
    </swiper-item>
  </swiper>

//可以使用isLoad的变量进行状态的记录.
   data() {
     
      return {
     
        isLoad : false,
      }
    }
//发出事件
   methods : {
     
      swiperLoad() {
     
        if(!this.isLoad) {
     
          this.$emit('swiperImageLoad');
          this.isLoad = true;
        }
      }
    }

在`home.vue中接受事件

  <home-swiper :banners="banners" @swiperImageLoad="swiperImageLoad" />

//加载完成后获取高度
    swiperImageLoad() {
     
      this.tabOffsetTop = this.$refs.tabControl2.$el.offsetTop;
    },

监听滚动, 动态的改变tabControl的样式(显示或隐藏)

  • 问题:动态的改变tabControl的样式时, 会出现两个问题:

    • 问题一: 下面的商品内容, 会突然上移
    • 问题二: tabControl虽然设置了fixed, 但是也随着Better-Scroll一起滚出去了.
  • 其他方案来解决停留问题.

    • 在最上面, 多复制了一份PlaceHolderTabControl组件对象, 利用它来实现停留效果.
    • 当用户滚动到一定位置时, PlaceHolderTabControl显示出来.
    • 当用户滚动没有达到一定位置时, PlaceHolderTabControl隐藏起来.

    如下:

//navbar的下面复制一份tabcontrol
<nav-bar class="home-nav">
      <slot slot="center">购物街</slot>
    </nav-bar>
    <tab-control
      class="tab-control"
      :titles=" ['流行','新款','精选']"
      @tabClick="tabClick"
      ref="tabControl1"
      v-show="isTabFixed"
    ></tab-control>

现在我们要控制的就是这个控件的显示与隐藏,之前的那个会随着内容的滚动一直滚上去,从而在视觉上给用户一种下面的tabcontrol固定住了的效果.
通过变脸过来控制他显示与否

//默认情况下为false,不显示
isTabFixed : false

那么什么时候应该显示呢???之前已经获取到了offsetTop,当滚动的距离大于等于之前获取的值时就应该显示.
如下:

//contentscroll函数在掐已经介绍过了
  contentScroll(position) {
     
      this.isTabFixed = -position.y >= this.tabOffsetTop;
    },

让Home保持原来的状态

当我们滑动一会首页的时候肯会切换到其他页面,我们希望再从其他页面切换回来的时候首页已经保持在我们离开时的那个位置.

  • 离开时, 保存一个位置信息saveY.
  • 进来时, 将位置设置为原来保存的位置saveY信息即可.
    • 注意: 最好回来时, 进行一次refresh()

设置home组件不随意销毁keep-alive
在app.vue中设置

<keep-alive >
      <router-view></router-view>
</keep-alive>
 //活跃的时候返回该位置
  activated() {
     
     //一进入组件就滚动到离开时保存的位置
      this.$refs.scroll && this.$refs.scroll.scrollTo(0, this.saveY, 0);
      //refresh():重新计算 better-scroll,
      this.$refs.scroll && this.$refs.scroll.refresh();
  },
  //离开时保存一个位置
  deactivated() {
     
    //1. 保存离开时的位置
    this.saveY = this.$refs.scroll.scroll.y;
  },

你可能感兴趣的:(vue)