一、list教程
1)简单场景:在页面中实现 长列表 或者 屏幕滚动 等效果时,可以使用list。(平常会使用div,但是当DOM结构复杂时,滚动页面会出现卡顿现象,因为Native无法复用div组件实现的列表元素)
而list由于会复用相同的type属性的list-item,使得更加流畅。
使用list 组件代码如下:
<template>
<!--列表实现-->
<list class="tutorial-page" onscrollbottom="loadMoreData">
<!--商品列表-->
<block for="{{productList}}">
<list-item type="product" class="content-item" οnclick="route($item.url)">
.........
</list-item>
</block>
<!--加载更多,type属性自定义命名为loadMore-->
<list-item type="loadMore" class="load-more">
<progress type="circular">
<text>加载更多</text>
</list-item>
</list>
</template>
要实现DOM片段的复用,要求相同type属性的DOM结构完全相同。所以,设置相同type属性的list-item是优化列表滚动性能的关键;
注意:
list-item内不能再嵌套list
list-item 的type属性为必填属性
list-item内部需谨慎使用if指令或for指令,因为相同的type属性的list-itemde DOM结构必须完全相同,而使用if指令或for指令会造成DOM结构差异;可以使用show指令代替if指令
2) 复杂场景:一个商品列表页,图片位于左边和图片位于右边的商品交错显示
列表中的列表元素可以分为三类,设置三种不同type属性的 list-item。分别为:
a)图片在左,文字在右的list-item,type属性自定义命名为productLeft;
b ) 图片在右,文字在左的list-item,type属性自定义命名为productRight;
c)加载更多 list-item,type属性自定义命名为loadMore
<template>
<!--列表实现-->
<list class="tutorial-page" onscrollbottom="loadMoreData">
<block for="{{productList}}">
<!--图片在左,文字在右的list-item,type属性自定义命名为productLeft-->
<list-item type="productLeft" class="content-item" οnclick="route($item.url)" if="{{$idx%2===0}}">
.........
</list-item>
<!--图片在右,文字在左的list-item,type属性自定义命名为productRight-->
<list-item type="productRight" class="content-item" οnclick="route($item.url)" if="{{$idx%2===1}}">
.........
</list-item>
</block>
<!--加载更多,type属性自定义命名为loadMore-->
<list-item type="loadMore" class="load-more">
<progress type="circular">
<text>加载更多</text>
</list-item>
</list>
</template>
list组件性能优化: 精简DOM层级,复用list-item,细粒度划分list-item,关闭scrollpage四个方面;
其中,精简DOM层级,复用list-item是使用list组件必须遵循的优化原则,细粒度划分list-item,关闭scrollpage适用于部分场景;
细粒度划分的list-item,即列表中相同的DOM结构划分为尽可能小的列表元素(即list-item)
关闭scrollpage:list组件支持属性scrollpage,默认关闭,标志是否将顶部页面中非list元素随list一起滚动。开启scrollpage会降低list渲染性能,
因此在开发者开启scrollpage前,推荐先尝试将顶部页面中非list的元素,作为一种或多种type属性的list-item,移入list从而达到关闭scrollpage提高渲染性能的目的;
3)list-item懒加载
懒加载,简称 lazyload,本质上是按需加载;
在传统页面中,常用的lazyload优化网页的性能:
实现:不加载全部页面资源,当资源即将呈现在浏览器 可视区域 时,再加载资源;
优点:加快渲染的同时避免流量浪费
在框架中,开发者也可以使用lazy-load概念优化列表的渲染:
实现:提前fetch请求足够的列表数据保存在内存变量memList中,当list滚动到底部时,从memList中提取部分数据来渲染list-item。当memList中数据不足时,提前fetch请求数据,填充memList
优点:每次网络请求与页面渲染的数量不一致,减少首屏渲染占用的JS执行时间,较少渲染后续list-item的等待时间;
上述实现代码:
import { dataComponentListLazyload } from '../../Common/js/data';
//模拟fetch请求数据
function callFetch(callback){
setTimeout(function(){
callback(dataComponentListLazyload);
},500)
}
//内存中存储的列表数据
let memList = [];
export default{
private:{
productList:[],
hasMoreData: true,
size:10,//每次 渲染数据的商品数
isLoadingData:false,//是否正在fetch请求数据
},
onInit(){
this.$page.setTitleBar({ text:'list-item懒加载'});
this.loadAndRender();//获取数据并渲染列表
},
loadAndRender(doRender = true ){
this.isLoadingData = true;
//重新请求数据并根据数据模式判断是否需要渲染列表
callFetch(function(resList){
this.isLoadingData = false;
if(!resList){
console.info('数据请求错误');
}
else if(!resList.length){
this.hasMoreData = false;
}else{
memList = memList.concat(resList);
if(doRender){
this._renderList();
}
}
}.bind(this))
},
_renderList(){
if(memList.length>0){
const list = memList.splice(0,this.size);
this.productList = this.productList.concat(list);
}
if(memList.length<=this.size){
//提前请求新的数据
this.loadAndRender(false);
}
},
renderMoreListItem(){
if(!isLoadingData){
this._renderList();
}
}
}
4)吸顶效果
传统页面的实现思路
a)当手指向上滑动超过吸顶元素的初始位置,把吸顶元素固定在顶部;
b)当手指向下滑动到底吸顶元素的初始位置时,取消吸顶元素在顶部的固定;
吸顶在传统web页面中的实现思路是监听scroll事件,当页面滚动到一定位置时,做一些事情来改变吸顶元素在窗口中的位置;
框架的实现思路
注意:在框架中scroll仅适用于list组件,且获取的值是滚动的相对坐标值,在使用时,需要通过累加来获取当前滚动位置的绝对坐标;
并且scroll在列表滚动时会被高频触发,存在潜在性能问题;
采用stack组件作为整个页面的容器,stack组件的特性为:每个直接子组件按照先后顺序依次堆叠,覆盖前一个子组件;
“吸顶”条件:
a)当页面向下滚动到 顶部元素 消失在视野时,吸顶元素 需要固定在顶部,因此,监听 顶部元素的disappear事件,显示mask;
b)当页面向上滚动到 顶部元素 出现杂视野时,吸顶元素需要取消固定,因此,监听 顶部元素的appear事件,隐藏mask