前端需要掌握的基础 - 浏览器认知

介绍

(ctrl+shift+p) 浏览器刷新频率一般是 60FPS/s

  • 用户输入 url 地址 (关键字 会将关键根据默认的引擎生成地址) 会开始导航 浏览器进程里边做

  • 浏览器进程会准备一个渲染进程用于渲染页面

  • 网络进程加载资源, 最终将加载的资源交给渲染进程来处理

  • 渲染完毕显示

  • 网络七层模型 (物理层, 数据层) 网络层(ip) 传输层(tcp安全可靠 分段传输, UDP 丢包 一个包传输) (会话层, 表述层, 应用层) http

  • 先去查找缓存, 检测缓存是否过期, 直接返回缓存中内容

  • 看域名是否解析过, DNS协议 将域名解析成 ip 地址 (DNS 是基于 UDP) ip + 端口号 host

  • 请求是 https 需要ssl协商

  • ip地址来进行寻找地址, 排队等待 最多发送6个htt请求(客户端)

  • tcp 创建链接 用于传输(三次握手)

  • 利用 tcp 传输数据 (拆分成数据包,有序的) 特点:可靠、有序 服务器会按照顺序来接收(重排)

  • http 请求 (请求行 请求头 请求体)

  • 默认不会断开 keep-alive 为了下次传输数据时 可以复用上次的创建的链接

  • 服务器收到数据后解析 (响应行 响应头 响应体)

  • 服务器 返回301 302 会进行重定向操作 (重新走一遍) 304 会去查询浏览器缓存进行返回

浏览器接收资源后怎么处理

  • http 0.9 负责传输 html, 最早的时候没有请求头 和 响应头
  • http 1.0 提供了 http 的header 根据header的不同来处理不同的资源
  • http 1.1 默认开启了 keep-alive 链接复用 增加管线化(避免等待建立链接) (服务器处理多个请求(队头阻塞问题))
  • http 2.0 用同一个 tcp 链接来发送数据 一个域名一个tcp(多路复用) 头部压缩 服务器可以推送数据
  • http 3.0 解决了 tcp 的队头阻塞问题 加上QUIC协议并采用了 udpp

渲染流程

  • 解析html转化成 DOM 树 -> 解析css 也会解析成 styleSheets -> 然后解析成渲染树 -> 图层树 -> 绘制(paint) -> 合成线层(合成图层 com)
  • 样式是不会去阻塞解析的 渲染dom时,要等待样式加载完毕
  • 样式放在底部,可能会导致重绘, 当html渲染时,会先扫描 js 和css 渲染从上到下,边解析边渲染
  • js 会阻塞dom解析, 需要暂停DOM 解析去执行 js, js可能会操作样式,所以需要等待样式加载完成再继续加载, js阻塞html解析,阻塞渲染,js 要等上面的css加载完毕,保证页面js 可以操作样式

Perfomance 使用

  • TTFB
    • time to first byte(首字节时间)
    • 从请求到数据返回第一个字节所消耗的时间
  • TTI
    • Time to Interactive(可交互时间)
    • DOM 树构建完毕,代表可以绑定事件
  • DCL
    • DOMContentLoaded(事件耗时)
    • 当HTML 文档被完全加载完毕之后才, DOMContentLoaded事件会触发
  • L
    • onLoad(事件耗时)
    • 当依赖的资源全部加载完毕之后才会触发
  • FP
    • First Paint(首次绘制)
    • 第一个像素点绘制到屏幕的时间
  • FCP
    • Frist Contentful Paint(首次内容绘制)
    • 首次绘制任何文本,图像,非空白节点的时间
  • FMP
    • Frist Meaningful paint(首次有意义绘制)
    • 首次有意义绘制是页面可用性的度量标准
  • LCP
    • Largest Contentful Paint(最大内容渲染)
    • 在 viewport 中最大的页面元素加载的时间
  • FID
    • Frist Input Delay(首次输入延迟)
    • 用户首次和页面交互(单击链接, 点击按钮等) 到页面响应交互的时间

浏览器运行机制

