vue页面问题分享

项目介绍

这是一个微信端的搜索页面,主要包括三部分:搜索tag页,搜索结果页、搜素筛选页。

  • 搜索结果页有几种交互:排序筛选、tag子类筛选。

  • 筛选页交互:------

  • tag页:

    • 搜索框:重新搜索;
    • 联想词:搜索框内容改变,同步更新相关联想词;
    • 搜索历史:之前的搜索记录;
    • 热搜索词:接口返回的热搜词。


问题分享

问题一,关于筛选弹层的滚动穿透


前期方案:

给弹层需要滚动的父盒子添加样式{ height:100%; overflow-x: hidden; overflow-y: scroll; -webkit-overflow-scrolling: touch; overflow-scrolling: touch;},给弹层父盒子添加个事件 @touchmove="xx($event)"阻止默认事件。
在父盒子内子元素滚动到底部时会触发底层商品列表的滚动。

涉及问题:
  • 通过 event.stopPropagation(); 禁止冒泡;------没有效果。
  • 行内添加@movestop 禁止本身滚动事件 --------- 自己不能动了。

后期方案:

引用iscroll:(iscroll-lite-v5.js)

涉及问题1: 之前在需滚动盒子上设置的@movestop未删掉,导致本身不能滑动。

解决: 删掉所有前期设置的禁止touchmove相关事件

涉及问题2: 在整体配置成功后,由于iscroll本身为了防止事件冒泡(防止穿透),阻止掉了所有默认事件(包括点击事件),导致弹层内所有点击事件失效。

解决:在调用iscroll时,放开部分标签的默认事件 :preventDefaultException: { tagName: /^(INPUT|I|SPAN)$/}

that.myScroll = new IScroll("#wrapper", {
preventDefaultException: { tagName: /^(INPUT|I|SPAN)$/ }
});
涉及问题3: 放开权限的标签可以触发点击事件,但是在这些标签上面滑动又会引发穿透,导致下层的商品列表滑动。

解决: 在放开默认事件的标签上设: @touchmove.prevent,手动阻止元素本身本身的滑动事件( .prevent为阻止默认事件),这样也就不会把滑动事件冒泡到body。

{{item.name}}




问题2:关于页面回退


问题背景:在搜索结果页点击商品,跳到详情页,在详情页点击回退按钮,回到刚才搜索结果页。产品要求保存之前的全部状态(滚动高度,原交互痕迹)。

<注>:之前h5搜索是通过新开webview来解决回退保存状态的问题,微信浏览器不支持新开webview。
涉及问题:在微信浏览器中
  • iOS手机在页面回退时,不会执行生命周期中的方法(页面不会刷新),页面会保持原来的状态,符合产品要求;
  • 安卓系统会刷新页面(重新执行js),原来的状态全部被重置(高度回到顶部,操作状态全部清空)。
初步探索 : 滚动的时候记录当前高度,在页面刷新的时候读取当前高度;
不妥:因为页面首屏只加载20条数据,其他全部由滚动加载完成。当滚动触发多次滚动加载,页面超过20条数据时,点击跳到详情页再回退,
     因为dom没有那么长。无法滚动到记录的高度,只能滚动到第20条数据。滚动到不高不低的位置,很尴尬。(有些H5页面采用了这种做法)。
思考:由于dom高度不够无法滚动到相应位置;只有所有的数据全部一次性加载出来(不用滚动加载)才能通过以上方法实现。但是这样太耗性能。
发现:微信中的京东页面解决类似问题,在回退时完美重现了原来的状态(所有状态),就是有轻微的页面跳动痕迹(js回复原来的状态);

(微信--发现--购物)

解决:通过sessionStorage缓存住当前状态(拨云见日)。
  • 步骤1:在点击页面跳转时,sessionStorage存下需要记住的状态值,例如,目前滚动高度(window.scrollTop)和各种状态(控制交互痕迹样式的变量)以及跳转前请求接口得到的所有数据(商品列表首屏和滚动加载的所有值);

     注:这里有一点体现了vue的优势,数据驱动,这样减少了大量的代码去记录和恢复原来的状态,我们需要缓存和读取的都是数据。无论是首屏
       还是滚动加载,所有的数据都push到了对应的一个数组(我们只需要记住这个数组并在回退时从新给这个数组赋值)。
    

