事件循环
简单总结,事件循环重复以下步骤:
- 检查任务队列(先进先出)是否为空,不为空执行顶端任务
- 检查微任务队列是否为空,不为空执行顶端任务,重复此步骤直到清空微任务队列
- 浏览器根据渲染时机(硬件刷新率、页面性能、页面在后台等)判断是否需要更新渲染,补充一点:
requestAnimationFrame在 Layout 之前执行。
参考
输入 URL 到页面展现的全过程
这个问题应该思考过程中可做的优化。
导航(Navigation)
首先浏览器进程中 UI 线程判断输入是否为 URL,如果为 URL,由浏览器进程中网络线程处理:
- DNS 解析,优化:避免资源跨域
- TCP 握手
- TLS 协商(仅 HTTPS)
响应(Navigation)
当我们的设备与服务器建立好了连接:
- 发送 GET 请求
- 处理响应,如果响应头中的
content-type字段值为text/html,那么浏览器就会进行文档渲染
我们应该确保 HTML 文件小于 14KB,参考 TCP 慢启动。
解析(Parsing)
- 渲染进程中主线程处理 HTML 标记并且构建 DOM 树,注意,遇到没有
async或defer属性的<script>标签,会阻止渲染。在构建 DOM 树占据主线程的过程中,浏览器提供了预加载扫描器用于请求高优先级资源,这样不用等到解析器发现外部资源的引用时才请求它 - 渲染进程中主线程处理 CSS 并构建 CSSOM 树。在构建 CSSOM 树同样会下载文件(预加载扫描器)。
渲染(Render)
- 样式(Style):渲染进程中主线程将 DOM 和 CSSOM 组合成渲染树,从 DOM 树的根节点开始,历遍所有可见的节点
- 布局(Layout):渲染进程中主线程从渲染树的根节点开始,确定渲染树中所有节点的宽度、高度和位置构成布局树
- 绘制(Paint):渲染进程中主线程将布局树转换成供合成器使用的高效渲染格式,因为布局树中的元素可以分层到不同图层,所以需要构建分层树以确保它们以正确的顺序绘制到屏幕上并且内容被正确呈现。优化:将内容提升到 GPU 上的层(而不是 CPU 上的主线程)可以提高绘制和重绘性能,常设置
will-change。 - 合成(Compositing):渲染进程中合成线程按块(可以优先处理视图中的内容)发送到渲染进程中光栅线程,光栅线程光栅化(将信息转换为屏幕上的像素称为光栅化)每个图块并将它们存储在 GPU 内存中。在此过程中,光栅线程根据图块信息创建合成帧交给浏览器进程,最终浏览器进程交给 GPU 显示
参考
重排重绘
浏览器更新渲染时,如果页面没有变化,会跳过渲染(Render)阶段的布局(Layout)和绘制(Paint)以优化性能。
重排即布局改变,所以必须经过布局和绘制,应该尽量避免。
重绘即样式改变但不影响布局,跳过布局。
引起重排常见操作:
- 样式属性如
height、float、position等 - 添加/删除 DOM 元素
- 查询某些属性或调用某些计算方法:
offsetWidth、getComputedStyle等
引起重绘常见操作:样式属性如 color、opacity 等。
参考
垃圾回收机制
常见的 GC(Garbage Collection) 算法有引用计数法和标记清除法等。
引用计数
将资源(比如对象)的被引用次数保存起来,当被引用次数为零时释放。该方法的局限性:当出现循环引用时,互相引用的对象不会被回收。
标记清除
从一组已知的对象指针(称为根集,包括执行堆栈和全局对象等)中,进行递归标记可访问对象,最后清除不可访问的对象。
参考
内存泄露
- 全局变量(手动回收)
- 未销毁定时器、闭包
- 监听事件但是没销毁事件
参考
本地存储
浏览器本地存储主要分为 cookie、Web Storage 以及 IndexedDB。其中 Web Storage 又分为 sessionStorage 和 localStorage。
所有存储方式都受同源策略限制。
备注:一个会话是否结束由浏览器决定,某些浏览器哪怕关闭后,重新打开仍然可以恢复最近会话。
| cookie | sessionStorage | localStorage | IndexedDB | |
|---|---|---|---|---|
| 生命周期 | 可设置失效时间,默认为会话结束时清除 | 只能被当前标签页访问,会话结束时清除 | 持久存储 | 持久存储 |
| 存储空间最大值 | 4KB | 一般为 5MB | 一般为 5MB | 取决于用户设备容量和浏览器限额设置 |