使用better-scroll 进行滚动时 是根据scrollerHeight属性决定的
scrollerHeight属性是根据content 中子组件的高度
但是在计算scrollerHeight属性时没有计算图片的高度
所以计算出来一开始是1300+ 是错误的
后来图片加载完了有了新的高度 但是scrollerHeight属性没有更新
所以滚动出现了问题
所以需要图片加载完就执行一次scroll里的refresh()刷新
每张图片都加载完成
监听图片是否加载完成
原生的js监听图片 img.οnlοad=function(){}
Vue中监听 @load='方法'
如何将GoodsListItem.vue中的事件传入到Home.vue中
因为涉及到非父子组件的通信 所以选择事件总线 $ bus 管理事件
在main.js里
Vue.prototype.$bus = new Vue()
vue实例作为事件总线 赋值给 $ bus 这样 $ bus就可以发射事件
this. $ bus .emit(‘事件名称’,参数)
在Home里监听图片加载 创建的时候就要监听
this. $ bus . $ on (‘事件名称’,回调函数)
最好放在mounted 里比较好
原本是如下图 监听到图片加载完 就执行一次scroll里的refresh 打印30次 ‘------’
同时可以加上这个判断 因为有可能图片加载的快 但是scroll 还没加载好 就可能会报错
但是频繁调用refresh不好! 所以需要防抖
防抖debounce 节流throttle
也就是等加载完再向服务器请求 而不是每一次都请求
因为加载完一张图片 就执行一次refresh(),第一页有30张,所以执行30次
为了不让refresh频繁调用 所以用debounce函数代替 传入refresh(注意没有小括号 所以不会执行)和延迟时间 返回一个新的函数 赋值给refresh
相当于
refresh = function (...args) {
if(timer) clearTimeout(timer)
timer = setTimeout(()=>{
func.apply(this,args)
},delay)
}
然后直接在imageLoad里调用refresh()
这里的refresh 只是个变量 并不是scroll.refresh()
所以
在第一次调用debounce时 时间为空 返回一个新的函数 设置定时器 时间为延迟时间
比如说5秒 也就是说 过了5秒就会执行func函数 也就是refresh
但是还没到5秒第二张图片就加载完 再一次调用debounce
这里我觉得很奇怪 不是会重新让timer=null 吗 那clearTimeout 就永远不会执行??
然后我就把= null 删掉 发现并没有报错 所以其实只是定义一下变量?
(或者应该是第二次调用refresh时 是返回的那个新函数 timer虽然是局部变量 但是被闭包使用过的变量 不会被销毁 所以 还保存着上一次的变量 然后再清空掉timer ?)
所以我把let timer= null 移到debounce函数外,不过这样不好,因为全局变量容易被修改,不安全,导致变量污染问题,为了解决变量污染问题,需要把变量封装到函数内,成为局部变量。
之所以让debounce返回值 给 refresh 其实就是上面所说的 refresh() 相当于 新的函数() 这样每次调用refresh时 就不用经过timer的初始值了。
因为变量不会被垃圾回收机制回收,就会造成内存消耗很大。
也会导致内存泄漏,也就是得不到回收,所以需要=null 清除掉
使用标记清除法
第二张加载完再次调用debounce时 timer不为空 因为第一次把setTimeout赋值给timer了 或许可以理解为 设置了一个定时器 所以此时执行clearTimeout(timer) 也就是阻止定时器启动 前提是在delay时间之前
以此类推到第30张图片加载完 最后一次调用debounce 返回函数里再次clearTimeout 接着又开启定时器 等待5秒后 执行func函数 这样就只会执行打印一次‘-----’ 只执行一次refresh()
const refresh = this.debounce(this.$refs.scroll.refresh)
this.$bus.$on('imageLoad',() => {
refresh()
})
debounce(func,delay){
let timer = null
return function (...args) {
if(timer) clearTimeout(timer)
timer = setTimeout(()=>{
func.apply(this,args)
},delay)
}
}
因为防抖可以算是个功能函数 所以封装到common文件夹里的utils.js
在Home 里 import {debounce} from ‘…/…/common/utils’
上拉加载
首先要监听滚到底部的位置 pullUpLoad
默认是false
在scroll组件里发射pullingUp方法给Home组件 使用loadMore
在loadMore里通过封装的getHomeGoods 里的参数currentType 切换类型
这里的refresh应该不需要了
因为默认上拉加载一次 所以需要每次加载完都finish一下
之前封装过finish了 所以只要在getHomeGoods里 this.$refs.scroll.finishPullUp()
tabControl的吸顶
先删除之前写的class=“tab-control” 之前写这个类为sticky 没用
改为 ref=“tabControl”
为了获取到距离顶端的高度 使用offsetTop
因为需要等轮播图加载完
所以在HomeSwiper组件里 监听图片加载完成
因为和Home是父子组件 所以直接监听swiperImageLoad方法
同时方法名也是这个 获取正确的高度值
这样写会打印4次offsetTop 因为轮播图有4张图片
不过在这里.once 只会打印一次
或者使用变量记录状态
接着判断什么时候吸顶 和回到顶部图标什么时候显示一样 通过判断position 是不是 大于 目标值
所以
动态绑定类 对象语法 如果isTabFixed是true就显示.fixed
然后就出问题了 并没有吸顶 反而是图片本来是平缓向上滑 结果经过tabControl就咻地一下跨过tabControl 因为tabControl脱标了 而且不见了 因为滚动content是transform里地translate fixed也会跟着滚动 虽然确实有这个类 但是确实没效果
所以先删掉fixed类 和 之前导航栏的fixed 因为之前fixed是使用浏览器原生滚动时 为了导航固定才用这个 但是 除了导航栏 以下都是better-scroll 局部滚动 所以 不会影响到导航栏
删掉fixed 意味着导航栏不脱标
如果把tab-control 复制到nav-bar下面 就会紧挨在一起
同时原本的tab-control不能删 待会有用
因为content是绝对定位 脱标 压住了 tab-control (把class:fixed换成tab-control类)
所以为了让它显示在图片上方 使用相对定位 不脱标 同时设置层级
补充:
标准文档流:从上而下,从左到右。
现象:1、连续空行和空格会被当成一个空格 解决办法是把行内元素写在一行或者给行内元素加浮动 2、一行高度不一样,底边对齐 3、自动换行
定位:
- relative 相对 不脱标 设置位置
- absolute 绝对 脱标 会盖住 设置位置 相对于第一个父元素定位
- fixed 固定 脱标 会盖住 相对于浏览器
- static 默认 相对于文档定位 跟着滚动
- 子绝父相 相对于父定位
- z-index 定位才能用 定位压住没定位的
浮动:脱标 设置宽高 父浮动子也设置浮动
左浮动可以让元素水平排列
现在就是滑动到一定高度再显示tab-control
使用v-show=“isTabFixed” 就好了
这样滚动到原本tab-control位置时 这两个tab重叠在一起 。。。。
有个小bug 因为有两个tab-control 当往下滑切换到新款 然后再回退时 会变成流行 是因为index不统一 之前在tabControl里使用currentIndex来显示当前点击的是哪个 所以首先要先获取到是哪个tabcontrol 就在ref上 前一个标记tabControl1 后一个标记tabControl2 然后在点击tab方法里 让currentIndex=index
然后刚才使用offsetTop那里要改成tabControl2
Home离开时记录状态
keep-alive 在App.vue里
离开时deactivated保存位置信息 获取scroll.y 保存到变量里
进来时activated设置位置 调用scrollTo 传入新的y值 同时刷新一下
在data里设置变量saveY:0