内容简介:导语: 当我们有10万条或者更多的数据需要展示在页面中,会出现哪些问题呢,我们应该怎样处理和优化呢?当我们有10万条或者更多的数据需要展示在页面中,我们应该怎样处理呢?这里主要的方式有两种:
导语: 当我们有10万条或者更多的数据需要展示在页面中,会出现哪些问题呢,我们应该怎样处理和优化呢?
当我们有10万条或者更多的数据需要展示在页面中,我们应该怎样处理呢?
这里主要的方式有两种:
-
懒加载:即监听scroll事件或使用
IntersecionObserver
监听; -
可视区域的渲染:仅在可视区域展示数据,为保证滚动条的完整性,非可视区域使用占位元素的高度后者容器的位移来撑开。
1. 懒加载
懒加载的方式有两种:监听scroll事件或使用 IntersecionObserver
监听某个元素。
1.1 scroll事件
监听滚动事件应该是我们使用的最多的事件了,当滚动条滑动到页面最底部或者将要滑动到最底部的时候,去加载下一页的数据。同时图片懒加载或者其他组件的懒加载也可以依赖于滚动事件!
为了优化滚动事件,我们也会给滚动事件添加上 防抖和节流
。
这是一个很常见的demo,无限加载数据系列,当滚动条滑动到底部时,加载下一页的数据: 监听滚动事件懒加载数据 。
请分别用微信和QQ扫下这个二维码体验一下,体验时,请点击“向左”的按钮关闭代码的部分:
其实我们发现会有一个很有意思的体验:在android中的微信和QQ中基本可以无限的滑动,在iOS的微信中也可以,但是在iOS的QQ中,当我们一直滑动时,需要滑动到底部,然后才能加载新的数据,才能继续滑动!
这种原因是iOS不同的webview造成的。在iOS中,UIWebView是在iOS2中出现的,性能更好的WKWebView跟随iOS8一起出现。从性能方面来说,WKWebView会比UIWebView高很多,可以算是一次飞跃。它采用了跨进程的方案,用 Nitro JS 解析器,高达 60fps 的刷新率。同时,提供了很好的H5页面支持,类比UIWebView还多提供了一个加载进度的属性。同时,在UIWebView中,无论是页面的body滚动,还是div级别的局部滚动,都不会实时触发,而是在滚动结束后才会触发!
可以分别体验下面的两个二维码,左侧的为body级别的滚动,右侧为div的局部滚动:
总结下滚动事件在不同的webview中的表现情况:
机型 | body滚动 | 局部滚动 |
---|---|---|
iOS WKWebView | 实时触发 | 实时触发 |
iOS UIWebView | 无法实时触发 | 无法实时触发 |
Android | 实时触发 | 实时触发 |
那么该如何优化滚动的性能呢?
首先就是防抖和节流。在Chrome浏览器中使用 performance.now()
测试滚动每次触发的间隔,可以看到,频率大概在16ms左右,但很多时候我们并不需要这么快地触发我们的监听函数,而且,比如图片懒加载等场景,也要及时的更新一次,这时候就用到了防抖和节流!
再有就是使用 requestAnimationFrame
与 requestIdleCallback
代替定时器。若使用定时器进行间歇性触发监听函数,会出现掉帧的情况,我们也知道 setTimeout(fn, 100)
,不一定是正好在100ms后触发fn函数,而是等浏览器空闲之后才调用fn函数,当多次积累后就会产生掉帧的,那么使用 requestAnimationFrame
或 requestIdleCallback
,则浏览器会根据自己渲染的频率适时地执行回调。在追求高性能的渲染效果时,可以考虑用requestIdleCallback()和requestAnimationFrame()代替定时器。前者适合流畅的动画效果场景,后者适用于分离一些优先级低的操作逻辑,使用时需要考虑清楚
1.2 IntersecionObserver
使用IntersecionObserver也可以实现无限滚动,比如在底部监听一个透明的元素,当该元素可见时就加载新的资源!
demo: IntersecionObserver实现无限滚动
不过也应当看到的是IntersecionObserver的支持程度还不太好,如果要使用的话,还是需要polyfill方案!
而且IntersecionObserver在图片懒加载和组件懒加载的过程中,非常有用,我们后续会进行了解!
2. 模拟滚动
模拟滚动最代表的例子就是 iScroll
,监听手势的touchmove事件,然后使用CSS3中的transform产生位移。
不过在模拟滚动的过程中,若图片比较多时,可以感觉到滚动时有明显的卡顿感。查看这个样例: https://www.xiabingbao.com/demos/20190423/iscroll-scroll.html 。
这是利用IScroll中的 scroll
事件,无限地向页面中添加元素。不过当页面的数据是无限加载时,应当使用 iscroll-infinite
这个类库更好,这部分留到后面讲解!
3. 可视区域数据的渲染
上面的几种方法里,我们都是无限地直接往页面中添加元素,滑动的页码越大,页面中的DOM元素也就越多。但实际上,这是没有必要的,滚动到可视区域外的元素,用户是看不到的,也没不要保留在页面中,可以用一个占位元素或者transform撑开上面所有不可见元素的高度,让滚动条能够正常的上下滑动即可!
撑开上半部分不可见区域的高度,有两种方法,一种是在容器内的最顶部设置一个占位元素,这个占位元素的高度就是消失的所有DOM的高度;再一个就是给容器或者占位元素一个transform的位移!这里我们使用给占位元素一个高度撑起顶部的滚动区域。
3.1 固定高度的item
若每个item的height/margin/padding等数据都是固定写死的,这种情况比较容易实现!获取一次数据后,不用每次都重新计算。
每次滚动时,都要计算应当渲染列表的哪部分! start
表示列表开始的位置, fixedScrollTop
表示顶部占位元素的距离
// 滚动处理函数 function handleScroller() { let lastStart = 0; // 上次开始的位置 const item = document.querySelector('.container .item'); const itemStyle = getComputedStyle(item); const itemHeight = item.offsetHeight + parseInt(itemStyle['marginTop']) + parseInt(itemStyle['marginBottom']); return function() { const currentScrollTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop); const fixedScrollTop = currentScrollTop - currentScrollTop % itemHeight; let start = Math.floor( currentScrollTop/itemHeight ); // 可视区域开始渲染的位置 if (lastStart!==start) { lastStart = start; createDom(start, count, fixedScrollTop); } } }
设置顶部滚动的高度:
function createDom(start, count, height) { const container = document.querySelector('.container'); // container.style.transform = `translateY(${height}px)`; // 给容易一个位移 let div = document.createDocumentFragment(); // 创建占位元素 if (height) { let p = document.createElement('p'); p.style.height = height + 'px'; div.appendChild(p); } for(let i=start, len=start+count; i<len; i++) { let item = document.createElement('div'); item.className = 'item'; item.innerHTML = i; div.appendChild(item); } // 为了方便处理,我们这里采用了更新container中的全部元素 // 你也可以尝试只增加/删除首位的元素,中间元素不变 container.innerHTML = ''; container.appendChild(div); }
可以点击链接查看样例: 可视区域渲染之DOM元素增减 ,审查元素可以看得更清晰,无论我们怎么滚动, .container
中永远只有那么几个元素!
3.2 每个item的高度都不一定
待定,有个问题一直搞不定
3.3 DOM的复用
重点来了,在上面的章节里,我们进行了进一步的优化,只渲染可视区域内的数据。但是还是存在一个重要的问题: 频繁的改动DOM ,这样会频繁的引起页面的重绘。这里我们的思想是对页面中DOM元素进行复用,如下图中所示,从上面滑出的元素,可以直接定位下面再重新装填元素,反之亦然!
可以点击链接查看demo,在审查元素中我们可以看到,在滚动的过程中,DOM并没有被删掉,而是改变了transform,放到了最下面: 可视区域渲染之DOM元素复用 。
// 更新页面中DOM元素的位置 function updateDom(start, count, itemHeight, height) { document.querySelector('.container .content').style.transform = 'translateY('+height+'px)'; for (var i = start, len=start+count; i < len; i++) { var index = i % count; var cssIndex = (i-start) % len; document.querySelector('.item' + index).innerHTML = i; document.querySelector('.item' + index).style.transform = 'translateY('+itemHeight*cssIndex+'px)'; } }
这里我们还是监听了元素的scroll事件,我们在上面一笔带过的 iScroll-infinite
类库,是使用了模拟滚动+DOM复用。点击链接查看demo: iScroll-infinite实现的无限加载 。
4. 总结
最有效的方案应当是模拟滚动+DOM复用,既解决了滚动不能实时生效的问题,又能解决页面中DOM元素过多,造成滚动中卡顿的问题。
总是想着要讲的有很多,不过最后好像哪方面都欠缺了一点,后面有机会再针对其中的某一个点再深入进行探讨!例如使用 IntersecionObserver
实现Vue组件的懒加载,如何自己实现一个简单的模拟滚动等。
参考:
http://taobaofed.org/blog/2017/03/02/thinking-in-request-animation-frame
http://wiki.jikexueyuan.com/project/iscroll-5/customevents.html
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 极简实现列表查看页面
- React通过redux缓存列表数据以及滑动位置,回退时恢复页面状态
- 用weexplus从0到1写一个app(2)-页面跳转和文章列表及文章详情的编写
- C#列表到列表转换
- Python笔记(二):列表+列表数据处理+函数
- python创建列表和向列表添加元素方法
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
区块链与人工智能:数字经济新时代
高航、俞学劢、王毛路 / 电子工业出版社 / 2018-7-23 / 80
《区块链与人工智能》是畅销书《区块链与新经济:数字货币2.0时代》全新修订升级版。本书是市场上为数不多的系统阐述区块链、人工智能技术与产业的入门级系统教程。从比特币到各类数字货币(代币),从基础原理到应用探讨,全景式呈现区块链与人工智能的发展脉络,既有历史的厚重感也有科技的未来感。本书的另一个亮点是系统整理了区块链创业地图,是一本关于区块链创业、应用、媒体的学习指南,以太坊创始人Vitalik专门......一起来看看 《区块链与人工智能:数字经济新时代》 这本书的介绍吧!
HTML 压缩/解压工具
在线压缩/解压 HTML 代码
UNIX 时间戳转换
UNIX 时间戳转换