资讯专栏INFORMATION COLUMN

如何使页面交互更流畅

nifhlheimr / 1853人阅读

摘要:如下图所示渲染性能保证主动交互让用户感觉流畅一般超过认为是长任务会阻塞的运行如下是两种解决方案。下面是另外一种使页面流畅的方法时间分片。

流畅性

本篇是基于 FDCon2019 上《让你的网页更丝滑by刘博文》的复盘文。该课题也是博主感兴趣的领域, 后续会结合 React 的 Schedule 与该文进行进一步整合, 个人博客

被动交互: animation

主动交互: 鼠标、键盘

被动交互

当前市面上的设备频率在 60 HZ 以上。

主动交互

跑如下界面 https://code.h5jun.com/pojob

结合如下代码块, 可以看到 100ms 以下的点击是顺畅的, 而超过 100ms 的点击就会有卡顿现象。

</>复制代码

  1. var observer = new PerformanceObserver(function(list) {
  2. var perfEntries = list.getEntries()
  3. console.log(perfEntries)
  4. });
  5. observer.observe({entryTypes: ["longtask"]});
让用户感觉到流畅

衡量一个网页/App 是否流畅有个比较好用的 Rail 模型, 它大概有以下几个评判标准值。

</>复制代码

  1. Response —— 100ms
  2. Animation —— 16.7ms
  3. Idle —— 50ms
  4. Load —— 1000ms
像素管道

像素管道一般由 5 个部分组成。JavaScript、样式、布局、绘制、合成。如下图所示:

</>复制代码

  1. 渲染性能
保证主动交互让用户感觉流畅

</>复制代码

  1. function App() {
  2. useEffect(() => {
  3. setTimeout(_ => {
  4. const start = performance.now()
  5. while (performance.now() - start < 1000) { }
  6. console.log("done!")
  7. }, 5000)
  8. })
  9. return (
  10. );
  11. }

一般超过 50 ms 认为是 long task(长任务), long task 会阻塞 main thread 的运行, 如下是两种解决方案。

Web Worker

app.js 代码如下:

</>复制代码

  1. import React, {useEffect} from "react"
  2. import WorkerCode from "./worker"
  3. function App() {
  4. useEffect(() => {
  5. const testWorker = new Worker(WorkerCode)
  6. setTimeout(() => {
  7. testWorker.postMessage({})
  8. testWorker.onmessage = function(ev) {
  9. console.log(ev.data)
  10. }
  11. }, 5000)
  12. })
  13. return (
  14. );
  15. }

worker.js 代码如下:

</>复制代码

  1. const workerCode = () => {
  2. self.onmessage = function() {
  3. const start = performance.now()
  4. while (performance.now() - start < 1000) { }
  5. postMessage("done!")
  6. }
  7. }

此时在输入框输入时没有卡顿的感觉。

Time Slicing

下面是另外一种使页面流畅的方法 —— Time Slicing(时间分片)。

观察 Chrome 的 Performance, 火焰图如下,

从火焰图可以看出主线程被拆分为了多个时间分片, 所以不会造成卡顿。时间分片的代码片段如下所示:

</>复制代码

  1. function timeSlicing(gen) {
  2. if (typeof gen === "function") gen = gen()
  3. if (!gen || typeof gen.next !== "function") return
  4. (function next() {
  5. const res = gen.next() // ①
  6. if (res.done) return // ⑤
  7. setTimeout(next) // ③
  8. })()
  9. }
  10. // 调用时间分片函数
  11. timeSlicing(function* () {
  12. const start = performance.now()
  13. while (performance.now() - start < 1000) {
  14. console.log("执行逻辑")
  15. yield // ②
  16. }
  17. console.log("done") // ④
  18. })

该函数虽然代码量不长, 但却不易理解。前置知识 Generator

下面对该函数进行分析:

往时间分片函数 timeSlicing 中传入 generator 函数;

函数的执行顺序 —— ①、②、③、① (此时有个竞赛的关系, 如果 performance.now() - start < 1000 则继续 ②、③, 如果 performance.now() - start >= 1000 则跳出循环执行 ④、⑤);

conclusion

针对 long task 会阻塞 main thread 的运行的情形, 给出两种解决方案:

Web Worker: 使用 Web Worker 提供的多线程环境来处理 long task;

Time Slicing: 将主线程上的 long task 进行时间分片;

保证被动交互让用户感觉流畅

保证 16.7ms 有新的一帧传输到界面上。除去用户的逻辑代码, 一帧内留给浏览器整合的时间大概只有 6ms 左右, 回到像素管道上来, 我们可以从这几方面进行优化:

避免 CSS 选择器嵌套过深

Style 这部分的优化在 css 样式选择器的使用, css 选择器使用的层级越多, 耗费的时间越多。以下是测试 css 选择器不同层级筛选相同元素的一次测试结果。

