2023面试真题之浏览器篇
人生当中,总有一个环节,要收拾你一下,让你尝一尝生活的铁拳
大家好,我是柒八九。
今天,我们继续2023前端面试真题系列。我们来谈谈关于浏览器的相关知识点。
如果,想了解该系列的文章,可以参考我们已经发布的文章。如下是往期文章。
你能所学到的知识点
- 浏览器的进程和线程 推荐阅读指数⭐️⭐️⭐️⭐️⭐️
- 浏览器渲染过程 推荐阅读指数⭐️⭐️⭐️⭐️⭐️
- Link rel= "prelaod" 推荐阅读指数⭐️⭐️⭐️⭐️
- cookie设置的几种方式 推荐阅读指数⭐️⭐️⭐️⭐️⭐️
- cookie和session的区别和联系 推荐阅读指数⭐️⭐️⭐️⭐️
- 客户端缓存 推荐阅读指数⭐️⭐️⭐️⭐️⭐️
- LightHouse v8/v9性能指标 推荐阅读指数⭐️⭐️⭐️⭐️⭐️
好了,天不早了,干点正事哇。
浏览器的进程和线程
进程:某个应用程序的执行程序。
线程:常驻在进程内部并负责该进程部分功能的执行程序。
当你启动一个应用程序,对应的进程就被创建。进程可能会创建一些线程用于帮助它完成部分工作,新建线程是一个可选操作。在启动某个进程的同时,操作系统(OS)也会分配内存以用于进程进行私有数据的存储。该内存空间是和其他进程是互不干扰的。
有人的地方就会有江湖,如果想让多人齐心协力的办好一件事,就需要一个人去统筹这些工作,然后通过大喇叭将每个人的诉求告诉对方。而对于计算机而言,统筹的工作归OS负责,OS通过Inter Process Communication
(IPC
)的机制去传递消息。
网页中的主要进程
浏览器渲染过程(13步)
页面渲染起始标识
- 当垂直同步信号(VSync)被
排版线程
接收到,新的屏幕渲染开始
- 当垂直同步信号(VSync)被
输入事件回调
- 输入事件的数据信息从
排版线程
向主线程
的事件回调中传递。 - 所有输入事件的回调(
touchmove/scroll/click
)应该先被调用,并且每帧都应该触发,但是这不是必须的
- 输入事件的数据信息从
rAF
(requestAnimationFrame
)- 这是一个用于屏幕视觉更新的理想的位置。
- 因为,在此处能够获取到
垂直同步事件
最新的输入数据。
{解析HTML|Parse HTML}
- 通过指定的解析器,将不能被浏览器识别的HTML文本,转换为浏览器能识别的数据结构:DOM对象。
重新计算样式
- 对新生成或被修改的元素进行样式信息计算。
- 生成CSSOM
- 将元素样式和DOM元素结合起来,就会生成
Render Tree
{布局|Layout}
- 计算每个可视元素的位置信息(距离视口的距离和元素本身大小)。
- 并生成对应的
Layout Tree
。
{更新图层树|Update Layer Tree}
- 在
Render 树
的基础上,我们会将拥有相同z 坐标空间的Layout Objects
归属到同一个{渲染层|Paint Layer}中。 Paint Layer
最初是用来实现{层叠上下文|Stacking Context}- 它主要来保证⻚面元素以正确的顺序合成。
- 在
{绘制|Paint}:
- 该过程包含两个过程,
- 第一个过程是绘制操作(
painting
)- 该过程用于生成任何被新生成或者改动元素的绘制信息(包含图形信息和文本信息);
- 第二个过程是栅格化(
Rasterization
),- 用于执行上一个过程生成的绘制信息。
{页面合成|Composite}:
- 将图层信息(
layer
)和图块信息提交(commit
)到合成线程(排版线程)中。并且在合成线程中会对一些额外的属性进行解释处理。 - 例如:某些元素被赋值
will-change
或者一些使用了硬件加速的绘制方式(canvas
)。
- 将图层信息(
{栅格化|Rasterize} :
- 在绘制阶段(
Paint
)生成的绘制记录(Paint Record
)被合成线程维护的{图块工作线程|Compositor Tile Worker}所消费。 - 栅格化是根据图层来完成的,而每个图层由多个图块组成。
- 在绘制阶段(
页面信息提交:
- 当页面中所有的图层都被栅格化,并且所有的图块都被提交到{合成线程|Compositor},此时{合成线程|Compositor}将这些信息连同输入数据(input data)一起打包,并发送到GPU线程。
页面显示:
- 当前页面的所有信息在
GPU
中被处理,GPU
会将页面信息传入到双缓存中的后缓存区,以备下次垂直同步信号到达后,前后缓存区相互置换。然后,此时屏幕中就会显示想要显示的页面信息。
- 当前页面的所有信息在
requestIdleCallback:如果在当前屏幕刷新过程中,主线程在处理完上述过程后还有剩余时间(<
16.6ms
),此时主线程会主动触发requestIdleCallback
。
Link rel= "prelaod"
元素的
rel
属性的preload
值允许你在HTML的中声明获取请求,指定页面将很快需要的资源,你希望在页面生命周期的早期开始加载这些资源,在浏览器的主线程启动之前。这确保了它们更早可用,不太可能阻塞页面的呈现,从而提高了性能。即使名称包含术语
load
,它也不会加载和执行脚本,而只是安排以更高的优先级下载和缓存脚本。
将
rel
属性设置为preload
,它将转换为我们想要的任何资源的预加载器。
还需要指定其他的属性:
href
属性设置资源的路径。as
属性设置资源类型
"utf-8" />
"preload" href="style.css" as="style" />
"preload" href="main.js" as="script" />
复制代码
预加载还有其他优点。使用as
指定要预加载的内容类型允许浏览器:
- 更准确地优先考虑资源加载。
- 存储在缓存中以备将来的请求,并在适当时重用该资源。
- 对资源应用正确的内容安全策略(
CSP
)。- 内容安全策略(CSP)是一个额外的安全层,它有助于检测和减轻某些类型的攻击,包括
- 跨站脚本(XSS)
- 数据注入攻击。
- 为它设置正确的
Accept
请求标头。
预加载资源的类型(as的值类型)
cookie设置的几种方式
通常我们有两种方式给浏览器设置或获取Cookie
:
- 第一种 通过
HTTP
方式对Cookie
进行赋值,又分为Request
和Response
:- HTTP Response Headers 中的
Set-Cookie
Header - HTTP Request Headers 中的
Cookie
Header
- HTTP Response Headers 中的
- 第二种 通过
JavaScript
对document.cookie
进行赋值或取值。
两种方式的区别
HTTP Cookie
Set-Cookie
Header,除了必须包含Cookie
正文,还可以选择性包含6个属性:
path
、domain
、max-age
、expires
、secure
、httponly
它们之间用英文分号和空格("; ")连接;
JS Cookie
在浏览器端,通过 document.cookie
也可以设置Cookie
,JS Cookie
的内容除了必须包含正文之外,还可选5个属性:
path
、domain
、max-age
、expires
、secure
JS 中设置
Cookie
和HTTP
方式相比较,少了对HttpOnly
的控制,是因为 JS 不能读写HttpOnly Cookie。
http请求什么情况下会携带cookie
Cookie
请求头字段是客户端发送请求到服务器端时发送的信息
如果满足下面几个条件:(domain/http/path
)
- 浏览器端某个
Cookie
的domain
(.a.com
) 字段等于请求的域名或者是请求的父域名,请求的域名需要是a.com
/b.a.com
才可以 - 都是
http
或者https
,或者不同的情况下Secure
属性为false
(即secure
是true
的情况下,只有https
请求才能携带这个cookie
) - 要发送请求的路径,跟浏览器端
Cookie
的path
属性必须一致,或者是浏览器端Cookie
的path
的子目录
,- 比如浏览器端
Cookie
的path
为/test
,那么请求的路径必须为/test
或者/test/xxxx
等子目录才可以
- 比如浏览器端
上面 3 个条件必须同时满足,否则该请求就不能自动带上浏览器端已存在的 Cookie
客户端怎么设置跨域携带 cookie
- 前端请求时在
request
对象中- 配置
"withCredentials": true;
- 配置
- 服务端在
response
的header
中- 配置
"Access-Control-Allow-Origin", "http://xxx:${port}";
- 配置
"Access-Control-Allow-Credentials", "true"
`
- 配置
cookie和session的区别和联系
Session
比Cookie
安全,Session
是存储在服务器端的,Cookie
是存储在客户端的
cookie
数据存放在客户端,session
数据放在服务器上。cookie
不是很安全,别人可以分析存放在本地的cookie
并进行cookie欺骗- 考虑到安全应当使用
session
- 考虑到安全应当使用
session
会在一定时间内保存在服务器上,当访问增多,会比较占用服务器的性能- 考虑性能应当使用
cookie
- 考虑性能应当使用
- 不同浏览器对
cookie
的数据大小限制不同,个数限制也不相同。 - 可以考虑将登陆信息等重要信息存放为
session
,不重要的信息可以放在cookie
中。
客户端缓存
本地存储小容量
Cookie
主要用于用户信息的存储,Cookie
的内容可以自动在请求的时候被传递给服务器。- 服务器在响应 HTTP 请求时,通过发送
Set-Cookie
HTTP 头部包含会话信息。 - 浏览器会存储这些会话信息,并在之后的每个请求中都会通过 HTTP 头部
cookie
再将它们发回服务器。 - 有一种叫作
HTTP-only
的cookie
。HTTP-only
可以在浏览器设置,也可以在服务器设置,但只能在服务器上读取
- 服务器在响应 HTTP 请求时,通过发送
Web Storage
- 提供在
cookie
之外的存储会话数据的途径 - 提供跨会话持久化存储大量数据的机制
Web Storage
的第 2 版定义了两个对象:- 1.
LocalStorage
的数据将一直保存在浏览器内,直到用户清除浏览器缓存数据为止。 - 2.
SessionStorage
的其他属性同LocalStorage
,只不过它的生命周期同标签页的生命周期,当标签页被关闭时,SessionStorage
也会被清除。 。
- 提供在
本地存储大容量
IndexDB
:是浏览器中存储结构化数据的一个方案IndexedDB
是类似于MySQL
或Web SQL Database
的数据库
WebSQL
: 用于存储较大量数据的缓存机制。- 已废弃并且被
IndexDB
所替代
- 已废弃并且被
Application Cache
:允许浏览器通过manifest
配置文件在本地有选择的存储JS/CSS/图片等静态资源的文件级缓存机制- 已废弃并且被
ServerWorkers
所替代
- 已废弃并且被
ServerWorkers
:离线缓存
{服务工作线程|Service Worker}
{服务工作线程|Service Worker}是一种类似浏览器中代理服务器的线程,可以拦截外出请求和缓存响应。这可以让网页在没有网络连接的情况下正常使用,因为部分或全部页面可以从服务工作线程缓存中提供服务。
服务工作线程在两个主要任务上最有用:
- 充当网络请求的缓存层
- 启用推送通知
在某种意义上
- 服务工作线程就是用于把网页变成像原生应用程序一样的工具
- 服务工作线程对大多数主流浏览器而言就是网络缓存
创建服务工作线程
ServiceWorkerContainer
没有通过全局构造函数创建,而是暴露了 register()
方法,该方法以与 Worker()
或 SharedWorker()
构造函数相同的方式传递脚本 URL。
serviceWorker.js
// 处理相关逻辑
main.js
navigator.serviceWorker.register('./serviceWorker.js');
复制代码
register()
方法返回一个Promise
- 该
Promise
成功时返回ServiceWorkerRegistration
对象- 在注册失败时拒绝
serviceWorker.js
// 处理相关逻辑
main.js
// 注册成功,成功回调(解决)
navigator.serviceWorker.register('./serviceWorker.js')
.then(console.log, console.error);
// ServiceWorkerRegistration { ... }
// 使用不存在的文件注册,失败回调(拒绝)
navigator.serviceWorker.register('./doesNotExist.js')
.then(console.log, console.error);
// TypeError: Failed to register a ServiceWorker:
// A bad HTTP response code (404) was received
// when fetching the script.
复制代码
即使浏览器未全局支持服务工作线程,服务工作线程本身对页面也应该是不可见的。这是因为它的行为类似代理,就算有需要它处理的操作,也仅仅是发送常规的网络请求。
考虑到上述情况,注册服务工作线程的一种非常常见的模式是基于特性检测,并在页面的 load
事件中操作。
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker
.register('./serviceWorker.js');
});
}
复制代码
如果没有
load
事件做检测,服务工作线程的注册就会与页面资源的加载重叠,进而拖慢初始页面渲染的过程
使用 ServiceWorkerContainer 对象
ServiceWorkerContainer
接口是浏览器对服务工作线程生态的顶部封装
ServiceWorkerContainer
始终可以在客户端上下文中访问:
console.log(navigator.serviceWorker);
// ServiceWorkerContainer { ... }
复制代码
ServiceWorkerContainer
支持以下事件处理程序
oncontrollerchange
:
在ServiceWorkerContainer
触发controllerchange
事件时会调用指定的事件处理程序。- 在获得新激活的
ServiceWorkerRegistration
时触发。 - 可以使用
navigator.serviceWorker.addEventListener('controllerchange',handler)
处理。
- 在获得新激活的
onerror
:
在关联的服务工作线程触发ErrorEvent
错误事件时会调用指定的事件处理程序。- 在关联的服务工作线程内部抛出错误时触发
- 也可以使用
navigator.serviceWorker.addEventListener('error', handler)
处理
onmessage
:
在服务工作线程触发MessageEvent
事件时会调用指定的事件处理程序- 在服务脚本向父上下文发送消息时触发
- 也可以使用
navigator.serviceWorker.addEventListener('message', handler)
处理
ServiceWorkerContainer
支持下列属性
ready
:返回Promise
- 成功时候返回激活的
ServiceWorkerRegistration
对象。 - 该Promise不会拒绝
- 成功时候返回激活的
controller
:
返回与当前页面关联的激活的ServiceWorker
对象,如果没有激活的服务工作线程则返回null
。
ServiceWorkerContainer
支持下列方法
register()
:
使用接收的url
和options
对象创建或更新ServiceWorkerRegistration
getRegistration()
:返回Promise
- 成功时候返回与提供的作用域匹配的
ServiceWorkerRegistration对象
- 如果没有匹配的服务工作线程则返回
undefined
- 成功时候返回与提供的作用域匹配的
getRegistrations()
:返回Promise
- 成功时候返回与
ServiceWorkerContainer
关联的ServiceWorkerRegistration
对象的数组; - 如果没有关联的服务工作者线程则返回空数组。
- 成功时候返回与
startMessage()
:开始传送通过Client.postMessage()
派发的消息
使用 ServiceWorkerRegistration 对象
ServiceWorkerRegistration
对象表示注册成功的服务工作线程。该对象可以在 register()
返回的解决Promise的处理程序中访问到。通过它的一些属性可以确定关联服务工作线程的生命周期状态。
调用 navigator.serviceWorker.register()
之后返回的Promise会将注册成功的 ServiceWorkerRegistration
对象(注册对象)发送给处理函数。
在同一页面使用同一 URL 多次调用该方法会返回相同的注册对象:即该操作是幂等的
navigator.serviceWorker.register('./sw1.js')
.then((registrationA) => {
console.log(registrationA);
navigator.serviceWorker.register('./sw2.js')
.then((registrationB) => {
console.log(registrationA === registrationB);
// 这里结果为true
});
});
复制代码
ServiceWorkerRegistration
支持以下事件处理程序
onupdatefound
:
在服务工作线程触发updatefound
事件时会调用指定的事件处理程序。- 在服务工作线程开始安装新版本时触发,表现为
ServiceWorkerRegistration.installing
收到一个新的服务工作者线程 - 也可以使用
serviceWorkerRegistration.addEventListener('updatefound',handler)
处理
- 在服务工作线程开始安装新版本时触发,表现为
LightHouse v8/v9性能指标 (6个)
FCP
(First Contentful Paint
)FCP
衡量的是,在用户导航到页面后,浏览器呈现第一块DOM内容所需的时间。- 页面上的图片、非白色
元素和
svg
都被认为是DOM
内容; iframe
内的任何内容都不包括在内- 优化手段:缩短字体加载时间
SI
(Speed Index
)SI
指数衡量内容在页面加载期间视觉显示的速度。Lighthouse
首先在浏览器中捕获页面加载的视频,并计算帧之间的视觉进展- 优化手段:1. 减少主线程工作 2. 减少
JavaScript
的执行时间
LCP
(Largest Contentful Paint
)LCP
测量视口中最大的内容元素何时呈现到屏幕上。这接近于用户可以看到页面的主要内容
TTI
(Time to Interactive
)TTI
测量一个页面变成完全交互式需要多长时间- 当页面显示
- 有用的内容(由First Contentful Paint衡量),
- 为大多数可见的页面元素注册了事件处理程序
- 并且页面在50毫秒内响应用户交互时,
- 页面被认为是完全交互式的。
TBT
(Total Blocking Time
)TBT
测量页面被阻止响应用户输入(例如鼠标点击、屏幕点击或按下键盘)的总时间。总和是FCP
和TTI
之间所有长时间任务的阻塞部分之和- 任何执行时间超过 50 毫秒的任务都是长任务。50 毫秒后的时间量是阻塞部分。
- 例如,如果检测到一个 70 毫秒长的任务,则阻塞部分将为 20 毫秒。
CLS
(Cumulative Layout Shift
)- 累积布局偏移 (
CLS
) 是测量视觉稳定性的一个以用户为中心的重要指标 - CLS 较差的最常见原因为:
- 1.无尺寸的图像
- 2.无尺寸的嵌入和
iframe
- 3.动态注入的内容
- 优化手段1. 除非是对用户交互做出响应,否则切勿在现有内容的上方插入内容 2. 倾向于选择
transform
动画
- 累积布局偏移 (
优化LCP
导致 LCP
不佳的最常见原因是:
- 缓慢的服务器响应速度
- 阻塞渲染的
JavaScript
和CSS
- 缓慢的资源加载速度
- 客户端渲染
缓慢的服务器响应速度
使用{首字节时间|Time to First Byte}(
TTFB
) 来测量您的服务器响应时间
- 将用户路由到附近的
CDN
- 缓存资产
- 如果
HTML
是静态的,且不需要针对每个请求进行更改,那么缓存可以防止网页进行不必要的重建。通过在磁盘上存储已生成HTML
的副本,服务器端缓存可以减少TTFB
并最大限度地减少资源使用。 - 配置反向代理(Varnish、nginx)来提供缓存内容
- 使用提供边缘服务器的 CDN
- 如果
- 优先使用缓存提供 HTML 页面
- 安装好的
Service Worker
会在浏览器后台运行,并可以拦截来自服务器的请求。此级别的程序化缓存控制使得缓存部分或全部 HTML 页面内容得以实现,并且只会在内容发生更改时更新缓存。
- 安装好的
- 尽早建立第三方连接
- 对第三方域的服务器请求也会影响
LCP
,尤其是当浏览器需要这些请求来在页面上显示关键内容的情况下。 - 使用
rel="preconnect"
来告知浏览器您的页面打算尽快建立连接。 - 还可以使用
dns-prefetch
来更快地完成 DNS 查找。 - 尽管两种提示的原理不同,但对于不支持
preconnect
的浏览器,可以考虑将dns-prefetch
做为后备。
- 对第三方域的服务器请求也会影响
阻塞渲染的 JavaScript 和 CSS
- 减少
CSS
阻塞时间- 削减
CSS
:CSS
文件可以包含空格、缩进或注释等字符。这些字符对于浏览器来说都不是必要的,而对这些文件进行削减能够确保将这些字符删除。使用模块打包器或构建工具,那么可以在其中包含一个相应的插件来在每次构建时削减 CSS 文件:对于webpack5
:css-minimizer-webpack-plugin i
- 延迟加载非关键
CSS
:使用Chrome
开发者工具中的代码覆盖率选项卡查找您网页上任何未使用的CSS
。
对于任何初始渲染时不需要的CSS
,使用loadCSS
来异步加载文件,这里运用了rel="preload"
和onload
。 - 内联关键
CSS
:把用于首屏内容的任何关键路径 CSS 直接包括在中来将这些
CSS
进行内联。
- 削减
- 减少
JavaScript
阻塞时间- 缩小和压缩
JavaScript
文件:
缩小是删除空格和不需要的代码,从而创建较小但完全有效的代码文件的过程。Terser
是一种流行的JavaScript
压缩工具;
压缩是使用压缩算法修改数据的过程。Gzip
是用于服务器和客户端交互的最广泛使用的压缩格式。Brotli
是一种较新的压缩算法,可以提供比Gzip
更好的压缩结果。
静态压缩涉及提前压缩和保存资产。这会使构建过程花费更长的时间,尤其是在使用高压缩级别的情况下,但可确保浏览器获取压缩资源时不会出现延迟。如果您的web
服务器支持Brotli
,那么请使用BrotliWebpackPlugin
等插件通过webpack
压缩资产,将其纳入构建步骤。否则,请使用CompressionPlugin
通过gzip
压缩您的资产。 - 延迟加载未使用的
JavaScript
通过代码拆分减少JavaScript
负载,-SplitChunksPlugin
- 最大限度减少未使用的
polyfill
- 缩小和压缩
最大限度减少未使用的 polyfill
Babel
是最广泛使用的编译代码的工具,它将包含较新语法的代码编译成不同浏览器和环境都能理解的代码。
要使用 Babel
只传递用户需要的信息
- 确定浏览器范围
- 对
@babel/preset-env
设置适当的浏览器目标 - 使用
停止向不需要的浏览器发送已翻译的代码
确定浏览器范围
翻译代码通常会导致文件的大小大于其原始形式。通过最小化编译量,可以减少捆绑包的大小,从而提高网页的性能。
Babel
没有包含特定的插件来选择性地编译您正在使用的某些语言特性,而是提供了许多预设,将插件捆绑在一起。使用@babel/preset-env
仅包含您计划瞄准的浏览器所需的转换和填充。
将@Babel/preset-env
包含在 Babel
配置文件的预置数组中.babelrc
:
{
"presets": [
[
"@babel/preset-env",
{
"targets": ">0.25%"
}
]
]
}
复制代码
通过向浏览器字段添加适当的查询,可以使用 target
字段指定要包含的浏览器版本。@babel/preset-env
集成了 Browserslist
,这是一种在不同工具之间共享的针对浏览器的开源配置。浏览器列表文档中有一个兼容查询的完整列表。另一种选择是使用。Browserslstrc
文件来列出您希望定位的环境。
> 0.25%
值告诉 Babel
只包含支持占全球使用量0.25% 以上的浏览器所需的转换。这样可以确保您的捆绑包不包含用于只有很小一部分用户使用的浏览器的不必要的已翻译代码。
在大多数情况下,这种方法比使用以下配置更好:
"targets": "last 2 versions"
复制代码
“last 2 versions”的值会转换每个浏览器的最后两个版本的代码,这意味着支持停止使用的浏览器,如 Internet Explorer
。如果您不希望使用这些浏览器访问应用程序,那么这可能会不必要地增加捆绑包的大小。
缓慢的资源加载速度
- 优化和压缩图像
- 对于许多网站来说,在页面加载完毕后,图像会是视图中的最大元素。这种情况的常见示例包括首图、大型轮播或横幅图像
- 改善这些类型的图像进行加载和渲染所需的时间将直接提升 LCP 的速度。实现方式:
- 首先考虑不使用图像。如果图像与内容无关,请将其删除。
- 压缩图像(例如使用
Imagemin
) - 将图像转换为更新的格式(JPEG 2000、JPEG XR 或 WebP)
- 使用响应式图像
- 考虑使用图像 CDN
- 预加载重要资源
- 有时,在某个
CSS
或JavaScript
文件中声明或使用的重要资源可能会比所期望的要晚一点被获取,例如深藏在应用程序众多 CSS 文件中的某个字体。 - 知道某个特定资源应该被优先获取,请使用
来更加及时地获取该资源
- 多种类型的资源都可以进行预加载,但您应该首先侧重于预加载关键资产,例如字体、首屏图像或视频,以及关键路径 CSS 或 JavaScript
- 有时,在某个
- 压缩文本文件
- 压缩诸如
Gzip
和Brotli
之类的算法可以显著缩减在服务器和浏览器之间传输的文本文件(HTML
、CSS
、JavaScript
)大小。所有浏览器都有效支持Gzip
,而Brotli
几乎可以在所有较新的浏览器中使用,并能提供更好的压缩结果。 - 首先,检查您的服务器是否已经自动压缩文件。大多数托管平台、CDN 和反向代理服务器在默认情况下都会对资产进行压缩编码,或者使您能够轻松配置资产。
- 如果您需要对服务器进行修改来使其压缩文件,请考虑使用
Brotli
,而不是gzip
,因为Brotli
可以提供更好的压缩率。 - 选择您要使用的压缩算法后,请在构建过程中提前压缩资产,而不是在浏览器请求时实时压缩资产。这样能够最大限度地减少服务器开销并防止在发出请求时出现延迟,尤其是在使用高压缩比的情况下。
- 压缩诸如
- 使用
Service Worker
缓存资产Service Worker
可用于缓存任何静态资源,并在收到重复请求时将资源直接提供给浏览器,而无需通过网络。- 使用
Service Worker
预缓存关键资源可以显著减少资源加载时间
客户端渲染
在搭建客户端渲染的网站时,请考虑以下优化:
- 最小化关键 JavaScript
- 使用服务端渲染
- 使用预渲染
最小化关键 JavaScript
如果您网站上的内容只有在一定数量的 JavaScript
完成下载后才变得可见或可以与之交互:尽可能缩减您的代码包的大小就变得尤为重要。这可以通过以下方式实现:
- 削减 JavaScript
- 延迟加载未使用的 JavaScript
- 最大限度减少未使用的 polyfill
使用预渲染
预渲染是一种独立的技巧,该技巧比服务端渲染简单。无头浏览器是一种没有用户界面的浏览器,我们会用无头浏览器在构建期间生成每个路由的静态 HTML 文件。然后可以将这些文件与应用程序所需的 JavaScript 包一起进行运送。
优化TTI
对TTI
有特别大影响的一个改进是延迟或删除不必要的JavaScript工作。寻找优化JavaScript
的机会。特别是,考虑通过代码拆分和应用PRPL模式来减少JavaScript
的有效负载。对第三方JavaScript的优化也为一些站点带来了显著的改进。
使用 PRPL 模式实现即时加载
PRPL
是四个英文单词的首字母缩写,它描述了一种可以提高网页加载速度和交互性的模式
- 推送 (
Push
)(或预加载)最重要的资源。 - 尽快渲染 (
Render
) 初始路线。 - 预缓存 (
Pre-cache
) 剩余资产。 - 延迟加载 (
Lazy load
) 其他路线和非关键资产。
作者:前端小魔女
来源:juejin.cn/post/7203180003470311483