javascript --- [读书笔记] 回流与重绘 前端优化小结

1. 浏览器渲染原理

请说出: 从用户在浏览器地址输入网址,到看整个页面,中间都发生了哪些事情?

  • HTTP请求阶段
  • HTTP响应阶段
  • 浏览器渲染阶段

1.1 可能用到的知识

1.1.1 进程 Process、线程 Thread、 栈内存 Stack

  • 进程: 就是开的每一个程序: QQ、网易云音乐、Typora、VSCode…

  • 线程: 一个做的好的事情.

  • 栈内存: 用来提供一个环境,供我们执行代码

1.1.2 多任务

现代操作系统比如Mac OS X, UNIX, Linux, Windows等,都是支持"多任务"的操作系统

单核CPU执行多任务: 操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换到任务二,任务2执行0.01秒,再切换到任务3…由于CPU的执行速度很快,我们感觉就像所有任务都再同时执行一样

多核CPU执行多任务: 真正的并行执行多任务只能在多核CPU上实现,但是,由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行

有些进程不止同时干一件事情,就需要同时运行多个"子任务",我们把进程内的这些子任务称为线程.

多个线程可以同时执行,多线程的执行方式和多进程一样的,也是由操作系统在多个线程之间快速切换,让每个线程都短暂地交替运行,看起来就像同时执行一样

1.2 浏览器渲染原理

  • 在服务器上有程序员提前写的项目代码.
  • 它存放在服务器的磁盘中,标识符为 project
  • request请求阶段:客户端在浏览器输入网址的时候,浏览器会向服务器端发送请求(DNS解析、TCP的三次握手与四次挥手、HTTPS与HTTP的区别)
  • response响应阶段 :浏览器有个专门的端口监听这个请求,验证之后,将项目的源代码返回给客户端浏览器(HTTP状态码、304缓存、HTTP报文)
  • 客户端浏览器,拿到response响应的代码后,专门在内存中开辟一个栈内存,给代码的执行提供环境;同时分配一个主线程,去:
  1. 一行一行的解析核执行代码.
<!-- 栈内存 -->
进栈 --->  <!DOCTYPE html>  ---> 执行完出栈
  1. 遇到 link、script、img、a、video等 标签,主线程,开新建一个子线程去执行相应的内容.自己继续向下执行,同时会将该异步任务放到任务队列中
<!-- 栈内存 -->
进栈 ---> <link href="1.css"> ---> 会将异步任务放到 TaskQueue中
<!-- 任务队列 -->
任务1: 请求1.css
  1. 根据上述规则: 同步代码依次执行,遇到请求资源等异步代码,会创建一个新的子线程,去执行,并将任务放到任务队列(TaskQueue)中,然后继续向下执行…
  2. 因此主线程相当于是一直在执行同步任务.速度会很快
  3. 很快,主线程执行到页面底部
<!-- 栈内存 -->
进栈 ---> </html> ---> 出栈
<!-- 任务队列 -->
任务1: 请求1.css(未完成)
任务2: 请求2.css(未完成)
...
任务n: 请求...(未完成)
  1. 当执行完毕 </html>仅仅只在内存中生产了一个DOM树(注意,此时的异步任务还没有执行)

  2. 事件循环(Event Loop):生成DOM树之后,主线程会在任务队列(Task Queue)中去寻找已经准备好的异步任务,将其取出到栈内存中执行

<!-- 任务队列 -->
任务1: 请求1.css(未完成)
任务2: 请求2.css(已完成) ---> 取出 ---> [栈内存]
...
任务n: 请求n(已完成)<!-- 栈内存 -->
进栈 ---> 任务2 ---> 
  1. 步骤7的机制就是 事件循环(Event Loop), 当任务队列中的最后一条css获取成功且在栈内存中执行完毕之后,会在内存中生产一个CSSOM.然后浏览器会把 CSSOM树核DOM树结合在一起生成一棵 Render Tree

  2. 得到渲染树(Render Tree)之后,进行回流(Layout): 根据生成的渲染树,计算它们在设备视口(view port)内的确切位置和大小,这个计算阶段称为回流,之后就是重绘(Painting)

  3. Painting(重绘): 根据渲染树以及回流得到的几何信息,得到节点的绝对像素.

  4. 绝对像素被分到一个图层中,每个图层又会被加载到GPU中形成渲染纹理,最终渲染到页面上

[注] : 图层在GPU中, transform是不会触发repaint的,使用transform的图层都会由 独立的合成进程进行处理

1.2.1 性能优化

  • (减少HTTP请求次数和数据量大小) : 资源合并与压缩 (图片、样式、JS文件)
  • 图片懒加载: 第一次回流重绘的时候,不加载图片,当第一次渲染完成之后,当屏幕滚动到哪里,就加载对应位置的数据

