其他✅
可有办法判断用户的网络条件, 判断网速快慢,网络状态?
答案
核心概念
判断用户网络条件主要依赖浏览器提供的 API 和网络请求观测,常用于动态调整前端资源加载策略,提升用户体验。
常用方法与原理
- Navigator Connection API:通过
navigator.connection获取网络类型(如 4g/3g)、下行速度(Mbps)、RTT 等,支持度有限但信息丰富。 - 请求测速法:发送小文件或 API 请求,测量响应时间,结合文件大小估算带宽,适合所有浏览器。
- 在线/离线事件:监听
online和offline事件或读取navigator.onLine,判断用户是否联网。
代码示例
// 1. Connection API
if ('connection' in navigator) {
const c = navigator.connection
console.log(c.effectiveType, c.downlink, c.rtt)
c.addEventListener('change', () => console.log('网络类型变化:', c.effectiveType))
}
// 2. 请求测速
const start = Date.now()
fetch('your-small-file-url').then(() => {
const duration = Date.now() - start
console.log('测速耗时:', duration, 'ms')
})
// 3. 在线/离线监听
window.addEventListener('online', () => console.log('网络已连接'))
window.addEventListener('offline', () => console.log('网络已断开'))
常见误区
- 误以为
navigator.onLine能判断真实网络可用性,实际仅反映物理连接状态。 - 依赖 Connection API 时未做兼容性处理,导致部分用户无效。
- 请求测速未考虑缓存、CDN 影响,建议用不可缓存资源。
实际开发中建议多手段结合,先用 Connection API 获取大致类型,再用测速法动态调整资源加载策略。
延伸阅读
弱网检测该如何做
在 JavaScript 中,可以通过以下几种方式进行弱网检测:
一、监测网络连接状态
- 使用
navigator.onLine属性:这个属性可以判断浏览器是否处于在线状态。当网络连接中断时,navigator.onLine会变为false;当网络连接恢复时,它会变为true。
- 示例代码:
function checkNetworkStatus () {
if (navigator.onLine) {
console.log('网络连接正常')
} else {
console.log('网络连接中断')
}
}
window.addEventListener('online', checkNetworkStatus)
window.addEventListener('offline', checkNetworkStatus)
- 使用
online和offline事件:可以监听online和offline事件来检测网络连接状态的变化。
- 示例代码与上述类似,在事件处理函数中执行相应的操作。
二、测量网络延迟和带宽
- 使用
XMLHttpRequest或fetch进行请求:可以发送一个小的请求到服务器,测量请求的响应时间。如果响应时间较长,可能表示网络状况不佳。
- 示例代码:
function measureNetworkLatency () {
const startTime = performance.now()
fetch('small-test-file.txt')
.then(() => {
const endTime = performance.now()
const latency = endTime - startTime
if (latency > 500) {
console.log('网络可能较慢')
} else {
console.log('网络状况良好')
}
})
.catch(() => {
console.log('网络连接问题')
})
}
measureNetworkLatency()
- 使用
Web Performance API:可以使用window.performance对象来获取页面加载和资源请求的性能数据,包括网络延迟和带宽信息。
- 例如,可以通过
performance.getEntriesByType('navigation')[0].responseEnd - performance.getEntriesByType('navigation')[0].requestStart来获取页面加载的总时间,包括网络延迟。
三、模拟弱网环境进行测试
-
使用浏览器开发者工具:现代浏览器的开发者工具通常提供了网络模拟功能,可以模拟不同的网络条件,如慢速 3G、2G 等,以测试应用在弱网环境下的表现。
-
使用第三方库:有一些专门的网络模拟库,如
Fiddler、Charles Proxy等,可以模拟各种网络状况,帮助进行弱网测试。
总之,弱网检测可以通过监测网络连接状态、测量网络延迟和带宽以及模拟弱网环境等方式来实现。根据具体的应用场景和需求,可以选择合适的方法来检测网络状况,并采取相应的措施来优化应用在弱网环境下的性能和用户体验。
Protobuf 相关知识
Protobuf(Protocol Buffers)是由 Google 开发的一种轻量级、高效的数据交换格式,它被用于结构化数据的序列化、反序列化和传输。相比于 XML 和 JSON 等文本格式,Protobuf 具有更小的数据体积、更快的解析速度和更强的可扩展性。
Protobuf 的核心思想是使用协议(Protocol)来定义数据的结构和编码方式。使用 Protobuf,可以先定义数据的结构和各字段的类型、字段等信息,然后使用Protobuf提供的编译器生成对应的代码,用于序列化和反序列化数据。由于 Protobuf 是基于二进制编码的,因此可以在数据传输和存储中实现更高效的数据交换,同时也可以跨语言使用。
相比于 XML 和 JSON,Protobuf 有以下几个优势:
-
可扩展性: Protobuf 支持向已有的消息类型中添加新的字段,而不会破坏对旧数据的兼容性。这使得系统能够逐渐演进而不需要修改所有的代码。
-
高效性: 相对于一些文本格式的序列化(如XML和JSON),Protobuf 使用二进制格式,因此更为紧凑,更高效地进行数据存储和传输。
-
语言中立: Protobuf 支持多种编程语言,包括但不限于C++, Java, Python, Go等,这使得不同语言的系统能够使用相同的数据结构进行通信。
-
自动代码生成: Protobuf 通过使用 .proto 文件定义消息结构,然后利用相应语言的编译器生成与消息结构对应的代码。这简化了开发过程,减少了手动编写序列化和反序列化代码的工作。
-
支持多种数据类型: Protobuf 提供了丰富的基本数据类型,包括整数、浮点数、布尔值、字符串等,以及可以嵌套的消息类型,使得可以构建复杂的数据结构。
-
适用于网络通信: Protobuf 在网络通信领域广泛应用,特别是在 gRPC 中作为默认的消息序列化格式。
可以参考文档:资料
web 应用中如何对静态资源加载失败的场景做降级处理
答案
核心结论:
前端静态资源(JS/CSS/图片/字体)加载失败通常源于 CDN 节点抖动、DNS 污染、版本发布旧文件被覆盖(404/Hash 失效)、网络离线或运营商劫持。 针对静态资源加载失败的降级策略需兼顾 自动化探测重试、域名与链路回退、Service Worker 缓存兜底、全局感知与监控上报 四大维度,形成高可用闭环。
原理解析与核心策略:
1. 全局资源加载错误捕获(事件捕获阶段)
资源加载错误(如 <script>、<link>、<img>)不会冒泡到 window,必须在 捕获阶段(capture: true)通过 window.addEventListener('error') 统一捕获,并根据 tagName 区分脚本、样式与图片:
window.addEventListener('error', (event) => {
const target = event.target || event.srcElement
const isElementTarget = target instanceof HTMLElement
// 区分普通 JS 运行报错与资源加载失败
if (isElementTarget && ['SCRIPT', 'LINK', 'IMG'].includes(target.tagName)) {
const resourceUrl = target.src || target.href
console.warn(`[Resource Load Error]: ${target.tagName} failed to load from ${resourceUrl}`)
// 触发自动降级逻辑与上报
handleResourceFallback(target)
}
}, true)
2. CDN 镜像域名主备切换与重试机制
针对核心脚本与样式,配置主备 CDN 域名列表,失败后按优先级自动重试并切换域名:
const CDN_DOMAINS = ['https://cdn1.example.com', 'https://cdn2.example.com', 'https://backup.example.com']
const MAX_RETRY = 2
function handleResourceFallback(element) {
const retryCount = Number(element.dataset.retryCount || 0)
if (retryCount >= MAX_RETRY) {
// 达到重试上限,进行 UI 提示或兜底降级
reportResourceError(element)
return
}
const originalUrl = element.src || element.href
const currentUrl = new URL(originalUrl)
const nextDomain = CDN_DOMAINS[(retryCount + 1) % CDN_DOMAINS.length]
const fallbackUrl = `${nextDomain}${currentUrl.pathname}${currentUrl.search}`
if (element.tagName === 'SCRIPT') {
const newScript = document.createElement('script')
newScript.src = fallbackUrl
newScript.dataset.retryCount = retryCount + 1
newScript.crossOrigin = element.crossOrigin || 'anonymous'
document.head.appendChild(newScript)
} else if (element.tagName === 'LINK' && element.rel === 'stylesheet') {
const newLink = document.createElement('link')
newLink.rel = 'stylesheet'
newLink.href = fallbackUrl
newLink.dataset.retryCount = retryCount + 1
document.head.appendChild(newLink)
}
}
3. Service Worker 拦截与离线缓存回退
通过 Service Worker 的 fetch 事件实现网关层降级,不仅可从 Cache Storage 优先响应历史缓存,还可在网络请求失败时平滑改发请求至备用源站:
// service-worker.js
self.addEventListener('fetch', (event) => {
if (event.request.destination === 'script' || event.request.destination === 'style') {
event.respondWith(
fetch(event.request).catch(async () => {
// 1. 尝试从本地缓存读取
const cachedResponse = await caches.match(event.request)
if (cachedResponse) return cachedResponse
// 2. 本地无缓存,尝试请求备用 CDN
const backupUrl = event.request.url.replace('https://cdn1.example.com', 'https://cdn2.example.com')
return fetch(backupUrl).catch((err) => {
// 3. 最终回退或返回降级空实现
throw err
})
})
)
}
})
4. 构建层集成(Webpack / Vite 自动化 Chunk 兜底)
现代前端单页应用(SPA)动态 import() 路由分割时,最常见问题是发布后用户未刷新页面,导致旧 Hash 对应的 chunk 404:
- Webpack:使用
webpack-retry-chunk-load-plugin,在 chunk 加载失败时自动执行退避重试或全局页面软刷新。 - 全局路由错误捕获(Vue Router / React Router):
router.onError((error) => {const pattern = /Loading chunk (\d)+ failed/gconst isChunkLoadFailed = error.message.match(pattern)if (isChunkLoadFailed) {// 避免无限死循环刷新,带上标记if (!sessionStorage.getItem('chunk_retry_reload')) {sessionStorage.setItem('chunk_retry_reload', '1')window.location.reload()}}})
5. 图片与非关键资源的 UI 兜底
对于图像类静态资源,采用内联 Base64 占位图或默认图片回退:
<img src="https://cdn.example.com/avatar.png" onerror="this.onerror=null;this.src='/static/images/default-avatar.svg';" alt="Avatar" />
面试官视角与考点总结:
- 事件模型细节:考查候选人是否清楚普通 JS 报错(冒泡)与静态资源 404/加载失败(不冒泡,只在捕获阶段触发)的区别。
- 工程化容灾意识:是否具备应对微前端 / 动态导入 Chunk 404、CDN 节点故障与网络抖动的兜底策略闭环(主备切换 + 监控告警 + 页面平滑重载)。
- 避免死循环:在执行动态创建标签或
location.reload()时,是否有严格的重试计数上限与限频防抖设计。
怎么处理连接雪崩问题?
答案
雪崩的问题发生是大量请求同时涌入服务器,导致服务器资源耗尽,无法处理请求。典型场景包括
- 流量激增:促销活动、热点事件等引发大量用户同时访问。
- 服务故障:某个关键服务不可用,导致大量请求堆积。
端侧的核心解决思是“削峰填谷+保护后端”,通过限流、排队、熔断、降级等手段平滑流量,保护系统稳定。
典型策略包括
- 指数退避 + 随机抖动,调整端侧重连策略,通过延长重连时间,避免请求堆积,平滑请求, 具体策略可以参考 亚马逊 full jitter
- 分流 基于服务端返回的连接池进行重试,进一步可以在本地保留一个连接池的 MAP 表,动态切换
- 降级 长连接多次失败,发起连接降级通过 long polling 或短轮询等方式继续保持连接
- 兜底告警和长时间重试 任然不可用,显示告知用户,同时拉长重试间隔,确保服务恢复后及时可用