页面加载执行
浏览器事件循环
事件循环是指: 执行一个宏任务,然后执行清空微任务列表,循环再执行宏任务,再清微任务列表
- 微任务
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 多进程架构图
- 浏览器主进程:负责界面显示、用户交互、子进程管理,同时提供存储等功能
- 渲染进程:负责将
HTMLCSS和JavaScript转换为用户可以与之交互的网页- 排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中
- 默认情况下 Chrome 会为每个 Tab 标签创建一个渲染进程
- 出于安全考虑渲染进程都是运行在沙箱模式下
- GPU 进程:负责网页、Chrome 的 UI 界面的绘制
- 网络进程:负责页面的网络资源加载(之前是作为一个模块运行在浏览器进程)
- 插件进程:负责插件的运行(因为插件易崩溃所以需要通过插件进程来隔离,以保证插件崩溃不会对浏览器和页面造成影响)
Cao Note