1.2.2 优化点

  • request阶段: DNS解析、TCP的三次握手与四次挥手、HTTPS与HTTP的区别
  • response阶段: HTTP状态码、304缓存、HTTP报文

1.2.2.1 DNS预解析(Chrome浏览器完成)

当用户将鼠标停留在一个链接上,就预示着一个用户的偏好以及下一步的浏览行为.

  • 这时,Chrome就可以提前进行DNS Lookup及TCP握手
  • 当在地址栏触发高可能性选项时,同样会触发DNS lookup 和TCP预连接
  • Chrom会研究,每个人每天可能访问的网址,并对网页上的子资源尝试预解析、预加载以提高用户体验

1.3 性能优化

  • DOM的重绘和回流 Repaint & Reflow

  • 重绘: 元素样式的改变 (但宽度、大小、位置等不变)

如 outline,visibility,color,background-color等

  • 回流: 元素的大小或者位置发生了变化(当页面布局和集合信息发生变化的时候),触发了重新布局,导致渲染树,重新计算布局和渲染.

如添加或删除可见的DOM元素;元素的位置发生变化;元素的尺寸发生变化;内容发生变化(比如文本变化或图片被另一个不同尺寸的图片所代替);页面一开始渲染的时候(这个无法避免);因为回流是根据视口的大小来计算元素的位置和大小的,所以浏览器的窗口尺寸变化也会引发回流

注意: 回流一定会触发重绘,而重绘不一定会回流

  • 进可能减少 回流和重绘: 因为回流会根据当前窗口的大小计算元素的位置,然后回触发重绘.根据渲染树和回流的几何位置信息,算出元素的绝对像素,进而通知GPU重新渲染.

  • 避免DOM的回流: mvvm / mvc / virtual dom / dom diff

1.3.1 分离读写操作 (现代的浏览器都有渲染队列的机制)

​ offsetTop、 offsetLeft 、 offsetWidth、 offsetHeight、 clientTop、 clientLeft、 clientWidth、clientHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、getComputedStyle、currentStyle

<style>#box {width: 100px;height: 100px;background: red;border: 10px solid green;}
</style>
<body><div id="box"></div><script>let box = document.getElementById('box');box.style.width = '200px';box.style.height = '200px';box.style.margin = '10px';</script>
</body>
  • 当代浏览器,遇到了上面连续对DOM操作的代码,会将其先存在一个队列里面,然后一起进行一次回流.
  • 但如果遇到如下
<script>let box = document.getElementById('box');box.style.width = '200px';console.log(box.clientWidth);box.style.height = '200px';box.style.margin = '10px';
</script>
  • 以上代码会回流2次,因为在写操作中穿插了读操作,将读操作写在最下面可以使回流变成一次
<script>let box = document.getElementById('box');box.style.width = '200px';box.style.height = '200px';box.style.margin = '10px';console.log(box.clientWidth);
</script>

1.3.2样式集中改变

display.cssText = ‘width:20px; height:20px;’

divclassName = “box”

  • 不把样式分开写,统一写

  • 使用cssText

<script>let box = document.getElementById('box');box.style.cssText = 'width:200px;height:200px;margin:10px';
</script>
  • 使用添加类
<style>.box{width: 200px;height: 200px;margin: 10px;}
</style>
<script>let box = document.getElementById('box');box.className = 'box'
</script>

1.3.3 缓存布局信息

  • 看下面代码
<script>box.style.width = box.clientWidth + 10 + 'px';box.style.height = box.clientHeight + 10 + 'px'
</script>
  • 以上会触发2次回流,因为在遇到clientWidth、clientHeight时,浏览器都会清空渲染队列,实现一次回流

  • 改进做法:

<script>let width = box.clientWidth + 10 + 'px';let height = box.clientHeight + 10 + 'px';box.style.width = width;box.style.height = height;
</script>

1.3.4 元素批量修改

  • 例如我们经常动态的往ul中添加li属性, 你可能会写出如下代码
<ul id="box"></ul>
<script>let box = document.getElementById('box');for(let i =0; i< 5; i++){let li = document.createElement('li');li.innerHTML = i;box.appendChild(li);}
</script>
  • 以上会触发5次回流,可以将DOM操作,次数太少了看不出效果…我们尝试触发50000次回流,打开网页.你会发现浏览器卡顿了一下,然后渲染.

  • 尝试使用字符串拼接的方式来减少回流次数

<ul id="strBox"></ul>
<script>let strBox = document.getElement('strBox');let str = '';for(let i =0; i< 50000; i++){str += `<li>${i}</li>`;}strBox.innerHTML = str;
</script>
  • 在创建50000个li的情况下, 使用第一种方式大概花费330ms,而第二种方式只需100ms

