浏览器的渲原理
### 常见的面试题:
- 前端优化 ( 雅虎14条优化原则 )
- 浏览器兼容
- 浏览器的工作/渲染原理
- 浏览器的缓存问题
- 响应式布局 ( 媒体查询 )
- vue的问题较多 ( 路由传参 组件传值 生命周期 双向数据绑定的原理 )
- 着重问js ( 一些公司 )
# 浏览器的渲染原理
#### 问题: 当我们在浏览器的地址栏输入网址(譬如: www.baidu.com), 然后回车,回车这一瞬间到看到页面到底发生了什么呢?
域名解析( 浏览器自身的DNS缓存-- 操作系统的自身的DNS缓存--本地host -- DNS服务器)
--> 发起TCP的3次握手
--> 建立TCP连接后发起http请求
--> 服务器响应http请求,浏览器得到html代码
--> 浏览器解析html代码,并请求html代码中的资源(如js、css、图片等)
--> 浏览器对页面进行渲染呈现给用户
![](C:\Users\Lenovo\Desktop\面试题讲解\1\whl-前端性能优化\新建文件夹\request01.png)
## 浏览器的组成
![](C:\Users\Lenovo\Desktop\面试题讲解\1\whl-前端性能优化\新建文件夹\How-browsers-work1.png)
- **用户界面**- 包括地址栏、后退/前进按钮、书签目录等,也就是你所看到的除了用来显示你所请求页面的主窗口之外的其他部分
- **浏览器引擎**- 用来查询及操作渲染引擎的接口
- **渲染引擎(浏览器内核**)- **负责渲染页面,** 用来显示请求的内容,例如,如果请求内容为html,它负责解析html及css,并将解析后的结果显示出来---> **不同的浏览器有不同的渲染引擎, 同一份代码使用不同的引擎解析, 解析的结果不一样, 会有兼容性问题出现**
- 网络- 用来完成网络调用,例如http请求,它具有平台无关的接口,可以在不同平台上工作
- UI 后端- 用来绘制类似组合选择框及对话框等基本组件,具有不特定于某个平台的通用接口,底层使用操作系统的用户接口
- 网络模块, ui模块浏览器自己提供的,不需要管
- JS解释器- 用来解释执行JS代码
- 数据存储- 属于持久层,浏览器需要在硬盘中保存类似cookie的各种数据,HTML5定义了Storage技术,这是一种轻量级完整的客户端存储技术(cookie, localStorage, sessionStorage)
## 主流的渲染引擎
> 浏览器的渲染引擎也叫排版引擎,或者是**浏览器内核**
主流的 渲染引擎 有
- **Chrome浏览器**: Blink引擎(WebKit的一个分支)。
- **Safari浏览器**: WebKit引擎,windows版本2008年3月18日推出正式版,但苹果已于2012年7月25日停止开发Windows版的Safari。
- **FireFox浏览器**: Gecko引擎。
- **Opera浏览器**: Blink引擎(早期版使用Presto引擎)。
- **Internet Explorer浏览器**: Trident引擎 。
- **Microsoft Edge浏览器**: EdgeHTML引擎(Trident的一个分支)。
## 渲染引擎工作原理
![](C:\Users\Lenovo\Desktop\面试题讲解\1\whl-前端性能优化\新建文件夹\Render-Process.jpg)
**渲染引擎解析的基本流程:**
```
1. 解析HTML, 构建Dom树.(包含页面全部的元素 )
2. 构建渲染树( DOM树与css结合, 形成渲染树 ),渲染树并不等同于Dom树,因为像`head`标签 或 `display: none`这样的元素就没有必要放到渲染树中了,但是它们在Dom树中。
3. 重排(回流): 对渲染树进行布局,定位坐标和大小、确定是否换行、确定position、overflow、z-index等等,这个过程叫`layout` 或 `reflow`。
4. 重绘: 绘制渲染树,调用操作系统底层API(UI Backend)进行绘图操作。
```
**webkit内核工作流程**
![](C:\Users\Lenovo\Desktop\面试题讲解\1\whl-前端性能优化\新建文件夹\webkitflow.png)
**gecko内核工作流程**
![](C:\Users\Lenovo\Desktop\面试题讲解\1\whl-前端性能优化\新建文件夹\gecko.jpg)
结论:浏览器能够解析HTML文件,并且显示到页面中。所以我们写的文件能够使用浏览器打开并且能够看到效果。
## 性能优化:重绘与回流(重排)
> 重绘在最后一步, 重排必定会引起重绘
`重排(reflow)` : 当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。
`重绘(repaint)` :当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color color。
1. 每个页面至少需要一次重排+重绘。
2. 重排必将引起重绘
#### 重排什么时候发生?(页面的布局发生改变)
1、添加或者删除可见的DOM元素;
2、元素位置改变;
3、元素尺寸改变——边距、边框、宽度和高度
4、内容改变——比如文本改变 或者 图片大小改变而引起的计算值宽度和高度改变;
5、页面渲染初始化;
6、浏览器窗口尺寸改变——resize事件发生时;
```js
var s = document.body.style;
s.padding = "2px"; // 回流+重绘
s.border = "1px solid red"; // 再一次 回流+重绘
s.color = "blue"; // 再一次重绘
s.backgroundColor = "#ccc"; // 再一次 重绘
s.fontSize = "14px"; // 再一次 回流+重绘
// 添加node,再一次 回流+重绘
document.body.appendChild(document.createTextNode('abc!'));
```
#### + 如何性能优化? 尽量减少重绘与回流的次数
> 注意: display:none会触发reflow,而visibility:hidden只会触发repaint,因为没有发现位置变化。
+ 直接使用className修改样式,少用style设置样式
``` div.style.padding = 10px div.style.height = 10px div.style.width = 10px <style> .one { width: 10px height: 10px padding: 10px } </style> div.classList.add('one')//一次重排 + 一次重绘 ```
+ 让要操作的元素进行”离线处理”,处理完后一起更新 (渲染树里面不包含隐藏的元素)
+ 使用 DocumentFragment对象 进行缓存操作,引发一次回流和重绘
+ 使用display:none技术,只引发两次回流和重绘;
+ clone一个DOM结点到内存里,然后想怎么改就怎么改,改完后,和在线的那个的交换一下
``` div.style.display = 'none'//1次重排+ 1次重绘 div.style.padding = 10px div.style.height = 10px div.style.width = 10px div.style.display = 'block'//1次重排+1次重绘 ```
+ 将需要多次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素,或者使用c3动画, 也不会影响其他盒子 那么修改他们的 CSS 是会大大减小 reflow .
<html> <style> * { padding: 0; margin: 0; box-sizing: border-box; } .box { width: 600px; height: 200px; border: 1px solid red; margin: 200px auto; padding: 20px; } ul { list-style: none; width: 100%; height: 100%; display: flex; justify-content: space-around } li { width: 30%; height: 100%; } .one { background-color: blue; transition: transform 1s; } .one:hover { transform: scale(2) } </style> <body> <div class="box"> <ul> <li></li> <li class="one"></li> <li></li> </ul> </div> </body> </html>
![](C:\Users\Lenovo\Desktop\面试题讲解\1\whl-前端性能优化\新建文件夹\scale.png)
4)不要把DOM结点的属性值放在一个循环里当成循环里的变量。不然这会导致大量地读写这个结点的属性。
5)尽可能的修改层级比较低的DOM。当然,改变层级比较底的DOM有可能会造成大面积的reflow,但是也可能影响范围很小。
6)千万不要使用table布局。因为可能很小的一个小改动会造成整个table的重新布局。
+ **完成功能是前提,在完成功能的情况下想着优化代码**
```javascript var pNode,fragment = document.createDocumentFragment(); //动态创建20个p标签,先用DocumentFragment 对象来缓存 for(var i=0; i<20; i++){ pNode = document.createElement('p'); pNode.innerHTML = i; fragment.appendChild(pNode); } document.body.appendChild(fragment); ```
**DocumentFragment ( 文档片段节点 ) :**
我们经常使用javascript来操作DOM元素,比如使用appendChild()方法。每次调用该方法时,浏览器都会重新渲染页面。如果大量的更新DOM节点,则会非常消耗性能,影响用户体验
javascript提供了一个文档片段DocumentFragment的机制。如果将文档中的节点添加到文档片段中,就会从文档树中移除该节点。把所有要构造的节点都放在文档片段中执行,这样可以不影响文档树,也就不会造成页面渲染。当节点都构造完成后,再将文档片段对象添加到页面中,这时所有的节点都会一次性渲染出来,这样就能减少浏览器负担,提高页面渲染速度
由于文档片段的优点在IE浏览器下并不明显,反而可能成为多此一举。所以,该类型的节点并不常用
https://coolshell.cn/articles/9666.html
https://www.cnblogs.com/11lang/p/6206541.html
https://www.cnblogs.com/xiaohuochai/p/5816048.html