主题
浏览器
1. 跨标签页通讯
不同标签页间的通讯,本质原理就是去运用⼀些可以 共享的中间介质, 因此比较常用的有以下方法:
- 通过父页面
window.open()
和子页面postMessage
- 异步下, 通过
window.open('about: blank')
和tab.location.href = '\*'
- 异步下, 通过
- 设置同域下共享的
localStorage
与监听window.onstorage
- 重复写入相同的值无法触发
- 会受到浏览器隐身模式等的限制
- 设置共享
cookie
与不断轮询脏检查(setInterval
) - 借助服务端或者中间层实现
2. 浏览器架构
- 用户界面
- 主进程
- 内核
- 渲染引擎
JS
引擎- 执行栈
- 事件触发线程
- 消息队列
- 微任务
- 宏任务
- 消息队列
- 网络异步线程
- 定时器线程
3. 浏览器下事件循环(Event Loop)
事件循环是指:执⾏⼀个宏任务,然后执⾏清空微任务列表,循环再执⾏宏任务, 再清微任务列表
- 微任务 microtask(jobs):
promise
/ajax
/Object.observe
(该方法已废弃) - 宏任务 macrotask(task):
setTimout
/script
/IO
/UI Rendering
4. 从输入 url 到展示的过程
DNS
解析TCP
三次握手- 发送请求,分析
url
,设置请求报文(头, 主体) - 服务器返回请求的文件 (
html
) - 浏览器渲染
HTML parser
-->DOM Tree
- 标记化算法, 进⾏元素状态的标记
dom
树构建
CSS parser
-->Style Tree
- 解析
css
代码,生成样式树
- 解析
attachment
-->Render Tree
- 结合
dom
树 与style
树,生成渲染树
- 结合
layout
: 布局GPU painting
: 像素绘制页面
5. 重绘与回流
当元素的样式发生变化时, 浏览器需要触发更新, 重新绘制元素 。这个过程中,有两种类型的操作, 即重绘与回流。
重绘(repaint
): 当元素样式的改变不影响布局时, 浏览器将使用重绘对元素进⾏更新,此时由于只需要 UI
层面的重新像素绘制, 因此 损耗较少
回流(reflow
): 当元素的尺⼨ 、结构或触发某些属性时, 浏览器会重新渲染页面,称为回流 。此时, 浏览器需要重新经过计算,计算后还需要重新页面布局, 因此是较重的操作 。会触发回流的操作:
页面初次渲染
浏览器窗口大⼩改变
元素尺⼨ 、位置 、内容发生改变
元素字体大⼩变化
添加或者删除可见的
dom
元素激活
CSS
伪类 (例如::hover
)查询某些属性或调用某些方法
clientWidth
、clientHeight
、clientTop
、clientLeft
offsetWidth
、offsetHeight
、offsetTop
、offsetLeft
scrollWidth
、scrollHeight
、scrollTop
、scrollLeft
getComputedStyle()
getBoundingClientRect()
scrollTo()
回流必定触发重绘, 重绘不⼀定触发回流 。重绘的开销较⼩, 回流的代价较高。
最佳实践:
css
- 避免使用
table
布局 - 将动画效果应用到
position
属性为absolute
或fixed
的元素上
javascript
- 避免频繁操作样式, 可汇总后统⼀ ⼀次修改
- 尽量使用
class
进行样式修改 - 减少
dom
的增删次数, 可使用 字符串 或者documentFragment
⼀次性插⼊ - 极限优化时,修改样式可将其
display: none
后修改 - 避免多次触发上面提到的那些会触发回流的方法, 可以的话尽量用 变量存住
6. 存储
我们经常需要对业务中的⼀些数据进行存储, 通常可以分为 短暂性存储 和 持久性储存。
- 短暂性的时候, 我们只需要将数据存在内存中, 只在运行时可用
- 持久性存储, 可以分为 浏览器端 与 服务器端
- 浏览器:
cookie
: 通常用于存储用户身份,登录状态等http
中自动携带, 体积上限为4K
, 可自行设置过期时间
localStorage
/sessionStorage
: 长久储存/窗口关闭删除, 体积限制为4~5M
indexDB
- 服务器:
- 分布式缓存
redis
- 数据库
- 分布式缓存
- 浏览器:
7. Web Worker
现代浏览器为 JavaScript
创造的 多线程环境 。可以新建并将部分任务分配到 worker
线程并行运行,两个线程可 独立运行, 互不⼲扰, 可通过自带的消息机制 相互通信。
- 基本用法:
js
// 创建 worker
const worker = new Worker("work.js");
// 向主进程推送消息
worker.postMessage("Hello World");
// 监听主进程来的消息
worker.onmessage = function (event) {
console.log("Received message " + event.data);
};
限制:
- 同源限制
- 无法使用
document
/window
/alert
/confirm
- 无法加载本地资源
8. 内存泄露
- 意外的全局变量: 无法被回收
- 定时器: 未被正确关闭, 导致所引用的外部变量无法被释放
- 事件监听: 没有正确销毁 (低版本浏览器可能出现)
- 闭包: 会导致父级中的变量无法被释放
dom
引用:dom
元素被删除时, 内存中的引用未被正确清空
可用 chrome 中的 timeline 进行内存标记, 可视化查看内存的变化情况,找出异常点。