1.3.5 硬件加速优化

  • CSS3硬件加速

[栗子] : 将盒子向右移动100像素

<script>box.style.left = '100px';
</script>

使用上面技术,会使页面产生一次回流

但是使用transform则不会产生回流

<script>box.style.transform = 'translateX(200)'
</script>
  • 你可能会好奇,为什么使用CSS3的API,不会产生回流,CSS3硬件加速的工作原理

  • 不会引起回流重绘的属性: transform \ opacity \ filters

  • 硬件加速可能存在的缺点: 过多使用会占用大量内存,性能消耗可能会比较严重、有时候会导致字体模糊等…

1.3.6 牺牲平滑度换取速度

  • 改变动画的最小平移单位,例如: 将原本需要1像素移动改为3像素…这样可以减少DOM的节流和重绘

1.4 小结

  • 从用户在浏览器地址输入网址,到看整个页面,中间都发生了哪些事情?
  1. 首先浏览器会根据输入的域名,通过域名服务器得ip地址,
  2. 然后会检查本地中是否有请求的资源,并且判断请求资源是否是最新的,如果是则返回,否则去服务器端得到资源(如果有错误,希望指出)
  3. 得到资源后,浏览器会在内存中开辟一个栈内存,同时分配一个主线程去从上到下解析文档结构,遇到 link、img等标签会将其放在任务队列中,继续向下执行
  4. 执行到最底部,此时生成了一个 DOM 树,然后进入事件循环(Event Loop)
  5. 事件循环: 浏览器从 任务队列(Task Queue)中取出已经准备好的的任务到栈内存中逐条执行
  6. 结束后会生成一棵CSSOM树, 然后主线程会根据 DOM树和 CSSOM树生成一棵渲染树(Render Tree)
  7. 之后会根据 视口大小(View Port)计算出节点的几何位置(称为节流)
  8. 之后是重绘: 根据渲染树和 节流算出的几何位置得到节点的确切位置.
  9. 完成重绘后,主线程会将每个每个绝对位置加载到GPU中,最终渲染成纹理,呈现在页面上.

参考

Chrome高性能的秘密:DNS预解析

CSS3硬件加速的工作原理

浏览器渲染原理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/250326.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ARP协议,以及ARP欺骗

1.定义&#xff1a; 地址解析协议&#xff0c;即ARP&#xff08;Address Resolution Protocol&#xff09;&#xff0c;是根据IP地址获取物理地址的一个TCP/IP协议。主机发送信息时将包含目标IP地址的ARP请求广播到网络上的所有主机&#xff0c;并接收返回消息&#xff0c;以此…

css --- [小结]让盒子水平垂直居中的解决方案

描述 有如下模型,想办法让 <style>.box{width: 500px;height: 500px;background: skyblue;} </style> <div class"box"><div class"inner"></div> </div>想办法让inner在box中水平垂直居中 方案1: 使用绝对定位 让…

数组洗牌 Fisher Yates

看播放器代码时发现的这个洗牌算法&#xff0c;再网上查了一番 作用是把数组变成随机序列&#xff0c;原理类似于从牌堆A中随机抽牌放进牌堆B 代码1&#xff1a; 返回一个由&#xff08;数组下标&#xff09;组成的数组 function random(length) {function shuffle (arr) {for…

一个不错的MYSQL数据库备份类,PHP版,一个文件,精简版

