什么是时间分片(Time Slicing)?
根据W3C性能小组的介绍,超过50ms的任务就是长任务。
图片来自使用 RAIL 模型评估性能
根据上图我们可以知道,当延迟超过100ms,用户就会察觉到轻微的延迟。
所以为了避免这种情况,我们可以使用两种方案,一种是Web Worker,另一种是时间切片(Time Slicing)。
Web Worker
我们都知道,JS是单线程,所以当我们在运行长任务时,容易造成页面假死的状态,虽然我们可以将任务放在任务队列中,通过异步的方式执行,但这并不能改变JS的本质。
所以为了改变这种现状,whatwg推出了Web Workers。
具体的语法不会进行说明,有兴趣的童鞋可以查看MDN Web Worker。
我们可以看看使用了Web Worker
之后的优化效果:
const testWorker = new Worker('./worker.js')
setTimeout(_ => {
testWorker.postMessage({})
testWorker.onmessage = function (ev) {
console.log(ev.data)
}
}, 5000)
// worker.js
self.onmessage = function () {
const start = performance.now()
while (performance.now() - start < 1000) {}
postMessage('done!')
}
代码以及截图来自于让你的网页更丝滑
时间切片(Time Slicing)
时间切片是一项使用得比较广的技术方案,它的本质就是将长任务分割为一个个执行时间很短的任务,然后再一个个地执行。
这个概念在我们日常的性能优化上是非常有用的。
例如当我们需要在页面中一次性插入一个长列表时(当然,通常这种情况,我们会使用分页去做)。
如果利用时间分片的概念来实现这个功能,我们可以使用requestAnimationFrame
+DocumentFragment
关于这两个API,我同样不会做详细的介绍,有兴趣的可以查看MDN requestAnimationFrame跟MDN DocumentFragment。
这里有两个DEMO,大家可以对比下流畅程度:
未使用时间分片:
<style>
* {
margin: 0;
padding: 0;
}
.list {
width: 60vw;
position: absolute;
left: 50%;
transform: translateX(-50%);
}
</style>
<ul class="list"></ul>
<script>
'use strict'
let list = document.querySelector('.list')
let total = 100000
for (let i = 0; i < total; ++i) {
let item = document.createElement('li')
item.innerText = `我是${i}`
list.appendChild(item)
}
</script>
使用时间分片:
<style>
* {
margin: 0;
padding: 0;
}
.list {
width: 60vw;
position: absolute;
left: 50%;
transform: translateX(-50%);
}
</style>
<ul class="list"></ul>
<script>
'use strict'
let list = document.querySelector('.list')
let total = 100000
let size = 20
let index = 0
const render = (total, index) => {
if (total <= 0) {
return
}
let curPage = Math.min(total, size)
window.requestAnimationFrame(() => {
let fragment = document.createDocumentFragment()
for (let i = 0; i < curPage; ++i) {
let item = document.createElement('li')
item.innerText = `我是${index + i}`
fragment.appendChild(item)
}
list.appendChild(fragment)
render(total - curPage, index + curPage)
})
}
render(total, index)
</script>
没有做太多的测评,但是从用户视觉上的感受来看就是,第一种方案,我就是想刷新都要打好几个转,往下滑的时候也有白屏的现象。
除了上述的生成DOM的方案,我们同样可以利用Web Api requestIdleCallback
以及ES6 API Generator]
来实现。
同样不会做太多的介绍,详细规则可以看MDN requestIdleCallback以及MDN Generator。
具体实现如下:
<style>
* {
margin: 0;
padding: 0;
}
.list {
width: 60vw;
position: absolute;
left: 50%;
transform: translateX(-50%);
}
</style>
<ul class="list"></ul>
<script>
'use strict'
function gen(task) {
requestIdleCallback(deadline => {
let next = task.next()
while (!next.done) {
if (deadline.timeRemaining() <= 0) {
gen(task)
return
}
next = task.next()
}
})
}
let list = document.querySelector('.list')
let total = 100000
function* loop() {
for (let i = 0; i < total; ++i) {
let item = document.createElement('li')
item.innerText = `我是${i}`
list.appendChild(item)
yield
}
}
gen(loop())
</script>
参考资料
- web-performance
- Measure Performance with the RAIL Model
- 让你的网页更丝滑
- 「前端进阶」高性能渲染十万条数据(时间分片)
后记
如果你喜欢探讨技术,或者对本文有任何的意见或建议,非常欢迎加鱼头微信好友一起探讨,当然,鱼头也非常希望能跟你一起聊生活,聊爱好,谈天说地。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- ELK学习笔记之filbeat pod无法将容器日志发送
- 小学生看了都会的Apache配置虚拟主机教程
- 【STM32F407开发板用户手册】第34章 STM32F407的SPI总线应用之驱动DAC8501
- 学长冷月带你怒刷LeetCode之反转链表
- php面试笔记-php基础知识-变量和引用变量
- 【STM32F429开发板用户手册】第34章 STM32F429的SPI总线应用之驱动DAC8501
- docker 安装mysql5.7
- php面试笔记(5)-php基础知识-自定义函数及内部函数考点
- AJAX的一个简单实例,跨域的解决,使用JQuery来进行ajax的调用
- ASP.NET Core 将文件夹内容输出为压缩包文件方法
- 如何阻止指定类型的SAP CRM附件被上传到服务器
- Docker 之NameSpace与Cgroup
- 微信网页扫码登录和公众号网页授权登录的比较
- 【TBase开源版测评】分布式数据库复制表关联查询
- v-decorator的取值与赋值