1. 浏览器是多进程的区分进程和线程?

  • 进程是一个工厂,工厂有它的独立资源
  • 工厂之间相互独立
  • 线程是工厂中的工人,多个工人协作完成任务
  • 工厂内有一个或多个工人
  • 工人之间共享空间
  • 工厂的资源 -> 系统分配的内存(独立的一块内存)
  • 工厂之间的相互独立 -> 进程之间相互独立
  • 多个工人协作完成任务 -> 多个线程在进程中协作完成任务
  • 工厂内有一个或多个工人 -> 一个进程由一个或多个线程组成
  • 工人之间共享空间 -> 同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)

最后,再用较为官方的术语描述一遍:

  • 进程是cpu资源分配的最小单位(是能拥有资源和独立运行的最小单位)
  • 线程是cpu调度的最小单位(线程是建立在进程的基础上的一次程序运行单位,一个进程中可以有多个线程)
  • 不同进程之间也可以通信,不过代价较大
  • 现在,一般通用的叫法:单线程与多线程,都是指在一个进程内的单和多。(所以核心还是得属于一个进程才行)

2. 浏览器都包含哪些进程?

1.Browser进程:浏览器的主进程(负责协调、主控),只有一个。作用有

负责浏览器界面显示,与用户交互。如前进,后退等
负责各个页面的管理,创建和销毁其他进程
将Renderer进程得到的内存中的Bitmap,绘制到用户界面上
网络资源的管理,下载等

2.第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建

3.GPU进程:最多一个,用于3D绘制等

4.浏览器渲染进程(浏览器内核)(Renderer进程,内部是多线程的):默认每个Tab页面一个进程,互不影响。主要作用为

页面渲染,脚本执行,事件处理等

3. 浏览器多进程的优势?

  • 避免单个page crash影响整个浏览器
  • 避免第三方插件crash影响整个浏览器
  • 多进程充分利用多核优势
  • 方便使用沙盒模型隔离插件等进程,提高浏览器稳定性

4. 浏览器内核(渲染进程)

1.GUI渲染线程

  • 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。
  • 当界面需要重绘(Repaint )或由于某种操作金发回流 (reflow )时,该线程就会执行
  • 注意,GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(相当于被冻结),GUI更新会保存在一个队列中等到JS引擎空闲时立即被执行

2.JS引擎线程

  • 也称为JS内核,负责处理JavaScript脚本.(例如V8引擎)
  • JS引擎线程负责解析JavaScript脚本运行代码
  • JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序
  • 同样注意,GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞

3.事件触发线程

  • 归属于浏览器而不是JS引擎,用来控制事件循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)
  • 当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中
  • 当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的对尾,等待JS引擎的处理
  • 注意,由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)

4.定时触发器线程

  • 传说中的 setInterval 与 setTimeout 所在线程
  • 浏览器定时计数器并不是由JavaScript 引擎计数的,(因为JavaScript引擎是单线程的,如果处于阻塞线程状态就会影响计时的准确)
  • 因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)
  • 注意,W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。

5.异步http请求线程

  • 在XMLHttpRequest在连接后通过浏览器新开一个线程请求
  • 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调在放入事件队列中。再由JavaScript引擎执行

5. Browser进程和浏览器内核(Renderer进程)的通信过程

如果自己打开任务管理器,然后打开一个浏览器,就可以看到:任务管理器中出现了两个进程(一个是主控进程,一个则是打开Tab页的渲染进程),然后在这前提下,看下整个过程

  • Browser进程收到用户请求,首先需要获取页面内容(比如通过网络下载资源),随后将该任务通过RendererHost接口传递给Render进程
  • Renderer进程的Renderer接口收到消息,简单解释后,交给渲染线程,然后开始渲染
    • 渲染线程接收请求,加载网页并渲染网页,这其中可能需要Browser进程获取资源和需要GPU进程帮助渲染
    • 当然可能会有JS线程操作DOM(这样可能会造成回流并重绘)
    • 最后Render进程将结果传递给Browser进程
  • Browser进程接收到结果并将结果绘制出来

