【前端写专利】一种基于预加载的网站首次访问加速方案
本人郑重声明: 作为多篇前端专利拥有者,公司专利提交主力军,本人公开发表的专利样本、原本文章均为未成功发布的专利,其中可能原因为创新点不足、功能逻辑未闭环或者缺少资料补充等等,此类文章未构成法律侵权。 也慎重提醒大家,正式专利受国家专利法保护,引用需按要求来,绝对禁止直接搬运交底书、专利原文。
摘要
利用预加载技术实现网站加速打开和交互的方案很多,但是对于特定条件下复杂网站首次访问的加速并没有可行的通用方法。
针对特定条件下的复杂网站加速,提出了一种基于预加载网站资源的加速首次打开方法。通过在前置网站中嵌入 iframe,并在 iframe 中提前把主网站需要的相关资源文件和动态数据缓存至本地,然后当用户进入主网站后优先读取已经缓存的资源和数据并通过 hash 值、时间戳等对比方式确认缓存是否可用,最后,主网站使用缓存打开页面,可以大幅度加快首次打开速度和交互速度。通过多种不同复杂度的网站验证,该方法稳定可靠,并且可以运用到网站加速中。
关键词:预加载;首次访问;网站加速;动态内容;
一、背景技术
随着科学技术的进步,人们越来越多的通过使用 Web 网站来满足生活和工作中的各种需求,对于网站的打开速度要求也越来越高,尤其首次访问打开速度体验会明显影响用户的留存率。
一般网站加载时间每超过1秒就会流失30%的访客,相反,快速加载的网站通常会具有更高的转化率。如沃尔玛研究发现:网站页面打开时间从1S增加到4S时,转化率的下降最为明显,而网站的打开速度每提升1S,转化率会提升2%。
预加载作为加速网站打开的重要技术,预加载的资源可以从缓存中立即获取,而不必依赖于网络请求,这可以显著提高应用的加载速度和响应性。
二、现状分析
目前预加载的实现有多种解决方案,然而
- 预加载通常发生在网站打开之后,对首次访问起到的加速效果有限
- 有一些在网页访问之前进行预缓存的方案,但只能缓存静态资源,对于包含动态或个性化内容的页面可能无法准确捕捉和呈现。
因此需要一种可以在网站打开之前同时预加载资源和动态数据的方案,缓存资源用于提高速度,数据用于动态化内容,可以提高用户对网站的使用体验。
2.1 CDN
CDN 技术是一种通过提前将内容加载到全球分布的 CDN 边缘节点,以优化内容的传输速度和用户体验的方法。
但其缩短资源加载速度的效果远不如读取本地缓存,并且通常情况下其只能缓存静态资源而无法缓存动态数据,因此其只能在一定程度上和一定范围内提高网站首次打开速度,无法做到真正的动态加速。
2.2 preload / prefetch / dns-prefetch
preload
和 prefetch
是两种用于优化网页性能的技术,它们都涉及到在页面加载过程中提前获取资源,dns-prefetch
可以进行 DNS 预解析,提前解析域名对应的 IP 地址。
但使用 preloa
、prefetch
和dns-prefetch
,因为其必须在网站打开后才开始工作,不可避免会导致初始页面加载时间变长,因为浏览器需要额外的时间来下载这些资源。而且预加载的资源可能不会在用户实际访问相关内容之前被使用,这可能会造成一些不必要的网络开销,尤其是在移动设备上。
2.3 async / defer
async
和 defer
是用于控制脚本加载和执行的两个 HTML <script>
元素的属性。它们对于优化网页性能和脚本执行时机有重要作用。
async
和 defer
允许脚本异步加载,不会阻塞 HTML 解析,可以与其他资源并行加载,提高页面加载速度。异步加载的脚本不会阻塞页面的渲染,因此可以更快地呈现页面内容。但async
和 defer
并非真正意义上的预加载技术,反而是通过延迟非重要资源的加载,从而让主资源更快加载的方案。因其只是改变资源加载顺序而非速度,所以网站全面功能的体验时间并未被缩短。
2.4 Service Worker 预缓存
使用 Service Worker 可以实现离线缓存和资源预加载,确保用户在离线状态下也能访问网站。Service Worker 可以在后台异步预加载资源,以提高用户体验。
但 Service Worker 预缓存的资源需要在首次加载时下载并存储在本地,后续再次访问才可以被利用起来。因此这会导致用户在首次进入网站是初始页面加载时的网络开销增加,尤其是对于包含大量资源的网站。
2.5 prerender
<link rel="prerender">
是一种用于预渲染网页的技术,通过在用户浏览器后台预加载和渲染下一个可能访问的页面,以提高用户体验。然而,其直会提前加载和渲染整个页面,包括图片、样式表、脚本等,对于包含动态或个性化内容的页面,预渲染可能无法准确捕捉和呈现,因为它只能渲染静态的页面快照。
三、发明内容
本方案描述了一种基于预加载网站资源和动态数据的首次访问加速方法
3.1 核心步骤
- 通常主体网站前都会有一个前置页面,如果没有可增加一个前置页面,此前置页面足够简单,功能少、依赖少,可以极速打开
- 在前置网站中嵌入 iframe 并进行隐藏处理,使用户无法感知
- Iframe 中打开主体网站
- 主体网站启动后使用 Service Worker 缓存网站静态资源和动态接口数据到本地数据库 IndexDB 和 cache storage中
- 用户打开主体网站,对缓存资源和数据查找并进行校验
- 如资源和数据存在且可用,直接使用缓存渲染
- 如果资源或数据不可用,则放弃对应缓存进行常规拉取
3.2 核心示意图
3.3 前置网站中嵌入 iframe
在网站 html 代码中引入 iframe 标签,并设置隐藏属性
<style>
.hidden-iframe {
display: none;
}
</style>
<iframe src="https://example.com" class="hidden-iframe"></iframe>
3.4 主体网站使用 Service Worker
在网站 js 代码中启动 Service Worker,为了防止作用域污染,将安装前注销所有已生效的 Service Worker
var sp = window.location.protocol + '//' + window.location.host + '/';
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(regs => {
for (let reg of regs) {
reg.unregister();
}
navigator.serviceWorker
.register(sp + 'service-worker.js', {
scope: sp,
})
.then(reg => {
console.log('set scope: ', sp, 'Service Worker instance: ', reg)
});
});
}
3.5 Service Worker 中缓存资源和动态数据(使用 Workbox 实现)
// 基础配置
import { setCacheNameDetails, skipWaiting, clientsClaim } from 'workbox-core';
// 缓存相关
import { precacheAndRoute } from 'workbox-precaching/precacheAndRoute';
import { registerRoute, setDefaultHandler } from 'workbox-routing';
import {
NetworkFirst,
StaleWhileRevalidate,
CacheFirst,
NetworkOnly,
} from 'workbox-strategies';
// 插件
import { CacheableResponsePlugin } from 'workbox-cacheable-response/CacheableResponsePlugin';
import { ExpirationPlugin } from 'workbox-expiration/ExpirationPlugin';
// 内置方案
import { pageCache, offlineFallback } from 'workbox-recipes';
// 自定义方法和插件
import { getCacheKeyWillBeUsed, blockStaticSource, blockImageSource } from '@/utils-ts/service-worker'
self.__WB_DISABLE_DEV_LOGS = true;
setCacheNameDetails({
prefix: 'sw-tools',
suffix: 'v1',
precache: 'precache',
runtime: 'runtime-cache',
});
skipWaiting();
clientsClaim();
/*
通常当用户访问 / 时,对应的访问的页面 HTML 文件是 /index.html,默认情况下,precache 路由机制会在任何 URL 的结尾的 / 后加上 index.html,这就以为着你预缓存的任何 index.html 都可以通过 /index.html 或者 / 访问到。当然,你也可以通过 directoryIndex 参数禁用掉这个默认行为
*/
precacheAndRoute(self.__WB_MANIFEST, {
ignoreUrlParametersMatching: [/.*/],
directoryIndex: null,
});
// URL navigation 缓存
pageCache();
// html 的缓存
// HTML,如果你想让页面离线可以访问,使用 NetworkFirst,如果不需要离线访问,使用 NetworkOnly,其他策略均不建议对 HTML 使用。
registerRoute(new RegExp(/.*.html/), new NetworkFirst());
// 静态资源的缓存
//CSS 和 JS,情况比较复杂,因为一般站点的 CSS,JS 都在 CDN 上,SW 并没有办法判断从 CDN 上请求下来的资源是否正确(HTTP 200),如果缓存了失败的结果,问题就大了。这种我建议使用 Stale-While-Revalidate 策略,既保证了页面速度,即便失败,用户刷新一下就更新了。如果你的 CSS,JS 与站点在同一个域下,并且文件名中带了 Hash 版本号,那可以直接使用 Cache First 策略。
registerRoute(
blockStaticSource,
new StaleWhileRevalidate({
cacheName: 'static-resources',
plugins: [
new CacheableResponsePlugin({
statuses: [0, 200],
}),
new ExpirationPlugin({
maxEntries: 500,
maxAgeSeconds: 30 * 24 * 60 * 60,
}),
],
}),
);
// 图片的缓存
// 图片建议使用 Cache First,并设置一定的失效事件,请求一次就不会再变动了。
registerRoute(
blockImageSource,
new CacheFirst({
cacheName: 'images',
plugins: [
{ cacheKeyWillBeUsed: getCacheKeyWillBeUsed('all') },
new CacheableResponsePlugin({
statuses: [0, 200],
}),
new ExpirationPlugin({
maxEntries: 200,
maxAgeSeconds: 30 * 24 * 60 * 60,
}),
],
}),
);
// xxxx 接口的缓存
registerRoute(
/^http(s)?://((dev.)|(test.)|(testing.))?xxxx.net/api/v\d+/(.*)?/xxxx.*/,
new StaleWhileRevalidate({
cacheName: 'xxxx_cache',
plugins: [
{ cacheKeyWillBeUsed: getCacheKeyWillBeUsed('t') },
new CacheableResponsePlugin({
statuses: [0, 200],
}),
new ExpirationPlugin({
maxEntries: 2000,
maxAgeSeconds: 7 * 24 * 60 * 60,
}),
],
}),
);
// yyyy 接口
registerRoute(
/^http(s)?://((dev.)|(test.)|(testing.))?xxxx.net/api/v\d+/(.*)?/yyyy.*/,
new StaleWhileRevalidate({
cacheName: 'yyyy_cache',
plugins: [
{ cacheKeyWillBeUsed: getCacheKeyWillBeUsed('t') },
new CacheableResponsePlugin({
statuses: [0, 200],
}),
new ExpirationPlugin({
maxEntries: 10000,
maxAgeSeconds: 20 * 60, // 20分钟
}),
],
}),
);
ps:Workbox 是 Google Chrome 团队推出的一套 PWA 的解决方案,这套解决方案当中包含了核心库和构建工具,因此我们可以利用 Workbox 实现 Service Worker 的快速开发。
3.6 主网站判断资源和数据是否可用
- 判断静态资源是否可用,可以对资源路径添加 hash 值,html 文件引入带 hash 值的文件,Service Worker 拦截请求,通过带 hash 值的路径查找缓存,判断是否存在
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hashed Resource Example</title>
<!-- 使用带哈希值的静态资源 -->
<link rel="stylesheet" href="/styles/style-abc123.css" />
</head>
<body>
<h1>Hashed Resource Example</h1>
<!-- 页面内容 -->
<script src="/scripts/script-xyz456.js"></script>
</body>
</html>
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
// 如果缓存中有匹配的资源,则返回缓存,否则从网络请求
return response || fetch(event.request);
})
);
});
- 判断动态数据是否可用,假设认为5分钟以内的数据为可用数据
const CACHE_NAME = 'my-api-cache-v1';
const MAX_AGE_SECONDS = 300; // 5分钟
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((cachedResponse) => {
if (cachedResponse) {
// 缓存中有匹配的响应
const cacheTimestamp = cachedResponse.headers.get('x-cache-timestamp');
const currentTimestamp = new Date().getTime();
// 检查时间戳是否在有效期内
if (currentTimestamp - parseInt(cacheTimestamp, 10) < MAX_AGE_SECONDS * 1000) {
// 数据在有效期内,返回缓存
return cachedResponse;
} else {
// 数据过期,从网络请求新数据
return fetchAndCache(event.request);
}
} else {
// 缓存中没有匹配的响应,从网络请求新数据
return fetchAndCache(event.request);
}
})
);
});
function fetchAndCache(request) {
return fetch(request).then((response) => {
// 克隆响应以便能够在响应中使用两次
const responseToCache = response.clone();
// 将新数据缓存起来
caches.open(CACHE_NAME).then((cache) => {
cache.put(request, responseToCache);
});
// 添加时间戳到响应头,以便后续判断数据是否过期
const timestampedResponse = new Response(response.body, {
headers: { 'x-cache-timestamp': new Date().getTime() },
});
return timestampedResponse;
});
}
可替代方案
- 前置网站可以是任何可以在主产品或主功能之前的页面,比如最常见的进入引导页、产品体验页、新手引导页、产品介绍页或网站入口页等
- IndexDB 可以换成 Web sql、localStorage等
- Cache Storage 可以换成 Application Cache 等
- Service Worker 技术,可以换成 HTTP 缓存等
- Hash 值校验可以换成,key 校验、时间戳校验或加密解密校验等校验方式
- Iframe 可以换成 webview 或 webContainer 等可以加载网站的容器
- 接口动态数据 5分钟以内可用的条件可以根据实际情况自行定义
结论
针对提升网站首次访问速度,本文提出了基于预缓存静态资源和动态数据的实现方案,其主要优势在于:
- 不同于其他预缓存方案只能在网站加载后进行,可以在网站访问之前进行预缓存
- 不同于其他缓存方案只能从第二次开始加速,新方案可以有效提升网站第一次的访问速度
- 不同于其他预缓存方案只能缓存静态资源,新方案同时缓存网站静态资源和数据,使网站打开速度快的同时交互也来的迅速,是一个完整的全缓存
- 不同于其他方案对用户有明显感知,新方案对用户几乎无感知
新方案有效的克服了其他方案提速效果不佳、无法提前预加载资源或无法预加载动态数据的缺点,此外新方法已经应用于实际项目中,实验结果表明能够满足实时性要求,是一个可靠的方案。
转载自:https://juejin.cn/post/7329796744791212095