示例首屏给商品数据赋值(赋值到对应数组)

if(that.resulteven.length <= that.resultodd.length){
for (var i = 0; i < res.data.length; i++) {
if (i % 2 == 1) {
that.resultodd.push(res.data[i]);
} else if (i % 2 == 0) {
that.resulteven.push(res.data[i]);
}
}
}

记录缓存

sessionVal.offsettop = $(window).scrollTop();
sessionVal.resultodd = this.$parent.resultodd;
sessionVal.resulteven = this.$parent.resulteven;
sessionVal.iscur = this.$parent.iscur;    
sessionVal.panellist = this.$parent.panellist; 
sessionVal.maxprice = this.$parent.$refs.filterPanel.maxprice;    
sessionVal.minprice = this.$parent.$refs.filterPanel.minprice;   
sessionVal.filterissel = this.$parent.filterissel;         
sessionVal.page = this.$parent.page;
sessionVal.ajaxparameter = this.$parent.ajaxparameter;
    
sessionStorage.setItem("sessionVal", JSON.stringify(sessionVal)); //(因为sessionstorage内的数据类型是字符串,要先把对象转成json格式字符串)
sessionVal = {};
  • 步骤2:在页面刷新的时候去判断页面是否有sessionstorage,有的话就把里边的值取出来赋给相应的变量(此时不再请求接口也就没有重置页面,重现跳转前的状态和操作痕迹),没有的话就请求接口刷新页面。

有缓存读取缓存,没有则onload刷新

sessionStorage.getItem("sessionVal") && (sessionVal = JSON.parse(sessionStorage.getItem("sessionVal")));
if(!this.isIos && sessionVal){ //详情页回退,取出之前的状态
this.getSession(sessionVal);
}else{
this.ajax(this.ajaxparameter,'renovation');
this.filterAjax(); //筛选侧边栏请求
this.scrollLoad(); //滚动加载
}

读取的缓存做相应的赋值

getSession:function(sessionVal){
var that = this;
if(sessionVal.zc){
this.showsearchpanel = true;
this.$refs.searchPanel.searchtext = sessionVal.searchtext;
}
this.resultodd = sessionVal.resultodd;
this.resulteven = sessionVal.resulteven;
this.iscur = sessionVal.iscur;    
this.panellist = sessionVal.panellist ;    
this.filterissel = sessionVal.filterissel;
this.$refs.filterPanel.maxprice = sessionVal.maxprice;
this.$refs.filterPanel.minprice = sessionVal.minprice;
this.page = sessionVal.page;
this.ajaxparameter = sessionVal.ajaxparameter;
setTimeout(function() {
window.scrollTo(0,sessionVal.offsettop);
that.scrollLoad() //滚动加载
sessionStorage.removeItem("sessionVal");
}, 100);
}

小结

  1. 之所以把缓存赋值方法提出公用,因为这个方法,解决了之后的多个产品要求保留状态的小问题。
    例如:专场页回退到搜索页面并机动保持原来的联想词(写下搜索词并没搜索就跳转了,写什么就再现什么并展示对应联想词)

  2. ios为什么能够缓存状态:back-forward cache (往返缓存)

    • ios会通过这个bfcache缓存住整个页面(包括dom和js),在会退的时候回如果有缓存, 优先读取缓存,阻止页面onload(在缓存中读取数据不会触发load)。不刷新页面,直接展示缓存的页面( 减少网络往返传输,增进浏览器流畅度)。

    • 和我们通过sessionStorage去解决回退有异曲同工的效果,就是我们通过页面刷新原样渲染了一遍(优势:可以解决不可控的样式,如:ios搜索框关键词不显示)。

    • 不同浏览器对bfcache的支持程度不同,ios和安卓在微信浏览器的内核不同,导致差异。
      (微信6.1以上安卓用户,都是QQ浏览器的X5内核,5.4-6.1之间的版本,若用户安卓QQ浏览器则用X5,未安装则用自身系统内核;iOS一直用自己的内核,以前是webkit,现在都换成了blink内核)