6. 梳理浏览器内核中线程之间的关系

  • GUI渲染线程与JS引擎线程互斥
    由于JavaScript是可操作DOM的,如果在修改这些元素属性同时渲染界面(即JS线程和UI线程同时运行),那么渲线程前后获得的元素数据就可能不一致。

    因此为了防止渲染出现不可预期的结果,浏览器设置GUI渲染进程与JS渲染引擎为互斥的关系,当JS引擎执行时GUI线程会被挂起,GUI更新则会被保存在一个队列中等到JS引擎线程空闲时立即被执行

  • JS阻塞页面加载
    尽量避免JS执行事件过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉

  • WebWorker,JS的多线程?
    JS引擎是单线程的,而且JS执行时间过长会阻塞页面,那么JS就真的对cpu密集型计算无能为力么?
    后来HTML5支持了 web worker

    Web Worker为web内容在后台线程中运行脚本提供了一种简单的方法。线程可以执行任务而不干扰用户界面

    一个worker是使用一个构造函数创建的一个 Object(e.g.Worker())运行一个命名的JavaScript文件,这个文件包含将工作线程中运行的代码;workers运行在另一个全局上下文中,不同当前的window,因此,使用window快捷方式获取当前全局的范围,在一个worker内将返回错误

    这样理解

    • 创建爱你worker时,JS引擎向浏览器申请开一个子线程(子线程是浏览器开的,完全受主线程控制,而且不能操作DOM)
    • JS引擎线程与worder线程间通过特定的方式通信(postMessage API,需要通过序列化对象来与线程交互特定的数据)

    所以,如果有非常耗时的工作,请单独开一个 Worker 线程,这样里面不管如何翻天覆地都不会影响JS引擎主线程,只待计算好出结果后,将结果通信给主线程即可。

    而且注意下,JS引擎是单线程的,这一点的本质仍然未改变,worker可以理解是浏览器给JS引擎开的外挂,专门用来解决那些大量计算问题。

7.WebWorker 与 SharedWorker

  • WebWorker 只属于某个页面,不会和其他页面的Render进程(浏览器内核进程)共享
    • 所以Chrome浏览器为SharedWorker单独创建一个进程来运行JavaScript程序,在浏览器中每个相同的JavaScript只存在一个SharedWorker进程,不管它被创建多少次。

本质上就是进程和线程的区别。SharedWorker 由独立的进程管理,WebWorker只是属于render进程下的一个线程

8.浏览器渲染流程

  • 浏览器输入url,浏览器主进程接管,开一个下载线程,然后进行 http 请求(略去DNS查询,IP寻址等等操作),然后等待响应,获取内容,随后将内容通过RendererHost接口转交给Renderer进程
  • 浏览器渲染流程开始

浏览器内核拿到内容后,渲染大概可以划分成一下几个步骤

  • 解析html建立dom树
  • 解析css构建render树(将css代码解析成树形的数据结构,然后结合DOM合并成render树)
  • 布局render树(Layout/reflow),负责各元素尺寸、位置的计算
  • 绘制render树(paint),绘制页面像素信息
  • 浏览器将各层的信息发送给GPU,GPU会将各层合成(composite),显示在屏幕上。

load事件与DOMContentLoaded事件的先后
上面提到,渲染完毕后会触发load事件,那么你能分清出load事件与DOMContentLoaded事件的先后么?

  • 当DOMContentLoaded 事件触发时,仅当DOM加载完成,不包括样式表,图片(比如如果有async加载的脚本就不一定完成)
  • 当onload事件触发时,页面上所有的DOM,样式表,脚本,图片都已经加载完成了。(渲染完毕了)
  • 顺序是:DOMContentLoaded -> load

css加载是否会阻塞dom树渲染?

这里说的是头部引入css的情况
首先,我们都知道:css是由单独的下载线程异步下载的。
这可能也是浏览器的一种优化机制
因为你加载css的时候,可能会修改下面DOM节点的样式
如果css加载不阻塞render树渲染的话,那么当css加载完之后,
render树可能又得重新重绘或者回流了,这就造成了一些没有必要的损耗。
所以干脆就先把DOM树的结构先解析完,把可以做的工作做完,然后等你css加载完之后,
在根据最终的样式来渲染render树,这种做法性能方面确实会比较好一点。

普通图层和复合图层
渲染步骤中就提到了 composite 概念。
可以简单的这样理解,浏览器渲染的图层一般包括两大类:普通图层以及复合图层
首先,普通文档流内可以理解为一个复合图层(这里称为默认复合层,里面不管添加多少元素,其实都在同一个复合图层中)
其次,absolute布局(fixed也是一样),虽然可以脱离普通文档流,但它仍然属于默认复合层。

