导航
一、利用组件来优化
二、利用wxs来优化
- 解密wxs的前世,从html的本质谈起<=点击查看
- html其实会转化为JavaScript(DOM对象),所以
html其实本质是js对象
- 小程序采用自己的标签,用精简版wxs编写的xml。
所以其实小程序页面是wxs对象
- 但有需要js的完整能力,于是
有两个独立线程(逻辑层js和渲染层wxs)
- wxs和js是独立隔离的,并用Native来做中转,所以
存在延时(需要通信)
- js对wxs通信
setData()
/其实onload时js执行了this.setData(data)=>所以设置值到页面是异步的
- 其它比如wx.showToast、wx.setNavigationBarTitle
这类和界面相关的
,相当于是给页面默认的一个toast控件、NavigationBa,setdata()√
- 获取视图、节点信息 wx.getSystemInfo、wx.createSelectorQuery、wx.createIntersectionObserver=>
所以获取视图节点信息是异步的
- wxs对js通信
事件
<==页面本质是js/wxs,且继承于事件==>页面的事件是属于渲染层的
像page生命周期ready是页面事件,其它是微信赋予js的事件,不是wxs的原生事件
官网生命周期介绍
setData({},()=>{})
的第二个参数,是setData的回调函数,相当于事件- 重复一遍:
页面的事件,如果执行函数在js中,则执行是wxs对js通信
wxs中执行callMethod可以调用js的函数数据
:√
- 那wxs能做什么?:如果某项操作,页面交互比重大于js操作(或者交互方面操作多于js的操作),则推荐使用wxs
- 由上图可知:
小程序最大的优化点就是这两条线的通信
- js对wxs的通信部分的优化:
避免频繁setdata
更新局部数据
:setData({
['List[' + index + ']']: newItem,
'obj.name':'hcl'
})- 如果页面交互比重大于js操作,则获取页面节点信息操作可以让wxs来执行wxs优化点之一
√
- wxs对js的通信部分优化:
页面事件,如果页面交互比重大于js操作,则推荐事件绑定wxs函数
1.事件绑定js中的函数: catch:tap="submitTap" // 注意:这里绑定函数其实是字符串,执行时是根据这个字符串查找js中的对应函数 2.事件绑定wxs中的函数 catch:tap="{{m1.submitTap}}" //这里是执行m1作用域中的submitTap函数。注意这里是变量。、 //对比以上,可以理解为还有个main作用域,是wxs中通信js的副本。
- 其它如
定时器/倒计时、动画
,如果页面交互比重大于js操作也推荐采用wxs
总结
wxs是小程序页面原生脚本,所以页面相关的处理,wxs是同步的实时的
如果你的小程序纯粹js来处理来处理逻辑,wxs是可以优化的方向!
wxs除了用于减少通信,还可以用于计算属性
:
小程序页面xml本质就是wxs嘛,所以可以直接访问wxs数据,而不能同步访问js数据
- 左右互搏--老顽童
- 本篇文章其实是认识小程序页面的原理,及双线程的原因及认识js和wxs直接的关系
- 目的是希望:
js和wxs能够左右互搏,两只手都发挥最大作用,进而提升小程序的效率!
2.实战----关于滑动后指定组件吸附置顶效果
A:第一种方式:传统方法
//第一步:获取节点到顶端的距离
// 计算分类icon到头部的距离
NodeToTopDistance(name,id){
const query = wx.createSelectorQuery()
query.select("#"+id).boundingClientRect()
query.exec((res)=>{
this.data[name]=res[0].top+res[0].height;
this.setData({
[name]:res[0].top+res[0].height
})
console.log("计算出来的距离:",name,this.data[name],res[0].top,res[0].height,res[0])
})
},
this.NodeToTopDistance("sortBarToTopDistance","sortBar")
//第二步,页面绑定滚动监听
onPageScroll(e){
if(!this.data.scrollTimeout){//没有开始计时则开始计时
this.pageScrollHandle(e);//处理逻辑函数
this.data.scrollTimeout=setTimeout(() => {
this.pageScrollHandle( this.data.pageScrollEvent);//处理逻辑函数
delete this.data.scrollTimeout;
}, 50);
}else{//开始计时了则保存最后一次的e
this.data.pageScrollEvent=e;
}
},
第一种方式缺点:
js通过setData频繁的和wxs通信。页面数据多时,会有明显卡顿现象
B:第二种方式:wxs中执行事件
//第一步:获取节点到顶端的距离
// 计算分类icon到头部的距离
NodeToTopDistance(name,id){
const query = wx.createSelectorQuery()
query.select("#"+id).boundingClientRect()
query.exec((res)=>{
this.data[name]=res[0].top+res[0].height;
this.setData({
[name]:res[0].top+res[0].height
})
console.log("计算出来的距离:",name,this.data[name],res[0].top,res[0].height,res[0])
})
},
this.NodeToTopDistance("sortBarToTopDistance","sortBar")
//第二步:修改下xml。这样才能绑定执行wxs函数
<scroll-view data-totopheight="{{sortBarToTopDistance}}" bindscroll="{{_wxs.scrollHandle}}">
。。。
。。。
</scroll-view>
//第三步,在wxs中,执行
var scrollTimeout;
var pageScrollEvent;
var scrollHandle=function(e,ins){
if(!scrollTimeout){//没有开始计时则开始计时
xx(e,ins)
scrollTimeout=setTimeout(function(){
xx(pageScrollEvent,ins)
scrollTimeout=undefined;
}, 50);
}else{//开始计时了则保存最后一次的e
pageScrollEvent=e;
}
}
var xx=function(e,ins){
var dataset = e.instance.getDataset();
//获取sortBar组件实例
var sortBar=ins.selectComponent("#sortBarContent")
// 获取搜索条组件实例
var search=ins.selectComponent("#search")
var sortBarToTopHeight=dataset.totopheight;
if(!sortBarToTopHeight) return ;//还没有获取到sortBar到顶端的距离。则不执行底下内容
else if(e.detail.scrollTop>sortBarToTopHeight){//开启 悬浮置顶
//isOpenToFixed=true;
console.log("fixed")
sortBar.addClass("fixed");
search.addClass("fixed");
}else{//关闭 悬浮置顶
//isOpenToFixed=false;
console.log("removeFixed")
sortBar.removeClass("fixed");
search.removeClass("fixed");
}
}
module.exports = {
scrollHandle:scrollHandle
};
第二种方式的优点:
从wxs获取节点接口可以发现:wxs获取节点是同步的
√(验证了xml其实就是wxs模型)
避免了js和wxs直接的通信,发现可以提升使用体验
C:第三种方式: wx.createIntersectionObserverd接口
//第一步:定义处理逻辑
intersectionObserver(){
this._observer = wx.createIntersectionObserver()
this._observer
.relativeToViewport({})
.observe('#sortBar', (res) => {
console.log(res)
/*
1:移出时,目标全都不可见,即:intersectionRatio==0
2:移入时,目标肯定是部分区域已经可视,即:intersectionRatio>0
3: 移出判断向上、向下移出,则判断res.intersectionRatio==0 && boundingClientRect的top。top为负数,则是在视图上面,即向上移出
4. 移入判断向上、向下移入,则判断boundingClientRect.bottom<relativeRect.bottom/2说明是上半屏。即向下移入
5. 因为是相交区域的上下左右边界。所以移出【全都移出才会触发这个函数】看top。移入【有部分可见就会触发这个函数】看bottom
6:只在切换可见、不可见时才会触发这个回调函数:显然是较好的方案
*/
if(res.intersectionRatio==0 && res.boundingClientRect.top<0){//向上移出
this.setData({
isOpenToFixed:true
})
}else if(res.intersectionRatio>0 && res.boundingClientRect.bottom<res.relativeRect.bottom/2){//向下移入
this.setData({
isOpenToFixed:false
})
}
})
},
//但要注意一点
onUnload() {
if (this._observer) this._observer.disconnect()
},
第三种方式优点:
代码少
只在必要的时候触发回调函数(移入移出时才触发
)。高效!