问题三,小问题点总结


1. 遍历生成的多选框选点击样式控制

背景: 传统控制多选框样式的方法是移动特定class(addclass/removeclass),但是vue尽可能的放弃了对dom的操作,不能继续类似的操作。只能通过变量来控制class。
{{item.name}}
问题:变量是全局的,就一个。需要控制的class有很多,这样点击其中一个改变变量的值,会引起全部的多选框都改变状态。然而我们不可能设多个变量去控制。
解决:通过点击事件给当前被点击的元素添加属性,属性值为false或true,点击时切换属性值,通过该属性控制多选框样式。

把当前元素(v-for里的item)作为参数传到点击事件的方法,每个元素通过自身的属性值来判断是否选中。

{{item.name}}

注:Vue.set(item, 'iscur', false)方法给置顶元素(item)添加自定义属性。只有在当前元素还没有改属性的时候才设置( item.iscur == undefined),如果有该属性就直接改变属性值

toggleClass: function(item,index){
item.iscur == undefined ? Vue.set(item, 'iscur', false) : "";
item.iscur = !item.iscur;
注:Vue.set(item, 'iscur', false)方法给当前元素(item)添加自定义属性。每次点击只有在当前元素还没
    有添加该属性的时候才set( item.iscur == undefined),如果有该属性则属性值直接取反,避免重复
    的去set这个属性。


2. 通过子组件索引($refs )去定位到某个元素(组件)(在不操作dom的情况下)

问题:
1. 在vue中不能像以前JQ那样直接通过id去获取相应元素;
2. 子组件获取父组件的变量和方法,可以通过$parent和传参,但是平行组件和子组件中的方法和变量如何获取不是太方便。


解决:
  • 可以在子组件或子元素内用ref指定一个索引id, 在父组件js内通过this.$refs.xx(ref名)调用到子组件的方法和变量;


if(sessionVal.zc){
this.showsearchpanel = true;
this.$refs.searchPanel.searchtext = sessionVal.searchtext;
}
  • 子组件和子组件之间可以通过父组件作为桥接调用对方的方法和变量;
sessionVal.maxprice = this.$parent.$refs.filterPanel.maxprice;    
sessionVal.minprice = this.$parent.$refs.filterPanel.minprice;   
sessionVal.filterissel = this.$parent.filterissel;


3. 微信浏览器关闭页面

背景:首次访问页面,假如无关键词的默认跳到tag搜索页,搜索结果页在无关键词时是无结果页被隐藏再底部。点击搜索框旁边的取消按钮,应该直接关闭浏览器跳回微信聊天界面。
解决:微信内置功能 WeixinJSBridge.call('closeWindow');


4. 关于v-if/v-show对autofocus的影响

问题:在input行内写入autofocus属性,会让input在dom中渲染的时候主动获取焦点,同时会弹出软键盘。但给input的容器设v-if/v-show有不同的结果。
  • 设v-if的时候只要input框出现就会弹出软键盘(每次都会);
  • 设v-show的时候只在第一次出现的时候弹出软键盘,之后隐藏再出现都不会弹出。
原因:v-if每次都是移除元素,添加元素。每次都是重新渲染,所以每次都会触发autofocus;而v-show只有第一次出现是渲染,之后都是隐藏和展示(类似于display:none),只有第一次触发了autofocus 。


5. 关于this.在不同作用域的指代问题

问题:在项目初期引用当前组件的变量和方法时,直接写this.xxx。导致报错找不到相应的变量和方法。
原因: 因为在方法当中会有很多不同的作用域嵌套,this指代的是当前作用域的this。在每个方法最前边把this赋值给一个变量,之后引用都用这个变量替代this,就解决了类似问题。
  _getSuggestion: function() {
                var that = this; 
                $.getJSON("http://super.fanli.com/h5/search/ajaxGetSearchWord", {keyword: this.searchtext })
                    .done(function(res) {
                        if (res.status == 1) { 
                            that.searchresult = res.data;
                        }
                    });
            },

你可能感兴趣的:(vue页面问题分享)