优化策略

网络优化

  1. 减少HTTP请求数, 合并js、css,合理嵌套 css、js
  2. 合理设置服务端缓存,提高服务器处理速度.(强缓存、对比缓存)
    - Expires/Cache-Control Etag/if-none-match/last-modified/if-modified-since
  3. 避免重定向,重定向会降低响应速度(301, 302)
  4. 使用 dns-prefetch, 进行DNS 预解析
  5. 采用域名分片技术, 将资源放到不同的域名下, 接触同一个域名最多处理 6个TCP链接问题
  6. 采用 CDN 加速加快访问速度,(指派最近、高度可用)
  7. gzip 压缩优化 对传输资源进行体积压缩 (html、css、js) img,video等不建议打包
    - // Content-Encoding: gzip
  8. 加载数据优先级: preload(预先请求当前页面需要的资源) prefetch(将来页面中使用的资源) 将数据缓存到HTTP 缓存中

关键渲染路径

  1. 重排: 添加元素,修改大小,删除元素,获取位置信息,移动元素位置, 都会触发重排
  2. 脱离文档流
  3. 渲染时给 图片 增加固定宽高
  4. 尽量使用 css3 动画
  5. 可以使用 will-change 提取到单独的图层中

图片优化

  1. 图片格式
    1. jpg: 适合色彩丰富的照片、banner图; 不适合图形文字、图标(纹理边缘有锯齿),不支持透明度
    2. png: 适合纯色、透明、图标,支持半透明; 不适合色彩丰富图片,因为无损存储会导致存储体积大
    3. gif: 适合动画,可以动的图标;不支持半透明,不适合存储彩色图片
    4. webp:适合半透明图片,可以保证图片质量和较小的体积 (兼容性不好)
    5. svg: 格式图片;相比于 jpg 和 png它的体积更小,渲染成本过高,适合小且色彩单一的图标
  2. 避免空 src 的图片
  3. 减少图片尺寸, 节约用户流量
  4. img 标签设置 alt 属性,提升图片加载失败时的用户体验
  5. 原生的 loading:lazy 图片懒加载 <img loading='lazy' src='./img/1.jpg' width='300' height='300'> 一般不用这个,因为不知道什么时候图片会加载
  6. 不同环境下,加载不同尺寸和像素的图片 <img src='./img/1.jpg' sizes='(max-width:500px) 100px,(max-width:600px) 200px' srcset='./img/2.jpg 100w ./img/3.jpg 200w'>w:宽度
  7. 对于较大的图片可以考虑采用渐进性图片(设计去做, 一点一点加载) 一般小
  8. 采用 base64URl 减少图片请求 缺陷非常大(如果图片大,会比以前图片大3分之一)
  9. 采用雪碧图合并图标图片等

HTML 优化

  1. 语义化 HTML: 代码简洁清晰,利于搜索引擎,便于团队开发
  2. 提前声明字符编码,让浏览器快速确定如果渲染网页内容 html lang=“zh-CN”
  3. 减少 HTML 嵌套关系、减少DOM 节点数量
  4. 删除多余空格、空行、注释、及无用的属性等
  5. HTML 减少 iframes 使用(iframe 会阻塞 onload 事件可以动态加载 iframe) 子页面加载完才能加载父页面(异步加载), src 动态加载
  6. 避免使用 table 布局

CSS 优化

  1. 减少伪类选择器、减少样式层数、减少使用通配符(可忽略)
  2. 避免使用 css 表达式, css 表达式会频繁求值,当滚动页面,或者移动鼠标时都会重新计算(IE6,7) (可忽略)
  3. 删除空行、注释、减少无意义的单位、css进行压缩
  4. 使用外链 css,可以对 css 进行缓存
  5. 添加媒体字段,只加载有效的 css 文件 <link href="index.css" rel="stylesheet" media="screen and (min-width:1024px)">
  6. css contain 属性,将元素进行隔离 (减少渲染资源)
  7. 减少 @import 使用, 由于 @import 采用的是串行加载 (link 是并行加载)

