Skip to content

页面加载执行 ​

浏览器事件循环 ​

事件循环是指: 执行一个宏任务,然后执行清空微任务列表,循环再执行宏任务,再清微任务列表

  • 微任务 microtask(jobs): promise / process.nextTick / MutationObserver
  • 宏任务 macrotask(task): setTimout / setInterval / setImmediate / script / IO / UI Rendering

宏任务中包括了 script ,浏览器会先执行一个宏任务,接下来有异步代码的话就先执行微任务

怎么判断页面是否加载完成 ​

  • Load 事件触发代表页面中的 DOM,CSS,JS,图片已经全部加载完毕。
  • DOMContentLoaded 事件触发代表初始的 HTML 被完全加载和解析,不需要等待 CSS,JS,图片加载

css 加载会造成阻塞吗 ? ​

  • DOM 和 CSSOM 通常是并行构建的,所以 CSS 加载不会阻塞 DOM 的解析。
  • 然而,由于 Render Tree 是依赖于 DOM Tree 和 CSSOM Tree 的,
  • 所以他必须等待到 CSSOM Tree 构建完成,也就是 CSS 资源加载完成(或者 CSS 资源加载失败)后,才能开始渲染。
  • 因此,CSS 加载会阻塞 Dom 的渲染。
  • 由于 JavaScript 是可操纵 DOM 和 css 样式的,如果在修改这些元素属性同时渲染界面(即 JavaScript 线程和 UI 线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。
  • 因此为了防止渲染出现不可预期的结果,浏览器设置 GUI 渲染线程与 JavaScript 引擎为互斥的关系。因此,样式表会在后面的 js 执行前先加载执行完毕,所以 css 会阻塞后面 js 的执行

为什么 JS 阻塞页面加载 ? ​

  • 由于 JavaScript 是可操纵 DOM 的,如果在修改这些元素属性同时渲染界面(即 JavaScript 线程和 UI 线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了
  • 因此为了防止渲染出现不可预期的结果,浏览器设置 GUI 渲染线程与 JavaScript 引擎为互斥的关系
  • 当 JavaScript 引擎执行时 GUI 线程会被挂起,GUI 更新会被保存在一个队列中等到引擎线程空闲时立即被执行
  • 从上面我们可以推理出,由于 GUI 渲染线程与 JavaScript 执行线程是互斥的关系,
  • 当浏览器在执行 JavaScript 程序的时候,GUI 渲染线程会被保存在一个队列中,直到 JS 程序执行完成,才会接着执行
  • 因此如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉

DOMContentLoaded 与 load 的区别 ? ​

  • 当 DOMContentLoaded 事件触发时,仅当 DOM 解析完成后,不包括样式表,图片。我们前面提到 CSS 加载会阻塞 Dom 的渲染和后面 js 的执行,js 会阻塞 Dom 解析,所以我们可以得到结论:
  • 当文档中没有脚本时,浏览器解析完文档便能触发 DOMContentLoaded 事件。如果文档中包含脚本,则脚本会阻塞文档的解析,而脚本需要等 CSSOM 构建完成才能执行。在任何情况下,DOMContentLoaded 的触发不需要等待图片等其他资源加载完成
  • 当 onload 事件触发时,页面上所有的 DOM,样式表,脚本,图片等资源已经加载完毕
  • DOMContentLoaded -> load

什么是 CRP,即关键渲染路径? 如何优化 ​

关键渲染路径是浏览器将 HTML CSS JavaScript 转换为在屏幕上呈现的像素内容所经历的一系列步骤。也就是我们上面说的浏览器渲染流程。

为尽快完成首次渲染,我们需要最大限度减小以下三种可变因素:

  • 关键资源的数量: 可能阻止网页首次渲染的资源。
  • 关键路径长度: 获取所有关键资源所需的往返次数或总时间。
  • 关键字节: 实现网页首次渲染所需的总字节数,等同于所有关键资源传送文件大小的总和

1. 优化 DOM

  • 删除不必要的代码和注释包括空格,尽量做到最小化文件。
  • 可以利用 GZIP 压缩文件。
  • 结合 HTTP 缓存文件

2. 优化 CSSOM

缩小、压缩以及缓存同样重要,对于 CSSOM 我们前面重点提过了它会阻止页面呈现,因此我们可以从这方面考虑去优化。

  • 减少关键 CSS 元素数量
  • 当我们声明样式表时,请密切关注媒体查询的类型,它们极大地影响了 CRP 的性能

3. 优化 JavaScript

当浏览器遇到 script 标记时,会阻止解析器继续操作,直到 CSSOM 构建完毕,JavaScript 才会运行并继续完成 DOM 构建过程。

  • async: 当我们在 script 标记添加 async 属性以后,浏览器遇到这个 script 标记时会继续解析 DOM,同时脚本也不会被 CSSOM 阻止,即不会阻止 CRP。
  • defer: 与 async 的区别在于,脚本需要等到文档解析后( DOMContentLoaded 事件前)执行,而 async 允许脚本在文档解析时位于后台运行(两者下载的过程不会阻塞 DOM,但执行会)
  • 当我们的脚本不会修改 DOM 或 CSSOM 时,推荐使用 async
  • 预加载 —— preload & prefetch
  • DNS 预解析 —— dns-prefetch

总结

  • 分析并用 关键资源数 关键字节数 关键路径长度 来描述我们的 CRP
  • 最小化关键资源数:消除它们(内联)、推迟它们的下载(defer)或者使它们异步解析(async)等
  • 优化关键字节数(缩小、压缩)来减少下载时间 优化加载剩余关键资源的顺序:
  • 让关键资源(CSS)尽早下载以减少 CRP 长度

线程和进程 ​

进程(process)和线程(thread)是操作系统的基本概念

进程是 CPU 资源分配的最小单位(是能拥有资源和独立运行的最小单位)
线程是 CPU 调度的最小单位(是建立在进程基础上的一次程序运行单位)

一个进程就是一个程序的运行实例:启动一个程序时,操作系统会为该程序创建一块内存,用来存放代码、运行中的数据和一个执行任务的主线程,我们把这样的一个运行环境叫进程

线程是进程内的一个执行单元:线程是不能单独存在的,是依附于进程并由进程来启动和管理的

进程和线程的关系特点

  • 进程是拥有资源的基本单位;线程是调度和分配的基本单位(是进程内的一个执行单元,也是进程内的可调度实体)
  • 进程之间相互隔离,互不干扰
  • 一个进程中可以并发执行多个线程
  • 一个线程只能隶属于一个进程,而一个进程是可以拥有多个线程的(但至少有一个主线程)
  • 同一进程的所有线程共享该进程的所有数据
  • 进程中的任意一线程执行出错,都会导致整个进程的崩溃
  • 当一个进程关闭之后,操作系统会回收进程所占用的内存

Chrome 打开一个页面会有几个进程? ​

最新的 Chrome 多进程架构图

  • 浏览器主进程:负责界面显示、用户交互、子进程管理,同时提供存储等功能
  • 渲染进程:负责将 HTML CSS 和 JavaScript 转换为用户可以与之交互的网页
    • 排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中
    • 默认情况下 Chrome 会为每个 Tab 标签创建一个渲染进程
    • 出于安全考虑渲染进程都是运行在沙箱模式下
  • GPU 进程:负责网页、Chrome 的 UI 界面的绘制
  • 网络进程:负责页面的网络资源加载(之前是作为一个模块运行在浏览器进程)
  • 插件进程:负责插件的运行(因为插件易崩溃所以需要通过插件进程来隔离,以保证插件崩溃不会对浏览器和页面造成影响)

Chrome 架构:仅仅打开了 1 个页面,为什么有 4 个进程?—— 浏览器工作原理与实践