UzumakiItachi
首页
  • JavaSript
  • Vue

    • Vue2
    • Vue3
  • React

    • React_18
  • WebPack
  • 浏览器相关
  • 工程化相关
  • 工作中遇到的问题以及解决方案
  • Git
  • 面试
  • 学习
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
  • 个人产出
  • 实用工具
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

UzumakiItachi

起风了,唯有努力生存。
首页
  • JavaSript
  • Vue

    • Vue2
    • Vue3
  • React

    • React_18
  • WebPack
  • 浏览器相关
  • 工程化相关
  • 工作中遇到的问题以及解决方案
  • Git
  • 面试
  • 学习
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
  • 个人产出
  • 实用工具
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • WebPack

  • 浏览器相关

    • 浏览器是如何渲染页面的
      • 计算机网络篇
      • Puppeteer-浏览器自动化操作实践
    • 工程化相关

    • 工作中遇到的问题以及解决方案

    • Git

    • Vite

    • 一些小工具

    • 算法

    • 服务器

    • HTTP

    • 技术
    • 浏览器相关
    hanhanbuku
    2023-02-28
    目录

    浏览器是如何渲染页面的

    # 一、什么是进程,什么是线程

    进程是cpu资源分配最小单位,线程是进程最小调度单位。 在我们的操作系统中,有许多许多的进程,操作系统每干一件事儿就会交给一个进程去做,比如打开音乐就是一个进程。而每个进程里又包含了许多的线程,例如打开了音乐播放器,放一首歌,就是一个新的线程,切换下一首,可能又是另外一个线程。总之他俩之间的关系就是一个进程包含多个线程。

    # 二、浏览器是多进程还是单进程

    浏览器一般都是多进程,每开一个tab页,就是一个新的进程。这也就是为什么一个tab页崩溃了不会影响到整个浏览器崩溃。他主要分为以下几个进程:

    • 1、主进程:只有一个,负责调度主控整个浏览器
    • 2、插件进程:每个插件都有一个进程,只在插件被调用的时候创建
    • 3、GPU进程:只有一个,负责3d绘制
    • 4、渲染进程:每个tab页一个,负责网页的渲染,脚本的执行和事件的处理等。这个进程也是前端最需要了解的进程。

    # 三、渲染进程

    要了解渲染进程,那就得从他有几个线程开始。 渲染进程主要有以下几个线程:

    • 1、GUI线程:负责页面的构建和渲染,当页面需要被绘制的时候就会启动这个线程,要注意的是,他和js引擎线程是互斥的,不能并行执行
    • 2、js引擎线程:负责解析和执行js脚本,因为他和GUI线程的互斥性,所以js代码是会导致页面渲染不连贯的,也就是常说的阻塞页面渲染
    • 3、事件触发线程:归属于浏览器,而不是js引擎,他主要就是控制事件循环,将一系列的任务加入一个队列等js引擎空闲下来后去执行
    • 4、定时器线程:管理定时器的计时,等时间到了就把事件推入任务队列,等待js引擎执行
    • 5、异步http请求线程:每发送一个请求就会开启一个新的线程,等待响应后把回调函数推入任务队列,等js引擎执行。

    以上几个就是渲染进程的主要线程。其实通过上述的了解我们就可以大概明白了,js的代码执行顺序为什么是那样,可以简单的认为只有同步代码是第一时间就在js引擎线程里去执行的,其他的例如宏任务,微任务,等都是由别的线程管理,当满足条件后才将代码交给js引擎去执行。

    # 那接下来就聊一聊老生常谈的浏览器渲染流程:

    • 1、首先浏览器会去解析html文件,构建DOM树,然后解析css,生成css树。
    • 2、等DOM树和css树都构建好了之后,会将两者合并,生成最终的render树
    • 3、接着去布局render树,计算位置 尺寸。然后再绘制render树。
    • 4、最后将各层的信息发送给GPU,GPU合成渲染层最终呈现出页面。

    在浏览器的渲染过程中还有两个钩子会被触发,他们就是load事件和DOMcontentloaded事件 那二者有什么区别呢? DOMcontentloaded事件会在dom树构建完成之后(不包括样式表,图片等)就执行,而load事件会在渲染完毕后被执行。所有前者是先于后者执行的

    # 下一个问题,css到底会不会阻塞dom渲染呢?

    首先看一下浏览器的渲染流程图 image.png 可以看到,DOM的加载和css的加载时并行执行的,所以理论上来说css是不会阻塞dom渲染的,但是最终的渲染是需要dom树和css树共同生成一个render树,就算dom树构建好了,但是css树没构建好,那也没办法渲染页面。所以css的加载不会阻塞但是css的解析又是会阻塞页面渲染的。

    # 浏览器的回流和重绘:

    回流指的时当元素发生大小位置上的变化,引起整个结构也变化了这个时候浏览器就会触发回流,重绘则是元素的颜色背景色等不影响布局变化的地方发生了变化,就会触发重绘。

    回流一定触发重绘,但重绘不一定触发回流。

    那平时应该怎么尽量避免这两个问题呢?

    • 1、尽量把操作dom的过程统一执行,以减少回流重绘的次数
    • 2、把需要多次操作的dom先display:none,操作完在展示出来
    • 3、使带有动画的dom脱离文档流
    • 4、尽量使用偏移量transform去代替left top等位移属性
    • 5、使用一些能触发GPU加速的样式代替原来的写法

    # script标签

    说完了html,css,当然还要聊一聊最重要的js环节了。 看完上面这些内容我们大概已经知道了,js为什么会阻塞页面渲染,那有没有什么办法避免呢。 首先了解以下script标签上的async和defer两个属性

    1. async:异步加载js文件,他会在异步加载完成之后立即执行js文件,所以还是会阻塞后续的dom渲染。
    2. defer:也是异步加载,他和async不同的是,他加载完成之后不会立即执行,而是会等到DOM树构建完毕DOMcontentloaded执行之前再去执行脚本。

    并且因为async加载就执行的特点,导致async加载js文件时无序的,这可能会出现当某个js需要依赖上一个js的时候,上一个js还没加载过来的问题。而defer则时有序的执行js,会按照你书写的script标签顺序去执行。

    好啦,了解完这么多,应该就明白了为什么通常js要放在页面的底部引入。只要记住GUI渲染线程和js线程是互斥的就能回答大部分阻塞的问题了

    编辑 (opens new window)
    上次更新: 2023/02/28, 22:24:12
    实现一个自动收集路由信息的webpack插件
    计算机网络篇

    ← 实现一个自动收集路由信息的webpack插件 计算机网络篇→

    最近更新
    01
    小程序实现全局状态管理
    07-09
    02
    前端检测更新,自动刷新网页
    06-09
    03
    swiper渲染大量数据的优化方案
    06-06
    更多文章>
    Theme by Vdoing | Copyright © 2023-2025 UzumakiItachi | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式