js 优化

  1. 通过 async(加载完了就立即执行)、defer(加载完后等待执行) 异步加载文件
  2. 减少 DOM 操作,缓存访问过的元素 (每次都重绘)
  3. 操作不直接应用到 DOM 上, 而应用到虚拟 DOM 上. 最后一次性的应用到DOM上
  4. 使用 webworker 解决程序阻塞问题
  5. IntersectionObserver ( 滚动API )
  6. 虚拟滚动 vertual-scroll-list
  7. requestAnimationFrame、requestIdleCallback (frqme 生命周期)
  8. 尽量避免使用 eval, 消耗时间久
  9. 使用事件委托,减少事件绑定个数
  10. 尽量使用 canvas 动画、css动画

字体优化

FOUT、FOIT

优化策略方法

  1. 关键资源个数越多,首次页面加载时间就会越长
  2. 关键资源的大小,内容越小,下载时间越短
  3. 优化白屏: 内联 css 和内联 js 移除文件下载,较小文件体积
  4. 预渲染,打包时进行渲染
  5. 使用 SSR 加速首屏加载,(耗费服务端资源), 有利于 SEO 优化. 首屏利用服务端渲染,后续交互采用客户端渲染
  6. 静态化

浏览器的存储

  1. cookie: cookie过期时间内一直有效,存储大小 4k 左右、同时限制字段个数,不适合大量的数据存储,每次请求会携带 cookie, 主要可以利用身份检查
    1. 设置 cookie 有效期
    2. 根据不同子域划分 cookie 较少传输
    3. 静态资源域名和 cookie 域名采用不同域名,避免静态资源访问时携带 cookie
  2. localStorage: chrome下最大存储 5M, 除非手动清除,否则一直存在,利用 localStorage 存储静态资源
  3. sessionStorage: 会话级别存储,可用于页面间的传值
  4. indexDB: 浏览器的本地数据库(基本无上限)

增加体验 PWA

  1. web App Manifest: 将网站添加到桌面、更类似 native 的体验
  2. Service Worker: 离线缓存内容, 配合cache API
  3. Push Api && Notification Api : 消息推送与提醒
  4. App Shell && App Skeleton: App 壳、骨架屏

LightHouse 使用

  1. npm install lighthouse -g
  2. lighthouse --view http://baidu.com

缓存机制

介绍

浏览器缓存机制有四个方面,按照获取资源时请求的优先级依次排列为:Memory Cache、Service Worker Cache、HTTP Cache、Push Cache。

Memory Cache (内存缓存)

  • 缓存存在内存中,在优先级方面,它是浏览器第一个尝试访问的缓存。浏览器秉承的是“节约原则”。Base64 格式的图片,几乎永远可以被塞进 memory cache,这可以视作浏览器为节省渲染开销的“自保行为”体积不大的 JS、CSS 文件,也有较大地被写入内存的几率——相比之下,较大的 JS、CSS 文件就没有这个待遇了,内存资源是有限的,它们往往被直接甩进磁盘。
  • 内存缓存中有一块重要的缓存资源是preloader相关指令(例如 )下载的资源。总所周知preloader的相关指令已经是页面优化的常见手段之一,它可以一边解析js/css文件,一边网络请求下一个资源。
  • 还有一个磁盘缓存

Service Worker

  • 我们书写的js代码通常是在主线程运行,可以访问DOM与Windows全局变量,Service Worker与Web Worker则是独立于主线程的JavaScript线层,它脱离于浏览器窗体,因此无法直接访问 DOM。这样独立的个性使得 Service Worker 的“个人行为”无法干扰页面的性能,这个“幕后工作者”可以帮我们实现离线缓存、消息推送和网络代理等功能。我们借助 Service worker 实现的离线缓存就称为 Service Worker Cache。同时Service Worker对协议也是有要求的,必须是https。

  • Service Worker 的生命周期包括 install、active、working 三个阶段。一旦 Service Worker 被 install,它将始终存在,只会在 active 与 working 之间切换,除非我们主动终止它。这是它可以用来实现离线存储的重要先决条件。

HTTP Cache

  • Cache-Control、expires 等字段控制的缓存 (gzip)

Push Cache (推送缓存)

  • Push Cache(推送缓存)是 HTTP/2 中的内容,当以上三种缓存都没有命中时,它才会被使用。它只在会话(Session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂,在Chrome浏览器中只有5分钟左右,同时它也并非严格执行HTTP头中的缓存指令。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值