进程相当于一个工厂,线程相当于工厂里的工人 工厂之间相互独立,工厂内有一个或多个工人 工人之间共享空间,多个工人协作完成任务
- 进程之间相互独立,拥有系统分配的独立的内存
- 一个进程由一个或者多个线程组成,多个线程在进程中协作完成任务
- 同一进程下的各个线程之间共享程序的内存空间(包括代码段、线程集、堆)
- 浏览器是多进程的
- 每打开一个tab页,就相当于创建了一个独立的浏览器进程
- Browser进程(浏览器主进程):
- 负责界面显示,用户交互,如前进、后退等等
- 负责页面管理、创建和销毁其他进程
- 将render进程得到的bitmap绘制到用户界面上
- 网络资源的管理、下载等
- 第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时,才创建
- GPU进程:用于3D绘制
- 浏览器渲染进程(浏览器内核)(render进程):默认每个tab一个,互不影响。页面渲染、脚本执行、事件处理等
- GUI渲染线程 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行 注意,GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
- JS引擎线程 负责解析js脚本、运行代码。 一直在等待任务队列中任务的到来,一个render进程里,只有一个js线程
- 事件触发线程 用来控制事件循环。 当js引擎执行代码块如settimeout时,会将对应任务添加到事件线程中 当对应的事件符合触发条件时,该线程会把事件添加到待处理队列的队尾 js是单线程的,待处理队列中的事件要排队等待js线程处理(js线程空闲时执行)
- 定时触发器线程 setInterval与setTimeout所在线程 计时完毕后,添加到事件队列中,等待js引擎空闲的时候执行
- 异步http请求线程 xmlhttprequest连接后是通过浏览器新开一个线程请求 如果有回调函数,异步线程会产生状态变更事件,将回调放到事件队列中
js可以修改dom,如果修改元素属性的同时渲染界面,渲染线程前后获得的元素数据可能不一致 js引擎执行时,GUI线程会被挂起 GUI更新会被保存在一个队列中,等js线程空闲时,立即执行
假设JS引擎正在进行巨量的计算,此时就算GUI有更新,也会被保存到队列中,等待JS引擎空闲后执行
浏览器输入url,浏览器主进程接管,开一个下载线程,等待响应,获取内容,把内容交给浏览器渲染进程。
- 解析html,构建dom树
- 解析css构建render树(将CSS代码解析成树形的数据结构CSSOM,然后结合DOM合并成render树)
- 布局render树(Layout/reflow),负责各元素尺寸、位置的计算
- 绘制render树(paint),绘制页面像素信息
- 浏览器会将各层的信息发送给GPU,GPU会将各层合成(composite),显示在屏幕上。 css加载不会阻塞DOM树解析,但会阻塞render树渲染(因为render树需要css信息)
JS分为同步任务和异步任务 同步任务都在主线程上执行,形成一个执行栈 事件触发线程管理着一个任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件 同步任务执行完毕,js引擎空闲,系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行
setInterval每隔一段时间推入一个事件,事件的实际执行时间不一定就准确,还有可能是这个事件还没执行完毕,下一个事件就来了。 会导致定时器代码连续运行好几次,而之间没有间隔。 setTimeout在前一个定时器执行完前,不会向队列插入新的定时器,保证定时器间隔。
function myInterval(fn, ms) {
const ref = {};
const exec = () => {
return setTimeout(() => {
fn.apply(null);
const timer = exec();
ref.current = timer;
}, ms);
};
ref.current = exec();
list.add(ref);
return ref;
}
function myClearInterval(ref) {
clearTimeout(ref.current);
list.delete(ref);
}
用setTimeout模拟setInterval
- 执行一个宏任务(栈中没有就从事件队列中获取)
- 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
- 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
- 当前宏任务执行完毕,开始检查浏览器渲染
- 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)
当Render Tree中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流
- 页面首次渲染
- 浏览器窗口大小发生改变
- 元素尺寸或位置发生改变
- 元素内容变化(文字数量或图片大小等等)
- 元素字体大小变化
- 添加或者删除可见的DOM元素
- 激活CSS伪类(例如::hover)
- 查询某些属性或调用某些方法
当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘
避免方法:
- 将动画效果应用到position属性为absolute或fixed的元素上(让其脱离文档流)
- 预先定义 class,然后修改 dom class 而不是直接操控 css
- 不要使用table布局
- 预先设定img的大小,以免载入图片之后大小变化导致 Reflow
- 先为元素设置display: none,操作结束后再把它显示出来
- 不要频繁操作样式
懒加载 虚拟列表 小图用base64格式,多个图片整合到一起(雪碧图) cdn 服务端开启文件压缩功能 defer:加了defer属性,script标签可以放到任意地方,脚本会被延迟到整个页面都解析完毕后再运行。浏览器立即下载,但延迟执行 async:script下载会和页面解析并行执行,script下载完后,立即执行,多个async script脚本谁先加载完谁先执行