语义化标签作用
- 优化 SEO,提升页面的搜索引擎的优先级
- 便于其他设备解析,如屏幕阅读器等,方便残障人员的访问
- 优化整体代码格式,增强语义化,便于代码的开发与维护
- 便于用户阅读,特别是针对样式丢失的情形
Vue3 的响应式基于 Proxy 实现;Vue2 的响应式基于 Object.defineProperty 实现
Vue3 更推荐使用 Composition API;Vue2 更推荐使用 Options API
Vue3 相较 Vue2 更利用 TypeScript 开发
Vue3 相对于 Vue2 更支持 TreeShaking
props & emits 父=>子 & 子=>父
provide & inject 祖=>孙
ref|$refs 引用 & expose
事件总线 EventBus & $on|$emit
$parent & $root
$attrs
<keep-alive>
理解在组件切换的时候,保存一些组件的状态防止重复渲染 DOM
LRU 缓存策略 - 找出最久未使用的数据并置换新的数据
通常基于链表实现,将新数据插至链表头部,若命中已有数据则将老数据移至链表头部,链表满后则丢弃链表尾部的数据
此时组件会增加 deactivated
与 activated
生命周期钩子,而替代 mounted
和 unmounted
生命周期钩子
nextTick()
理解作为 Vue 内部的异步队列的调用方法同时提供给开发者使用,核心是利用了如 Promise
、MutationObserver
、setImmediate()
、setTimeout()
等原生 JavaScript 方法来模拟对应的微/宏任务的实现,本质是对 JavaScript 执行原理 EventLoop 的一种应用
通常在数据变化后执行的某个操作需要使用随数据变化而变化的 DOM 结构的时候或需在 created
生命周期内修改 DOM 结构时使用
Model - 数据模型,定义数据和业务逻辑
View - UI 视图,负责数据展示
ViewModel - 负责监听 Model 数据改变并控制视图更新,同时处理用户交互操作
HTTP1.1 的报头必须是文本,数据体可以是文本或是二进制
HTTP2 的报头和数据体均为二进制,分别被称为头信息帧和数据帧
HTTP2 中头信息使用 gzip 或 compress 压缩后再发送
HTTP2 中客户端和服务器同时维护一张头信息表,所有字段存储在表中并生成索引号,头信息不会重复多次发送,后续头信息利用索引号避免重复发送
HTTP2 支持同时无序的发送多个请求或响应,从而避免队头堵塞的问题
因为 HTTP2 的数据包不是按序发送的,同个连接内的数据包可能属于不同的请求;因此 HTTP2 中将每个请求或响应的数据包均作为数据流给出唯一的编号,每个数据包均需标记数据流 ID,以表示其所属的数据流 ID
HTTP2 允许服务器未经请求,主导向客户端发送资源,可以一定程度上减少延迟时间
流量控制、传输可靠性
集成 TLS 加密功能
多路复用 - 同一物理连接上可以有多个独立的逻辑数据流
快速连接 - 基于 UDP,仅需 1~2 RTT 建立连接
TCP 发送数据前必须建立连接后发送数据
UDP 发送数据前不建立连接直接发送
TCP 连接通过给包编号并通过接收到数据后返回的 ACK 判断传输是否成功,从而确定是否重传数据;在出现拥塞时,会通过减小注入网络数据的速率和数量以缓解拥塞
UDP 不会进行检测包的发送成功与否,以恒定速率发送数据,不会根据网络状况调整发送速率
TCP 连接只有两个端点,只能进行点对点的数据传输
UDP 支持单播、多播、广播功能
TCP 连接在不保留报文边界情况下以字节流传输数据
UDP 连接在保留报文边界情况下以报文形式传输数据
TCP 首部最小 20 字节到 60 字节;UDP 首部为 8 字节
TCP 使用要求可靠连接的应用;UDP 适用于实时应用
200
OK 服务器成功处理301
Moved Permanently 永久重定向302
Found 临时重定向资源304
Not Modified 使用客户端缓存403
Forbidden 服务器拒绝资源访问404
Not Found 服务器未找到资源或无原因的拒绝访问500
Internal Server Error 服务器执行出错客户端向服务端发送连接请求报文段
服务端发送同意连接应答
客户端向服务端发送一个确认报文
客户端向服务端发送连接释放请求
服务端不再接收客户端发的数据,仍旧可以发送数据给客户端
服务端向客户端发送连接释放请求
客户端向服务端发送确认应答
SharedWorker 是 HTML 标准定义的 Web API 的一部分,是一种特殊的 Worker,支持在多个上下文(例如 window、iframe 甚至 Worker)之间共享
同时,SharedWorker 的全局上下文 SharedWorkerGlobalScope
也与 Worker 不同
和 Worker 一样,通过调用 SharedWorker()
构造函数来创建
1 | const worker = new SharedWorker('./worker.js') |
SharedWorker()
构造函数支持传入一组可选的配置项,与Worker()
构造函数相同SharedWorker()
构造函数也支持直接传入一个字符串,同配置项的name
参数;特别的,SharedWorker()
中的name
参数作为唯一的一个标识符,在创建新的与之前的拥有相同 URL 的 SharedWorker 时有一定作用
SharedWorker 通过脚本文件 URL 和 name 参数确定是否为同一个 SharedWorker
与 Worker 不同,Client 端通过创建的 SharedWorker 实例上的 port
属性暴露的 MessagePort
接口实例,调用其上的 postMessage()
方法实现发送消息
Client 端通过监听 SharedWorker 实例上的 message
事件实现接收到消息
1 | worker.port.start() |
SharedWorker 环境下接收消息,需要监听 connect
事件,从而获取到新的对应的 MessagePort 实例;监听 MessagePort 实例的 message
事件接收消息
SharedWorker 环境下接收消息,同样需要通过调用 MessagePort 实例的 postMessage()
方法实现发送消息
1 | self.addEventListener('connect', (e) => { |
通常,在 connect
事件回调函数内,会把接收到的 port 存储下来,以便之后使用
1 | const ports = [] |
仅支持在 SharedWorker 环境内调用 close()
方法,来卸载当前 Worker
1 | self.close() |
SharedWorker 生命周期与 Client 端的生命周期独立,当任一页面创建 SharedWorker 时其生命周期开始,在没有页面使用 SharedWorker 时其生命周期结束
SharedWorker 全局环境通过 SharedWorkerGlobalScope
表示,该接口继承自 WorkerGlobalScope
,它与 Worker 全局环境差别不大
1 | interface SharedWorker extends EventTarget, AbstractWorker { |
Web Worker 是 HTML 标准定义的 Web API 的一部分,可以在后台运行一个耗时的任务,避免因长期执行 JS 任务而阻塞用户界面渲染与交互
Web Worker 可以被 Window 环境创建,也可以被其他的 Worker 创建
Web Worker 是独立于主线程的一个线程,具有独立的作用域,其中运行的任务不会阻塞主线程
Web Worker 中的全局作用域 DedicatedWorkerGlobalScope
与 Window 的全局作用域不同,Window 环境中部分 API 在 Worker 环境中不可用或受到一定的限制
Web Worker 线程与主线程之间的通信通过 message 机制实现,传递的数据通过结构化拷贝算法传递,因此通常不存在处理线程安全的需要
通过调用 Worker()
构造函数,传入 Worker 脚本的 URL,来创建一个 Worker
1 | const worker = new Worker('./worker.js') |
Worker 脚本需要与 Client 同域
Worker()
构造函数支持传入一组可选的配置项
其type
参数指定脚本的类型,值可以是classic
或module
,默认值是classic
其name
参数指定 Worker 的名称,在 debug 时候有一定作用,在 Worker 内可以通过name
只读属性访问
其credentials
参数指定 Worker 的 credentials 选项,允许的值可以是omit
、same-origin
或include
若传入的 URL 解析失败,会抛出一个SyntaxError
错误
若接收到的脚本文件并非 JavaScript 格式,会抛出NetworkError
错误
若当前文档环境不支持创建 Worker,如未遵守同源策略,会抛出SecurityError
错误
无论是 Worker 端还是 Client 端,通过调用 postMessage()
方法实现发送消息,通过监听 message
事件实现接收消息
Client 发送消息
1 | worker.postMessage('message from client') |
Client 接收消息
1 | worker.addEventListener('message', (e) => { |
Worker 发送消息
1 | self.postMessage('message from worker') |
Worker 发送消息
1 | self.addEventListener('message', (e) => { |
此外,可以选择传入一组数组或包含 transfer
参数的配置项,定义需要转移所有权的对象
所有权被转移后,对应对象在原环境内不再可用,而是仅在新环境内可用
当然,传递的消息可以不仅仅是 string 类型,可以是其他任何可以被结构化拷贝算法执行的数据,包括:
结构化拷贝算法,严格来说,与 JSON.stringfy()
及 JSON.parse()
行为上不同。在结构化拷贝算法中,试图复制 Function 参数会抛出异常;但结构化拷贝算法支持复制包含循环对象的对象
可以转移的对象可以是:
SharedArrayBuffer
可以用于多个线程之间的共享数据,并利用 Atomics
实现线程同步与线程锁功能。
启用该 API 需要 secure context,并且需要 cross-origin isolate,可以通过检测 isSecureContext
全局变量和 crossOriginIsolated
全局变量来确定是否可以使用 SharedArrayBuffer
通过调用 worker 实例的 terminate()
方法,来卸载一个 Worker
1 | worker.terminate() |
或者调用 Worker 环境中的 close()
方法,来卸载当前的 Worker
1 | self.close() |
卸载是立即执行的,不会等待 worker 内部任务的完成
Worker 全局环境通过 DedicatedWorkerGlobalScope
表示,该接口继承自 WorkerGlobalScope
。
Worker 全局环境的 messageerror
事件会在传递的消息无法解析时触发,可用用于监听发送失败的消息(Worker 对象上同样存在)
Worker 全局环境的 importScripts()
方法可以导入一组同源的脚本文件,并在 Worker 全局环境下执行
1 |
|
ServiceWorkerGlobalScope
接口代表 ServiceWorker 的全局上下文,在 ServiceWorker 内通过 self 全局变量或者 globalThis 全局变量访问(该接口继承自 WorkerGlobalScope
)。
ServiceWorkerGlobalScope
以下代表在 ServiceWorkerGlobalScope
接口本身的属性、方法和事件:
ServiceWorkerGlobalScope
接口的 clients
属性代表一个 Clients
实例,可用于获取 Client
(可执行上下文)实例。
ServiceWorkerGlobalScope
接口的 registration
属性代表一个 ServiceWorkerRegistration
实例,即当前 ServiceWorker 注册的引用。
ServiceWorkerGlobalScope
接口的 serviceWorker
属性代表一个 ServiceWorker
实例,即当前 ServiceWorker 实例的引用。
ServiceWorkerGlobalScope
接口的 skipWaiting
方法强制当前 ServiceWorker 从等待状态变成激活状态,返回一个该 ServiceWorker 激活后完成的 Promise。其在 install 事件的回调中调用才具有实际意义。
WorkerGlobalScope
以下代表继承自 WorkerGlobalScope
接口的属性、方法和事件:
WorkerGlobalScope
接口的 location
属性代表一个 WorkerLocation
实例,是 Location
的字集。
WorkerGlobalScope
接口的 navigator
属性代表一个 WorkerNavigator
实例,是 Navigator
的字集。
WorkerGlobalScope
接口的 self
属性代表 WorkerGlobalScope
接口本身。
WorkerGlobalScope
接口的 importScripts
方法同步导入一组脚本文件并执行,接受一组参数,代表脚本文件的 URL,其可以为绝对路径或相对路径(相对文档路径)。
WorkerGlobalScope
接口的 error
事件在 ServiceWorker 内发生脚本错误时触发,返回一个 Event
实例。
WorkerGlobalScope
接口的 languagechange
事件在用户的首选语言更改时触发,返回一个 Event
实例。
WorkerGlobalScope
接口的 online
事件在浏览器获得网络访问权限并且 navigator.onLine
值切换到 true
时触发,返回一个 Event
实例。
WorkerGlobalScope
接口的 offline
事件在浏览器获得网络访问权限并且 navigator.onLine
值切换到 false
时触发,返回一个 Event
实例。
WorkerGlobalScope
接口的 rejectionhandled
事件在 ServiceWorker 内处理的 Promise 拒绝事件时触发,返回一个 Event
实例。
WorkerGlobalScope
接口的 unhandledrejection
事件在 ServiceWorker 内未处理的 Promise 拒绝事件时触发,返回一个 Event
实例。
以下代表暴露在全局的属性、方法和事件:
1 | self.fonts |
1 | self.atob() |
1 | interface ServiceWorkerGlobalScope extends WorkerGlobalScope { |
Local Font Access API 向开发者提供了获取用户本地安装的字体的信息的方式,包括字体名称、字体样式及字体族等等
调用 window.queryLocalFonts()
方法来获取本地安装的字体
方法允许传入一组可选的配置项,其 postscriptNames
参数允许传入一组字符串数组,代表希望筛选的 postscriptName 名称
方法会返回 Promise 的 FontData
数组,表示本地安装的字体的列表
方法可能抛出 NotAllowedError
异常,表示用户拒绝授予开发者 'local-fonts'
权限
方法可能抛出 SecurityError
异常,表示该 API 受 Permissions Policy 的限制无法被调用或调用该方法并非缘于用户交互行为
1 | window.queryLocalFonts().then((fonts) => { |
字体信息使用 FontData
接口表示
FontData
接口的 family
属性表示字体的字体族,可以用于 CSS 的 font-family
属性或者 @font-face
规则中的 local()
函数等;
FontData
接口的 fullName
属性表示字体的全名,通常是一个用户可辨识的名称,可以用于向用户展示;
FontData
接口的 postscriptName
属性表示字体的 PostScript 名称,可以用于唯一地辨识字体;
FontData
接口的 style
属性表示字体的样式,可以用于 CSS 的 font-style
属性;
1 | async function logFonts() { |
FontData
接口的 blob()
方法以 Blob 形式返回字体的源数据;
1 | async function getFontFormat(font) { |
该 API 调用受到 local-fonts
权限策略的控制,可以通过 Permissions-Policy
响应头指定,或通过 <iframe>
标签的 allow
属性指定
默认为 self
,即允许在当前上下文或内嵌的其他同源上下文中使用
该 API 调用需要用户授予 local-fonts
权限,可以调用 Permission.query()
方法检查用户是否已授予了该权限
1 | interface Window { |
最主要在于降低首屏加载资源大小,仅加载所需的页面资源文件,加快页面的显示
1 | import C from 'c' |
1 | { |
原理即将导航中的路由组件从静态 import
导入改为动态 import()
导入
原理同路由懒加载
1 | import C from 'c' |
1 | const C = () => import('c') |
做组件懒加载一般在某些特别条件下使用,如组件仅在特定条件下才展示、当前页面文件过大、组件复用性较强
较大外部依赖可动态导入
1 | import * as THREE from 'three' |
1 | import('three').then((THREE) => { /* do */ }) |
但建议谨慎采取此方式
依赖 ESM 的静态特性,进行静态分析,在生成产物中去除无用的模块或代码,从而降低生成产物的大小
webpack 默认在构建阶段会启用 Tree Shaking,在开发阶段需手动配置
1 | module.exports = { |
1 | module.exports = { |
使用副作用
1 | { |
某些情况下需手动标记 /*#__PURE__*/
以标记代码,以标记语句是可执行 Tree Shaking 的
vite 原生基于 rollup 支持在构建阶段启用 Tree Shaking
webpack
可以使用 terser-webpack-plugin
插件来执行代码构建产物的压缩
1 | const TerserPlugin = require("terser-webpack-plugin") |
vite
内部默认集成 esbuild
进行代码构建产物的压缩,同时支持配置为使用 terser
来执行压缩
1 | export default { |
静态资源构建产物(特别是图片)的压缩可以使用一些插件实现,如 compression-webpack-plugin
等
外部库(特别是 UI 组件库)使用插件(如 babel-plugin-import
等)进行按需加载
可以适当进行代码分割,避免一次性加载过大的资源文件,阻碍页面的展示;也需要避免过度分割,一次性执行过多的资源获取请求
1 | export default { |
部分小体量的 JS 文件或 CSS 文件,可以内联到 HTML 文件中,减少请求的数量
可通过 webpack-bundle-analyzer
插件或 rollup-plugin-visualizer
插件来分析查看
1 | const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer') |
1 | import { visualizer } from 'rollup-plugin-visualizer' |
检查项目的依赖包是否有重复引用的情况,避免出现使用同样名称不同版本的依赖包引用的情况
主要应用于缩短白屏时长,特别是 SPA 单页应用
原理是直接把展示骨架屏的内容放在 html 文件内,在真正内容加载完后再隐藏骨架屏的内容
只渲染可视区域的列表项,非可见区域的不渲染
原理为计算列表的总高度,并在触发滚动事件时根据滚动高度更新起始下标和结束下标,从而取出相应的数据渲染元素
将一些长任务逻辑移入到 Worker 中,避免长任务的执行阻碍 UI 渲染而影响用户体验
是否使用 Worker,需要比较 Worker 通信时长与运算时长相比是否具有足够的优势
可以利用 requestAnimationFrame 处理周期任务
特别是需要较严格固定周期频率执行的情况(setInterval 和 setTimeout 无法保证准确的时间间隔)
同时 requestAnimationFrame 支持在页面隐藏或最小化时暂停执行周期任务,以节省性能(setInterval 和 setTimeout 不会因页面隐藏或最小化等因素暂停执行)
CSS 动画 Animation、过渡 Transition、变换 Transform 相较于 JS 性能通过上更具优势,并且浏览器更易于针对性地做优化
避用通配符选择器 *
减少使用标签选择器
优先使用默认的样式继承
避免层数过大的选择器
提升代码复用率,进行功能代码封装等
CSS 的样式简化,可以使用 TailwindCSS
、UnoCSS
等方案
防抖,使得指定函数至少间隔 n 秒才会执行一次
节流,使得指定函数在 n 秒中最多执行一次
对于容易连续触发的事件,如 mousemove
、pointermove
、scroll
、touchmove
、wheel
、resize
等,通过将事件处理方法绑定为防抖节流版本的方法,避免持续多次重复执行方法
will-change
优化动态效果预先将执行动画的元素设置 will-change
CSS 属性,以便浏览器引擎将其视为单独图层来进行优化
注意点是,避免过度应用 will-change
属性;建议仅在需要时候 JS 动态设置该属性
重绘指元素的非几何样式改变引起的浏览器重新渲染目标元素的现象
重绘指元素的几何样式改变引起的浏览器重新渲染整个渲染树或的现象
CSS中可使用如下一些方式将目标元素独立为合成层,从而进行独立渲染,以触发 GPU 渲染
will-change
属性perspective
transform
避免出现无效请求,例如表单提交频繁点击的问题,或路由切换时还有未完成的请求;对于服务器和用户来说,会造成不必要的困扰
HTTP2 支持头部压缩,能够减少数据传输量,节省消息投占用的网络的流量
且 HTTP2 支持多路复用、服务器推送等功能
HTTP 头部及资源启用 gzip 压缩,能够大大减少网络传输的数据量
可以使用 compression-webpack-plugin
或 vite-plugin-compression
压缩打包资源至 gzip
1 | const compression = require('compression-webpack-plugin') |
1 | import compression from 'vite-plugin-compression' |
对资源服务开启 gzip 支持
1 | gzip on; |
一般推荐提前处理完成 gzip 文件,再直接交由 nginx 服务
执行网络请求时,浏览器会自动带上同源的 Cookie 信息,一定程度上会增大请求头的大小
可以通过精简 Cookie 的内容,来降低请求信息的大小
同时可以对静态资源单独部署,避免请求携带不必要的 Cookie 信息
通过给请求头或响应头设置 Keep-Alive 头,通常用于提示连接超时时间和最大请求量
1 | Connection: Keep-Alive |
可以在跨域请求设置 Access-Control-Max-Age
响应头指定预检请求的缓存期限,从而在指定期限内的跨域请求无需进行预检请求可以直接发起请求
1 | Access-Control-Max-Age: 600 |
JS 会阻碍 DOM 渲染
<script src="main.js"></script>
async
模式异步加载 JS,执行无顺序,加载完成后立即执行
可以用于加载与 DOM 无关的 JS 资源,如埋点统计等
<script async src="main.js"></script>
defer
模式异步加载 JS,执行有顺序,加载完成后统一在 DOMContentLoaded 事件触发前执行
一般情况均可使用 defer 优化 JS 资源的加载,避免 JS 脚本加载与执行阻碍网页的渲染
<script defer src="main.js"></script>
module
模式行为上会类似于 defer 模式
<script type="module" src="main.js"></script>
fetchpriority
资源加载优先级可以利用 fetchpriority
HTML 属性指定 script 脚本加载的优先级,优先加载级别高的脚本,延后加载级别低的脚本
需要避免 preload
和 prefetch
的混用,以避免不必要的二次自由加载
preload
预先下载当前页面将使用的资源并缓存(不会执行),会提升资源的优先级
需同时指定 as
属性与 href
属性
<link rel="preload" href="style.css" as="style" />
<link rel="preload" href="main.js" as="script" />
建议指定 type
属性,以避免浏览器下载格式不支持的资源
建议同时指定 crossorigin
属性
prefetch
预加载未来页面将使用的资源,并保存在缓存内一段时间,会降低资源的优先级
要求当前页面需为安全上下文
<link rel="prefetch" href="main.js" />
modulepreload
类似于 preload
预加载当前页面将使用的模块脚本资源,并进行解析与执行
<link rel="modulepreload" href="main.js" />
prerender
预加载目标资源并提前在后台处理执行
仅部分浏览器支持该非标准特性
一般情况下,dns-prefetch
与 preconnect
都是配对使用
但不建议过度使用 preconnect
,仅用于未来一段时间极可能访问或请求的 origin;否则仅应用 dns-prefetch
同时 dns-prefetch
的浏览器兼容性优于 preconnect
建议使用以上两属性的同时指定 crossorigin
属性
dns-prefetch
提前执行目标 origin 的 DNS 解析,可以加快未来将访问或请求的 origin 的处理速度(直接使用已预先解析的 DNS 缓存)
<link rel="dns-prefetch" href="https://fonts.googleapis.com/" />
preconnect
提前执行目标 origin 的连接 —— DNS 解析、TCP 连接(及 TLS 握手),可以加快未来将访问或请求的 origin 的处理速度
<link rel="preconnect" href="https://fonts.googleapis.com/" />
尽量减少对非必要的外部依赖的使用,使用轻量级别替代方案或者自行实现
常见的如:
使用轻量级 day.js
替代 moment.js
使用 ESM 的 lodash-es
替代 CJS 的 lodash
CDN 即 Content Delivery Network,其具有分布于多个地域的服务器阵列
CDN 可以降低私有服务器的访问压力
地理位置的距离可能相对更近,一定程度上可以降低网络资源加载的时延
CDN 保证了比较正确的缓存配置
服务器在响应资源时,通过指定 Expires
响应头或 Cache-Control
响应头来控制浏览器该资源的缓存策略;若被指定为强缓存并且在有效期内直接使用缓存;反之若为被禁止使用缓存,则进行协商缓存,通过 If-Modified-Since
头向服务器提供浏览器缓存的资源的修改时间(在获取资源时服务器通过 Last-Modified
头指定)或通过 If-None-Match
头向服务器提供浏览器缓存的资源的标识符(在获取资源时服务器通过 ETag
头指定)
Expires
(推荐使用 Cache-Control
代替,其优先级更高)【响应头】
指定缓存失效的时间
Cache-Control
【响应头,请求头】
配置缓存的策略及有效期
no-store
不允许缓存
no-cache
允许缓存,但使用前需进行服务端验证
must-revalidate
允许缓存,有效期内直接使用缓存,超出有效期需进行服务端验证,通常结合 max-age=N 使用
max-age=N
指定缓存的有效期
Last-Modified
【响应头】/ If-Modified-Since
【请求头】
资源最近修改时间
ETag
【响应头】/ If-None-Match
【请求头】
资源唯一标识符
ServiceWorker
实现可控缓存利用 ServiceWorker 结合 CacheStorage 实现可控缓存,原理是基于受 ServiceWorker 控制的上下文会在 ServiceWorker 全局触发 fetch
事件,可以通过调用返回的 FetchEvent
的 respondWith()
方法自定义响应
webp
图片webp 格式图片大小通常比同等情况下的其他格式图片大小有较大优势,因此若浏览器支持 webp 格式图片,优先使用 webp 格式图片
可以利用离线或在线 webp 图片格式转换工具转换图片格式为 webp
JS 手动控制
初始不指定图片标签的 src
属性,直到图片需要展示时再指定其 src
属性,避免图片的自动预加载
1 | <img data-src="/img/png" /> |
1 | // 适当情况下调用该方法 |
利用 img 标签特性(更推荐)
可以设置 img
标签的 loading
属性实现懒加载功能,将属性值指定为 lazy
以惰性加载图片
同时可以指定 img
标签的 fetchpriority
属性,以控制获取图片资源的优先级,设定为 high
以提升获取的优先级,设定为 low
以降低获取的优先级
同时可以指定 img
标签的 decoding
属性,以设定解码图片的模式(是否允许在图片解码完成前展示图片),设定为 sync
以同步解码图片,设定为 async
以异步解码图片
1 | <img src="/img/png" alt="" loading="lazy" fetchpriority="auto" decoding="auto" /> |
将小图标利用字体形式加载,如 IconFont
1 | @import url('//at.alicdn.com/t/font_8d5l8fzk5b87iudi.css'); |
1 | <i class="iconfont icon-xxx"></i> |
通常加载资源大小会更小,并且能够避免重复加载图片并降低请求数量,且支持修改各类字体样式
将小图片转换为 base64 编码内联入 html 文档,可以减少请求数量
webpack 中可以使用 url-loader
插件自动转换内联图片
1 | module.exports = { |
vite 原生支持内联图片,默认在图片大小小于 4KB 时启用
可以通过 build.assetsInlineLimit
选项配置启用的阈值
1 | export default { |
对图片生成多个尺寸的备用图片,使用时根据需要加载不同尺寸的图片,减少不必要的资源流量
将图片等静态资源部署在单独的静态资源服务器或是 CDN 上,避免直接打包到项目中
设置图片标签的尺寸大小,防止图片加载中导致页面布局抖动,影响 CLS 指标
使用第三方字体库时,尽可能按需生成,避免不必要的全量引入字体库
对于大对象数据,尽量采用 JSON 格式而非 JS 对象格式,因为 JSON 语法比 JS 简单,解析速度更快
如 vite 支持将 JSON 文件打包为 JSON 字符串而非 JS 对象
1 | export default { |
提前结束的逻辑利于编译器的优化
1 | function () { |
1 | function () { |
逻辑能提前结束就提前结束
switch 对于连续值会处理成数组,而数组会具有更高效率的随机访问性能
1 | function get(/* @type {1 - 10} */ level) { |
1 | function getPrice(level) { |
若条件可以处理成连续的数字,可以使用 switch 来进行优化
对于循环,满足条件或完成任务后即刻跳出,避免不必要的执行损耗
1 | function find(data) { |
1 | function find(data) { |
在循环处理数组、集合等容器的元素,若可以保证容器的容量不会发生变化,可以提前提取容器的容量,避免在循环中重复获取
对于一些和 2 相关的乘除法或者条件相关的,可以用位运算替代
1 | const A = 2 ** 8 |
1 | const A = 2 << 3 |
在 ServiceWorker 中,会对应着多个 Client
实例,代表着受 ServiceWorker 控制的上下文,可以是网页、Worker 乃至 SharedWorker 等,而 Client 实例的访问与控制通过 Clients
实例实现,通过 clients
属性获取
同时,在 ServiceWorker 中,通过 registration
属性暴露 ServiceWorkerRegistration
实例,即当前 ServiceWorker 注册;通过 serviceWorker
属性暴露 ServiceWorker
实例,即当前 ServiceWorker 实例
Clients
在 ServiceWorker 中,Clients
接口提供对 Client
接口的访问
Clients
接口的 get()
方法根据给定的 id 获取对应的 Client
实例
接收一个字符串,代表 Client
的 id
返回一个 Promise 的 Client
(或 undefined
)
Clients
接口的 matchAll()
方法获取所有匹配的 Client
实例
接收一组可选的配置项
配置项的 type
参数指定需匹配的 Client
类型,允许的值为 "window"
, "worker"
, "sharedworker"
和 "all"
,默认为 "window"
配置项的 includeUncontrolled
参数指定是否返回未受控制的 Client
,默认为 false
返回一个 Promise 的 Client 数组
Clients
接口的 claim()
方法将所有匹配的 Client
实例置于当前 ServiceWorker 的控制之下
返回一个 Promise
Clients
接口的 openWindow()
方法创建新的顶层上下文并加载给定的 URL
要求该 URL 需与 ServiceWorker 同源,并且要求调用前一段时间用户需发生交互行为
Firefox 中要求该方法必须在通知点击回调方法中调用
返回一个 Promise 的 WindowClient
实例或 null
Client
在 ServiceWorker 中,Client
接口表示受 ServiceWorker 控制的执行上下文
其中 WindowClient
接口继承自 Client
接口,表示受 ServiceWorker 控制的 Window 执行上下文
受 ServiceWorker 控制的各执行上下文可以是 Worker、Shared Worker 以及 Window
Client
接口的 id
属性返回一个字符串,代表对应的执行上下文的 ID
Client
接口的 type
属性返回一个字符串枚举,可能为 "window"
、"worker"
、"sharedworker"
之一,代表对应的执行上下文的类型
Client
接口的 url
属性返回一个字符串,代表对应的执行上下文的 URL
Client
接口的 frameType
属性返回一个字符串枚举,可能为 "auxiliary"
、"top-level"
、"nested"
、"none"
之一,代表对应的执行上下文的类型
WindowClient
接口的 focused
属性返回一个布尔值,代表对应的 Window 执行上下文是否处于聚焦状态
WindowClient
接口的 visibilityState
属性返回一个字符串枚举,可能的值为 "hidden"
、"visible"
、"prerender"
,代表对应的 Window 执行上下文的可见性类型
WindowClient
接口的 focus()
方法控制对应的执行上下文聚焦
返回一个 Promise 的 WindowClient
实例
WindowClient
接口的 navigate()
方法控制对应的执行上下文加载指定 URL
方法接收一个字符串或 URL 实例,代表 URL
若 ServiceWorker 执行上下文与 URL 同源,返回 Promise 的 WindowClient
;否则返回 Promise 的 null
在 Client 端中,通过 ready
属性暴露控制当前页面的 ServiceWorkerRegistration
实例,通过 controller
属性暴露控制当前页面的 ServiceWorker
实例
ServiceWorkerContainer
在 ServiceWorker 中,ServiceWorkerContainer
接口包含 ServiceWorker 的相关状态与相关控制方法,用于实现对 ServiceWorker 的管理
ServiceWorkerContainer
接口的 ready
属性返回一个 Promise 的 ServiceWorkerRegistration
,表示控制当前页面的 ServiceWorker 的注册;该属性与 ServiceWorkerGlobalScope
接口的 registration
属性类似
ServiceWorkerContainer
接口的 controller
属性返回一个 ServiceWorker
或 null
,表示是否存在控制当前页面的 ServiceWorker 的实例;该属性与 ServiceWorkerGlobalScope
接口的 serviceWorker
属性类似
ServiceWorkerContainer
接口的 getRegistration()
方法根据给定的 URL (默认使用当前 Client 的 URL)返回与之匹配的 ServiceWorkerRegistration
对象
方法可以接受一个参数
方法返回一个 Promise 的 ServiceWorkerRegistration 或者 undefined,根据是否存在对应的注册对象
ServiceWorkerContainer
接口的 getRegistrations()
方法获取所有与当前 Client 相关的 ServiceWorkerRegistration
对象
返回一个 ServiceWorkerRegistration
的数组
ServiceWorkerContainer
接口的 startMessages()
方法强制当前上下文提前开始接收发送自 ServiceWorker 的消息
ServiceWorkerContainer
接口的 controllerchange
事件在控制当前 Client 的 ServiceWorker 变化时触发,返回一个 Event
事件
ServiceWorkerRegistration
在 ServiceWorker 中,ServiceWorkerRegistration
接口表示 ServiceWorker 的注册对象
ServiceWorkerRegistration
接口的 active
属性返回一个 ServiceWorker
或者 null
,代表最新注册的 state
属性为 activating
或 activated
的 ServiceWorker
ServiceWorkerRegistration
接口的 installing
属性返回一个 ServiceWorker
或者 null
,代表最新注册的 state
属性为 installing
的 ServiceWorker
ServiceWorkerRegistration
接口的 waiting
属性返回一个 ServiceWorker
或者 null
,代表最新注册的 state
属性为 installed
的 ServiceWorker
通常而言,active
属性、installing
属性、waiting
属性三个中最多只有一个是非 null 值。
ServiceWorkerRegistration
接口的 scope
属性返回一个字符串,代表 ServiceWorker 的注册域
ServiceWorkerRegistration
接口的 updateViaCache
属性返回一个字符串枚举,可能的值为 'all'
、'imports'
、'none'
,指定 HTTP 缓存的脚本如何应用于 ServiceWorker
以上两个属性的值在注册 ServiceWorker 时指定
ServiceWorkerRegistration
接口的 updatefound
事件在新的 ServiceWorker 开始下载时触发,返回一个 Event
事件ServiceWorker
在 ServiceWorker 中,ServiceWorker
接口表示 ServiceWorker 对象
ServiceWorker
接口的 scriptURL
属性返回一个字符串,表示 ServiceWorker 的注册脚本 URL
ServiceWorker
接口的 state
属性返回一个字符串枚举,可能的值包括 "parsed"
、"installing"
、"installed"
、"activating"
、"activated"
、"redundant"
,表示 ServiceWorker 的状态
"parsed"
ServiceWorker 在下载完成并且验证可运行后的初始值"installing"
ServiceWorker 在安装中"installed"
ServiceWorker 安装完成"activating"
ServiceWorker 在激活中"activated"
ServiceWorker 激活完成"redundant"
ServiceWorker 被替代或安装失败ServiceWorker
接口的 statechange
事件在 ServiceWorker 的状态更新时触发,返回一个 Event
事件
1 | interface Client { |