1 <?php2 class DbManage {3 var $db; // 数据库连接4 var $database; // 所用数据库5 var $sqldir; // 数据库备份文件夹6 // 换行符7 private $ds "\n";8 // 存储SQL的变量9 public $sqlContent "";10 // 每条sql…

javascript --- 堆栈内存与闭包的作用

你可能会用到的 堆内存: 存储引用类型值所在的空间栈内存: 存储基本类型值和存储代码所在空间函数上下文: JS每一个函数在执行的时候都会创建一个执行上下文 1. 堆内存中的数字和字符串都是相等的 let a {}, b0, c0; a[b] marron; a[c] Mar console.log(a[b]) // Mar第一…

python_sting字符串的方法及注释

string类型是python内置的类型&#xff0c;无需安装 方法/属性说明 capitalize() 把字符串的第一个字符改为大写 casefold() 把整个字符串的所有字符改为小写 center(width) 将字符串居中&#xff0c;并使用空格填充至长度width的新字符串 count(sub[,start[,end]]) …

作业3

import turtle turtle.bgcolor(red) turtle.color(yellow)turtle.fillcolor(yellow) turtle.begin_fill() for i in range(5):turtle.forward(100)turtle.right(144) turtle.end_fill() turtle.done()转载于:https://www.cnblogs.com/zhangkef97/p/9016608.html

javascript --- [小练习]变量提升、优先级综合

求下列函数输出结果 function Foo() {getName function() {console.log(1)}return this } Foo.getName function() {console.log(2) } Foo.prototype.getName function() {console.log(3) } var getName function() {console.log(4) }function getName() {console.log(5) …

Confluence 6 自定义 Decorator 模板的宏和针对高级用户

宏 页面的某些部分使用的是 Velocity 宏进行创建的&#xff0c;包括导航栏。有关宏的创建&#xff0c;你可以参考页面 Working With Decorator Macros 页面中的内容。 针对高级用户 velocity 目录是 Confluence 首先进行模板搜索的查找路径。你可以对 Confluence 的 velocity …

Matlab之rand(), randn(), randi()函数的使用方法

1. rand()函数用于生成取值在&#xff08;0~1&#xff09;之间均匀分布的伪随机数。rand(n)&#xff1a;生成n*n的0~1之间的满足均匀分布的伪随机矩阵&#xff1b;rand(m,n)&#xff1a;生成m*n的伪随机数&#xff1b;rand(m,n,double)&#xff1a;生成m*n的双精度伪随机数&am…

javascript --- [有趣的条件]双等号的隐式调用和数据劫持

1 双等号的隐式调用和数据劫持 求下面条件,在a为什么样时,等号成立 if(a 1 && a 2 && a 3){console.log(等号成立) }1.1 双等号的隐式转换 首先得了解双等号的隐式转换规则 等式备注对象 字符串隐式调用 toString方法将对象转换成字符串null undeifne…

分组查询

1、简单分组查询 语法形式&#xff1a; select function() from table_name where condition group by field; 进行分组查询时&#xff0c;分组所依据的字段上的值一定要有重复值&#xff0c;否则分组没有任何意义。 2、实现统计功能分组查询 关键字group by单独使用时&#xf…

深入探讨多模态模型和计算机视觉

近年来&#xff0c;机器学习领域在从图像识别到自然语言处理的不同问题类型上取得了显着进展。然而&#xff0c;这些模型中的大多数都对来自单一模态的数据进行操作&#xff0c;例如图像、文本或语音。相比之下&#xff0c;现实世界的数据通常来自多种模态&#xff0c;例如图像…

移动硬盘函数不正确要如何寻回资料

移动磁盘打不开函数不正确&#xff0c;是因为这个I盘的文件系统内部结构损坏导致的。要恢复里面的数据就必须要注意&#xff0c;这个盘不能格式化&#xff0c;否则数据会进一步损坏。具体的恢复方法看正文 工具/软件&#xff1a;AuroraDataRecovery 步骤1&#xff1a;先百度搜索…

vue --- [全家桶]vue-router

1. Vue - router Vue Router是 Vue.js 官方的路由管理器它和Vue.js的核心深度集成,可以非常方便的用于SPA应用程序的开发 Vue Router包含的功能有: 支持HTML5历史模式或hash模式支持嵌套路由支持路由参数支持编程式路由支持命名路由 <div id"app"><rout…

HashMap的四种访问方式

第一种&#xff1a;通过Map.entrySet使用iterator遍历key和value 1 public void visit_1(HashMap<String,Integer> hm){ 2 Iterator<Map.Entry<String,Integer>> it hm.entrySet().iterator(); 3 while(it.hasNext()){ 4 Map.Entry<String ,Integer> …

16.unix网络编程一卷 unp.h

unix网络编程 --ubuntu下建立编译环境 1.安装编译器&#xff0c;安装build-essential sudo apt-get install build-essential 2.下载本书的头文件 下载unp13e&#xff1a; http://pix.cs.olemiss.edu/csci561/prg561.1.html 3.进入unp13e 查看readme&#xff0c;照下列提示操作…

webpack --- [读书笔记] webpack中常用的一些配置项

1. Webpack 当前Web开发面临的困境 文件依赖关系错综复杂静态资源请求效率低模块化支持不友好浏览器对高级JavaScript特性兼容程度低 1.1 webpack概述 webpack是一个流行的前端项目构建工具,可以解决当前web开发中所面临的困境. webpack提供了友好的模块化支持,以及代码压…

spring中bean的作用域属性single与prototype的区别

https://blog.csdn.net/linwei_1029/article/details/18408363 转载于:https://www.cnblogs.com/stanljj/p/9907444.html

windows程序设计.第一个windos程序

Windows程序设计&#xff08;第5版&#xff09; windows程序需要调用API。 第一个Windows程序 1 /*HelloMsg.c -- Displays "Hello World!" in a message box*/ 2 #include <Windows.h> 3 4 int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE…