前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【译】如何避免在JavaScript中阻塞DOM

【译】如何避免在JavaScript中阻塞DOM

作者头像
腾讯IVWEB团队
发布2020-06-28 11:18:12
2.7K0
发布2020-06-28 11:18:12
举报

原文链接:https://www.sitepoint.com/avoiding-dom-blocking/

在浏览器和在诸如Node.js的运行时环境中,JavaScript程序是运行在单线程上的。这意味着当浏览器正在执行代码的时候,所有其他事情都会停下来:菜单命令,下载,渲染,DOM更新甚至GIF动画的播放。

对于用户来说,这个过程往往不是很明显,因为代码处理是以小块的形式快速发生。例如:当一个按钮被点击后触发了一个事件,这个事件执行一个函数,在函数内进行了一些计算并更新DOM。一旦完成,浏览器便空闲下来,从任务队列中取出下一个任务来处理。

JavaScript代码并不会等待一些事情的发生,试想一下如果每次发起Ajax请求整个应用都会停止响应是多么令人懊恼的事情。因此,JavaScript使用事件和回调机制来处理:当一个操作已经完成并且其结果已经就绪时,浏览器或者操作系统才会去回调一个特定的函数来执行后续的操作。

在下面的例子中,当按钮的点击事件触发时,相应的处理函数通过为元素添加CSS类的方式使其执行动画。而当动画结束时,这个CSS类会被一个匿名回调函数移除。

代码语言:javascript
复制
// raise an event when a button is clicked
document.getElementById('clickme').addEventListener('click', handleClick);

// handle button click event
function handleClick(e) {

  // get element to animate
  let sprite = document.getElementById('sprite');
  if (!sprite) return;

  // remove 'animate' class when animation ends
  sprite.addEventListener('animationend', () => {
    sprite.classList.remove('animate');
  });

  // add 'animate' class
  sprite.classList.add('animate');
}

ES2015提供了Promise语法,并且在ES2017中带来了async/await语法使得编程变得更容易,但其实在底层仍然使用的是回调。更多相关内容,参考"Flow Control in Modern JS"。

阻塞匪徒

不幸的是,一些JavaScript操作总是同步的,包括:

下面的CodePen展示了一个“入侵者”,它组合使用了CSS动画和JavaScript,来实现运动和肢体摆动。右侧的图片是一个基本的GIF动画。点击write按钮执行默认的100,000次sessionStory操作

CodePen

上述操作会导致DOM更新被阻塞。所以这个"入侵者"在大多数浏览器中会卡住不动,GIF动画会间断性的暂停。在较慢的设备上可能会显示“脚本未响应”的警告。

这是一个复杂的例子,但它演示了前端性能是如何受到基础操作影响的。

Web Workers

一个解决长时间运行任务的方案是利用web workers。它允许浏览器主应用程序启动后台脚本并使用消息事件来通信。举个例子:

代码语言:javascript
复制
// main.js
// are web workers supported?
if (!window.Worker) return;

// start web worker script
let myWorker = new Worker('myworker.js');

// message received from myWorker
myWorker.onmessage = e => {
  console.log('myworker sent:', e.data);
}

// send message to myWorker
myWorker.postMessage('hello');

web worker脚本:

代码语言:javascript
复制
// myworker.js
// start when a message is received
onmessage = e => {
  console.log('myworker received:', e.data);
  // ... long-running process ...
  // post message back
  postMessage('result');
};

一个worker甚至可以生成其他worker来执行复杂任务,类似线程操作。然而,worker被故意设计了一些限制,worker无法直接访问DOM或者localStorage(这样做会使JavaScript变成多线程模型并破坏浏览器的稳定性)。而且因为所有的消息都作为字符串发送,这允许传递JSON格式的对象,却不允许传递DOM节点。

worker可以接受一些window属性,web socket和IndexDB——但他们并不能改进前面展示的例子。在大多数场景下,worker被用来执行长时间计算任务——例如光线追踪、图像处理、比特币挖掘等。

(Node.js提供了类似web worker的child processes,它不同的地方在于提供的选项允许使用其他语言编写的可执行文件。)

硬件加速动画

大多数现代浏览器不会阻止硬件加速的CSS动画,这些动画运行在自己的层上。

默认设置下,前面的例子中“入侵者”通过改变left-margin来移动。这个属性及相似的属性如leftwidth会导致在动画的每一步浏览器都需要对整个页面文档进行回流和重绘。

当使用transform或者opacity这样的属性时,动画会更高效。因为它们可以使元素被放置到一个单独的合成层中,以便它可以利用GPU隔离地设置动画。

点击hardware acceleration选项,动画会立刻变得更加平滑。现在尝试另一次sessionStorage写入,我们会发现即使GIF动画仍然是停滞的,“入侵者”可以正常地持续运动。注意到因为肢体的摆动是由JavaScript控制的,所以它们仍然会因阻塞而暂停。

内存存储

更新内存中的对象要比使用写入磁盘的存储机制快得多。选择CodePen中的object存储类型然后点击write。可以看到结果会有所不同,它应该会比同等的sessionStorage操作快上10倍左右。

内存是不稳定的:关闭选项卡或者离开当前页面都会导致所有数据丢失。一个好的折衷办法是使用内存中的对象来提高性能,然后在合适的时机对数据进行持久化——例如在卸载页面时:

代码语言:javascript
复制
// get previously-saved data
var store = JSON.parse(localStorage.getItem('store'));

// initialise an empty store
if (!store || !store.initialized) {
  store = {
    initialized: true,
    username: 'anonymous'
    score: 0,
    best: { score: 1000, username: 'Alice' }
  }
};

// save to localStorage on page unload
window.addEventListener('unload', () => {
  localStorage.setItem('store', JSON.stringify(store));
});

游戏或者单页应用可能会遇到更多复杂的情况,比如,当遇到下面几种情况时数据需要被保存:

  • 几秒钟内没有用户活动(鼠标、触摸或键盘事件)
  • 游戏暂停或者选项卡切出到后台(见Page Visibility API)
  • 自然的暂停-例如当玩家死亡,完成一个关卡,在主屏幕之间移动等等

web性能

web性能是一个热门的话题。开发者们希望不受浏览器的限制,用户们希望应用程序的性能能像操作系统一样快速。

我们应当尽可能少地进行任务处理,并且不要明显地阻塞DOM。此外,幸运的是,在无法避免长时间运行任务的情况下,也存在一些选项可供开发者选择。

用户和客户们可能永远不会注意到你所做的速度优化,但当应用程序变慢时,他们总是会抱怨!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 阻塞匪徒
  • Web Workers
  • 硬件加速动画
  • 内存存储
  • web性能
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档