Lambda博客
  • 自我介绍
  • SSR
    • 浅谈SSR
  • React
    • 页面路由——React-Router
    • 管好你的状态——React-Redux
    • 一个哪行——React详解
    • 左膀右臂一个不少——React初探
  • 问题记录
    • js-xlsx实现纯前端导出excel表格
    • 前端开发需要了解的东西
    • 打造高性能网站
  • JavaScript学习笔记
    • 语法和API
    • js-垃圾回收
    • 收集的JS使用技巧
    • 执行机制
    • 原型和原型链
    • 执行上下文
    • 事件循环
    • JavaScript手写代码
    • 43道JS面试题
    • 悄悄的带走了你的东西——闭包详解
    • 你是个富二代吗?——JavaScript作用域链
    • 捉摸不透的女生——JavaScript类型转换
    • 这是你的女神!——JavaScript
  • 网络学习笔记
    • 强不强——HTTP-协商缓存VS强缓存
    • 网络缓存
    • 我要飞的更高——计算机网络
    • 快点!再快点!——CDN
    • 喜怒哀乐多状态——HTTP状态码
    • 你会爱上我的(ಥ_ಥ) ——TCP详解
    • 隔壁老王想篡位?门都没有——同源策略
  • 软技能
  • 安全专题
    • 保护好自己——网站安全,预防
  • 浏览器兼容性
    • 我是个好人——浏览器兼容性
  • 多图片加载——懒加载详解
  • 数据结构
  • Node
    • Node初探
  • Typescript
    • JS Plus 真香——初探 TypeScript
  • 设计模式
    • 不要花里胡哨,要一套一套——设计模式(1)
    • 学会“套路”——设计模式(2)
  • Vue
    • 实操实干——vue实例记录
    • 停下来,问问自己——Vue-刨根问底
    • 你有喘息的机会吗?——Vue,逐步了解
    • 你累吗?来来来,安利框架——Vue-初次见面
  • 小程序
    • 今天天气怎么样——记一次小程序开发
  • Webpack
    • 蛋糕分割整合工具——Webpack-前端工程化
  • 你一块,我一块——Web-modules 前端模块化
  • HTML5
    • 你会画小猪佩奇吗?—— canvas
    • 画个矢量图——HTML5-SVG
    • 听说你爱闹腾——HTML5-多媒体
    • 动画神器——HTML5-requestAnimationFrame
    • 留下痕迹——HTML5-客户端存储
    • 你知道指北针吗?——HTML5-DeviceMotionEvent
    • 你在哪啊?我在这啊——HTML5-Geolocation
    • 你到这,你到那——HTML5-拖拽
    • 你从这,到那——HTML5-拖拽上传文件
    • 开启前端之路——HTML 标签
  • 瀑布流(无限滚动)
  • 我是怎么来的?——浏览器渲染原理
  • Css
    • 由大变小,你行吗 —— 移动端
    • 动起来,这样比较炫—— CSS3 动画
    • 请不要拐弯抹角 —— CSS3 选择器
    • 想炫吗?—— CSS3 属性
    • 最后的最后——CSS自问自答
    • 万事开头难?——深入学习前端常见布局
    • 一入前端深似海——BFC剖析
    • 还有哪些好玩的东西——CSS提升
    • 那些好看的页面是怎么形成的——CSS 初识
  • 拿个小本本记下——Cookie
由 GitBook 提供支持
在本页
  • 引入
  • 浏览器渲染页面的过程
  • 重绘重排
在GitHub上编辑

我是怎么来的?——浏览器渲染原理

引入

提到页面渲染,最重要的就是关键渲染路径

通常我们在编写页面时,只需要编写 HTML、Css、JavaScript 屏幕上就会显示出漂亮的页面,但浏览器是如何使用我们的代码在屏幕上渲染像素的呢? 浏览器将 HTML、Css、JavaScript 转换为屏幕上所呈现的实际像素,这期间所经历的一系列步骤,叫做关键渲染路径(Critical Rendering Path)

浏览器渲染页面的过程

从耗时的角度,浏览器请求,加载,渲染一个页面,时间花在下面五项

  • DNS 查询

  • TCP 连接

  • HTTP 请求及响应

  • 服务器响应

  • 客户端渲染

本文讨论第五个部分,即浏览器对内容的渲染

  1. 处理 HTML 生成 DOM 树

  2. 处理 Css 生成 CssOM 树

  3. 将两树合并成 render 树

  4. 对 render 树进行布局计算

  5. 将 render 树中的每个节点绘制到屏幕上

如果 DOM 或 CssDOM 被修改,以上过程需要重复执行,这样才能计算出那些像素在屏幕上进行重新渲染。

细化分析

  1. 浏览器把获取到的 HTML 代码解析成一个 DOM 树,HTML 中的每个 tag 都是 DOM 树中的 一个节点,根节点就是我们常用的 document 对象,当然这里包含使用 js 动态创建的 DOM 节点

  2. 浏览器把所有样式(主要包括 Css 和浏览器的默认样式设置)解析成样式结构体,在解析的过程中会去掉浏览器不能识别的样式,生成 CssDOM 树

  3. DOM tree 和 CssDOM tree 合并成 render tree, render tree 中每个 node 都有自己的 style,而且 render tree 不包含隐藏的节点(比如display: none的节点,还有无样式的<head>节点),因为这些节点不会用于呈现,而且不会影响呈现的。注意:visibility: hidden隐藏元素还是会包含到 render tree 中,因为visibility: hidden会影响布局(layout),会占有空间

  4. render tree 构建完毕之后根据样式计算布局,布局阶段的输出结果都称为”盒模型”(box-model)。盒模型精准表达了窗口每个元素的位置和大小,而且所有的相对度量单位都被转换成了屏幕上的绝对像素位置(根据 Css2 的标准,render tree 中的每个节点都称为 box),box 所有属性:width,height,margin,padding,left,border…

  5. 将这些信息渲染为屏幕上每个真实的像素点,这个阶段称为”绘制”,或者”栅格化”。

渲染阻塞

现代的浏览器总是并行加载资源。

当 HTML 解析器被脚本阻塞时,解析器虽然会停止构建 DOM,但仍会识别该脚本后面的资源,并进行预加载。

  1. 默认情况下,Css 被视为阻塞渲染的资源,这意味着浏览器将不会渲染任何已处理的内容,直到 CssDOM 构建完毕

    • DOM 树和 CssOM 解析是互不影响的,因此,Css 不会阻塞 DOM 的解析

    • 当 Css 还有构建完成时,不会渲染到页面的,因此Css会阻塞DOM的渲染

    • 这可能是浏览器的一种机制,如果在加载Css的时候,可能会修改下面的DOM节点样式,如果Css加载不阻塞DOM的话,那么当Css加载完成之后,DOM树又得重绘或重排了,这就造成不必要的损耗。

  2. JavaScript 不仅可以读取和修改 DOM 属性,还可以读取和修改 CssDOM 属性

    • 故在JS脚本执行前,浏览器必须保证到Css文件完全加载并解析完成。这就导致了JS执行的延迟,也因此导致HTML解析和渲染延迟

      存在阻塞的 Css 资源时,浏览器会延迟 JavaScript 的执行和 DOM 构建

总结

  • Css 加载不会阻塞 DOM 树的解析

  • Css 加载会阻塞 DOM 树的解析

  • Css 加载会阻塞后面 JS 语句的执行

  1. 当浏览器遇到一个 script 标记时,DOM 构建将暂停,直至脚本完成执行

  2. JavaScript 可以查询和修改 DOM 与 CssDOM

  3. CssDOM 构建时,JavaScript 执行将暂停,直至 CssDOM 就绪

script 标签的位置很重要,实际使用时应将资源放在页面底部

defer 和 async

注意:async 与 defer 属性对于 inline-script 都是无效的

defer

defer 属性表示延迟执行引入的 JavaScript,在这段代码加载时 HTML 并未停止解析,这两个过程是并行的。当整个 DOM 解析完毕且 defer-script 也加载完成后,执行 defer-script 里的 JavaScript 代码。

执行结果放在 HTML 标签解析完成之后

async

async 属于异步加载 JavaScript,在这段代码加载时 HTML 并未停止解析,这两个过程是并行的。当 async-script 加载完成后,会立即执行 async-script 里的 JavaScript 代码。

执行结果可能在 HTML 标签解析完成之前或之后

动态创建标签

当使用document.createElement('script')创建的 script 默认是异步的

console.log(document.createElement("script").async); //true

重绘重排

重排重绘会影响性能

  1. 我们计算它们在当前设备中的准确位置和尺寸。这正是布局阶段要做的工作,该阶段在英语中也被称为 “回流”(reflow),当 render tree 中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等给便需要重新构建。也会回流(其实我觉得叫重新布局简单明了些)。每个页面至少需要一次回流,就是在页面第一次加载的时候。

  2. **重绘(repaints)**当 render tree 中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局,比如background-color,则就称为重绘

触发重排的方法

以下这些属性和方法需要返回最新的布局信息,重新计算渲染树,就会造成回流,触发重排以返回正确的值。建议将他们合并到一起操作,可以减少回流的次数。这些属性包括:offsetTop、offsetLeft、 offsetWidth、offsetHeight;scrollTop、scrollLeft、scrollWidth、scrollHeight;clientTop、clientLeft、clientWidth、clientHeight;getComputedStyle() 、currentStyle()

提高网页性能,就是要降低”重排”和”重绘”的频率和成本,尽量少触发重新渲染。

DOM 变动和样式变动,都会触发重新渲染。但是,浏览器已经很智能了,会尽量把所有的变动集中在一起,排成一个队列,然后一次性执行,尽量避免多次重新渲染.

优化办法

理论上的优化办法

  1. DOM 的多个读操作,(或多个写操作),应该放在一起,不要两个读操作之间插入一个写操作

  2. 离线操作 DOM。如使用隐藏元素 document.createDocumentFragment(),cloneNode()

  3. 修改样式的时候添加类名,或一次性添加到 dom.style.cssText上等

上一页瀑布流(无限滚动)下一页Css

最后更新于3年前

mymf3T.png