</>复制代码

  1. div.box:not(:empty):last-of-type span 2.25ms
  2. index.html:85 .box--last span 0.28ms
  3. index.html:85 .box:nth-last-child(-n+1) span 2.51ms
避免布局重排

</>复制代码

  1. // 先修改值
  2. el.style.witdh = "100px"
  3. // 后取值
  4. const width = el.offsetWidth

这段代码有什么问题呢?

可以看到它会造成布局重排。

应对的策略是调整它们的执行顺序,

</>复制代码

  1. // 先取值
  2. const width = el.offsetWidth
  3. // 后修改值
  4. el.style.witdh = "100px"

可以看到经过调换顺序后, 后执行的 el.style.width 会新开一个像素管道, 而不会在原先的像素管道进行重排。

此外不要在循环中执行如下的操作,

</>复制代码

  1. for (var i = 0; i < 1000; i++) {
  2. const newWidth = container.offsetWidth; // ①
  3. boxes[i].style.width = newWidth + "px"; // ②
  4. }

可以在火焰图中看到它发生了重绘的警告,

执行顺序是 ①②①②①②①..., 假若我们在第一个 ① 后面插入一条竖线后 ①|②①②①②①, 其就变成先修改值后取值的情景, 所以也就发生了重绘!

正确的使用姿势应该如下:

</>复制代码

  1. const newWidth = container.offsetWidth;
  2. for (var i = 0; i < 1000; i++) {
  3. boxes[i].style.width = newWidth + "px";
  4. }
避免重绘

创建 Layers(图层) 可以避免重绘,

</>复制代码

  1. {
  2. transform: translateZ(0);
  3. }

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/109839.html

相关文章

  • 什么是交互设计?

    导语▼上篇讲到产品的一致性是产品的金钥匙,通过对一致性的设计可以使产品在品牌标识、用户使用和开发效率等方面得到提升。交互设计对于产品有哪些价值呢?本质是站在多维角度挖掘,分析,梳理,提炼用户更深层的需求和痛点,利用交互理论方法,保证产品的设计完整性,产品用户体验的流畅性,产品商业价值的最大化。What|什么是交互设计?我们的日常生活中处处可见交互行为,从使用淘宝购物到扫场所码向防疫人员展示核...

    ernest.wang 评论0 收藏0
  • 基于腾讯X5内核的混合APP开发模式

    摘要:腾讯浏览服务是致力于优化移动端体验的解决方案由浏览器团队出品使用腾讯浏览服务内核和腾讯浏览服务云端服务解决移动端使用过程中出现的拖拽不流畅切换留白窗口闪烁等问题。 这是一个移动互联的时代,这是一个属于Android,属于iOS的时代,在响应大众创业万众创新的号召下,越来越多的开发者转向了移动开发的领域。在Android,iOS刚刚兴起的时候,对于开发者而言,要开发一整套完整的App是一...

    Edison 评论0 收藏0
  • Win10应用设计的那些事儿

    摘要:如何挑选合适的导航结构导航设计是应用设计的关键,设计规范以下简称规范中将导航元素分为对等层次和历史导航等几类,例如表和透视表导航窗格是对等导航元素,中心大纲细节属于分层导航元素,返回则属于历史导航元素。 此文已由作者杨凯明授权网易云社区发布。 欢迎访问网易云社区,了解更多网易技术产品运营经验。 继Windows 10系统发布之后,很多Windows用户更新了系统。win10系统的发布,...

    ad6623 评论0 收藏0
  • 原生js练习题---第五课

    摘要:那该如何是好原题给出思路是让事件负责标记按键就好了,而方向键的事件处理使用设个周期比较小的定时器持续监听,由于周期小,长按时就会立刻执行相应的事件处理,效果更加流畅。闪烁实现效果闪烁简单的一个定时器应用,用或都可以实现。 0x1模拟select控件 实现效果:5-01模拟select控件 比较简单的点击事件处理,也就处理点击选择框展示菜单、点击菜单选择、点击页面任意角落隐藏菜单这三件事...

    winterdawn 评论0 收藏0
  • 前端性能优化

    摘要:端优谈谈关于前端的缓存的问题我们都知道对页面进行缓存能够有利于减少请求发送,从而达到对页面的优化。而作为一名有追求的前端,势必要力所能及地优化我们前端页面的性能。这种方式主要解决了浅谈前端中的过早优化问题过早优化是万恶之源。 优化向:单页应用多路由预渲染指南 Ajax 技术的出现,让我们的 Web 应用能够在不刷新的状态下显示不同页面的内容,这就是单页应用。在一个单页应用中,往往只有一...

    Dean 评论0 收藏0

发表评论

0条评论

nifhlheimr

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<