百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

这个前端黑科技可能是YouTube比B站、优酷、爱奇艺加载快的原因

myzbx 2024-12-29 04:38 13 浏览

↑更多精彩,请点击上方蓝字关注我们!


“为什么同样是视频网站,Youtube 感觉加载很快、很轻,B 站、优酷、爱奇艺就感觉慢且重呢?”这是知乎上面的一个浏览次数很高的帖子,而在下面,有人就回答说


虽然我们并不确定这是否是主要因素,但是Service Worker对于前端性能优化的增益效果足以可见了。

之前在上篇超过100位程序员大佬:这是现代前端性能优化必会的黑科技中我们已经为大家介绍了Service Worker的概览,以及生命周期。今天我们将在下篇内容里为大家介绍service worker与APIs、使用场景以及小贴士。



APIs


Service Worker依赖于包括cachefetch在内的许多相关的浏览器API来使得网页应用功能更加丰富,越来越接近原生应用的体验。(注:这里的cache和fetch是指浏览器接口,与上文的Service Worker的生命周期事件注意区分)


Cache & Fetch API

作为一个网页应用,除了前端无法控制的服务接口,最重要的无非是获取和存储资源了,fetchcache两个接口作为现代浏览器的重要接口,Service Worker的fetch事件的响应几乎可以说是重度依赖于他们。如果浏览器版本稍老一些,可能还需要使用腻子脚本(polyfill)来提供这两个接口。


通过它们,Service Worker可以在截取页面的请求后,修改请求的参数内容,修改请求路径(注意跨域),延迟响应,修改响应内容,使用缓存内容伪造响应内容,甚至只用缓存构造一个完全不再依赖于网络的页面等等。




cache



添加资源到缓存对象中:

Bash
self.addEventListener('install', function(event) {
  // In install event, cache the resources first
  event.waitUntil(
    caches.open('my-cache-identifier')   // Open/create a cache with identifier
      .then(function(cache) {
        console.log('Opened cache');
        return cache.addAll([    
          '/',
          '/styles/main.css',
          '/script/main.js'
        ]); // Cache the major HTML, CSS, JS file
      })
  );
});


需要注意,一个缓存对象可以添加多个资源路径->响应结果。




fetch


Fetch API*是浏览器新的API标准,在Service Worker中通常被用于在FetchEvent*中转发请求。


当网页正在发送HTTP请求,触发了Service Worker的fetch事件,我们就能在劫持HTTP包中的内容,按照需要进行读取或者修改,然后再继续发送或者转发给其他目标。


通常有以下操作流程或其中一部分:

  • 检查请求目标是否已被缓存,如果已存在直接回复缓存内容;
  • 拆包分析请求内容,筛选请求,阻止某些请求被发送,或者修改其内容;
  • 使用fetch API发送请求,或者转发请求;
  • 当得到响应,拆包检查响应状态,类型或者其他的HTTP头,按照需要筛选或者修改回复体的内容;
  • 按需缓存响应结果;


流程代码示例:


Bash
self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        // Cache hit - return response
        if (response) {
          return response;
        }

        return fetch(event.request).then(
          function(response) {
            // Check if we received a valid response
            if(!response || response.status !== 200 || response.type !== 'basic') {
              return response;
            }

            // IMPORTANT: Clone the response. A response is a stream
            // and because we want the browser to consume the response
            // as well as the cache consuming the response, we need
            // to clone it so we have two streams.
            var responseToCache = response.clone();

            caches.open(CACHE_NAME)
              .then(function(cache) {
                cache.put(event.request, responseToCache);
              });

            return response;
          }
        );
      })
    );
});




存储 & 通信API

Fetch是通信用的API, cache是存储用的。但他们通常适用于“代理网页的网络请求”和 “缓存网页的资源”。


如果Service Worker还有其他的通信需求(如直接和页面通信,或者其他的Web Worker通信),或者存储不同需求的数据(如页面层数据,浏览器层数据,更大量的数据等),还有以下常用的API。




进程间通信


上文多次提及Web Worker, Service Worker与常规的Web Worker都是独立于渲染上下文的独立线程,所以它们都是无法直接操作DOM或者window对象的。如果我们有和其他Worker或者页面交互的需求,可以使用 postMessage* API和message事件来进行进程/线程间通信。


  • 在页面的主线程创建消息频道 MessageChannel*,使用 postMessage向频道上发送消息并且监听上面来的message事件:
function sendMessage(message) {
  // This wraps the message posting/response in a promise, which will resolve if the response doesn't
  // contain an error, and reject with the error if it does. If you'd prefer, it's possible to call
  // controller.postMessage() and set up the onmessage handler independently of a promise, but this is
  // a convenient wrapper.
  return new Promise(function(resolve, reject) {
    var messageChannel = new MessageChannel();
    messageChannel.port1.onmessage = function(event) {
      if (event.data.error) {
        reject(event.data.error);
      } else {
        resolve(event.data);
      }
    };

    // This sends the message data as well as transferring messageChannel.port2 to the service worker.
    // The service worker can then use the transferred port to reply via postMessage(), which
    // will in turn trigger the onmessage handler on messageChannel.port1.
    // See https://html.spec.whatwg.org/multipage/workers.html#dom-worker-postmessage
    navigator.serviceWorker.controller.postMessage(message,
      [messageChannel.port2]);
  });
}
  • 在Service Worker中,使用Client对象上的 Client.postMessage*来发送消息, 并且监听 message Service Worker自己的消息事件:

// Consume the message from host thread (or other Workers)
addEventListener('message', (event) => {
    console.log(`The client sent me a message: ${event.data}`);
});

{
  // Send message to host thread (or other Workers)
  clients.matchAll(event.clientId).postMessage({
    msg: "Hey I just got a fetch from you!",
  });
}

发送消息的demo

https://googlechrome.github.io/samples/service-worker/post-message/index.html




数据存储


Service Worker中也能使用各种新旧浏览器标准下的Web存储API来持久化数据,我们可以依照不同的需要来选择:



  1. 注意Service Worker的官方标准提到它是完全基于Promise的异步非阻塞 (it's designed to be fully asynchronous),同步的XHR请求和 localStorage (LocalStorage请求都是完全同步的*) 在Service Worker不可以被使用。
  2. 对于真正需要被长期持久化且在浏览器重启之后也需要被复用的内容,使用IndexedDB*是更加建议的方案,甚至可以在这之上做类似基于数据库的数据同步。




更多Web应用的API

Service Worker作为PWA的核心概念之一,它也是将web应用变得更接近原生应用的出发点。在它之上,更多浏览器特性提供了类似原生应用的支持。





使用场景


Server Worker在PWA之外也有诸多应用,基于它对HTTP请求和响应的强大管理能力,它可以作为多种依赖网络的应用的核心流程管理器。


  1. 全静态站点如果一个网站只包含静态数据而无需服务, 我们可以缓存所有的html页面,css样式,脚本和图片等资源,来使得这个页面在初次打开后可以被完全地离线访问。(宝可梦图鉴:https://pokedex.org/
  2. 预加载为了优化首屏渲染,页面上非必要的资源通常被延迟加载直到它们被需要。这类资源使用Server Worker来加载既可以使得在需要被加载时有良好的体验,又不会影响到首屏性能。(Demo:https://googlechrome.github.io/samples/service-worker/prefetch/index.html) / (Demo prefetch video:https://googlechrome.github.io/samples/service-worker/prefetch-video/index.html
  3. 应变响应有时候HTTP请求可能会因为不确定因素失败(如服务器离线,网络中断等),此时为用户提供一个应变的响应比如展示上一次成功加载的资源/数据。(例如:实时数据监测)Service worker可以帮助验证请求是否成功,如果失败就提供应变策略的回复。 (Demo:https://googlechrome.github.io/samples/service-worker/fallback-response/index.html
  4. 仿造响应仿造响应是非常有用的。它可以帮助我们隔离部分特定的请求来使用给定的回复,或者我们可以用它来测试一些尚不可用,或者不能稳定重现问题的资源或者REST API.
  5. 窗口缓存Service Worker来承担缓存数据的责任,页面可以直接使用window.cache来访问缓存。通过窗口缓存作为媒介可以间接实现service worker向页面的数据传递,也可以将Service Worker用作缓存的生产者而页面作为消费者。Demo:https://googlechrome.github.io/samples/service-worker/window-caches/index.html
  6. ......




小贴士


  • 在Service Worker中使用fetch API来转发请求,请求中默认不会包含cookie等中的用户认证信息。如果需要为转发请求附带认证信息, 在fetch请求中添加'credentials'的参数:
  • fetch(url, { credentials: 'include'})
  • 跨域资源默认是不支持缓存的,需要额外参数。如果目标资源支持CORS,在构建请求需要附带参数 {mode: 'cors'} 。如果目标资源不支持CORS或者不确定, 我们可以使用 non-cors模式,但这会导致"不透明"的响应, 意味着Service Worker不能判断响应中的状态,不透明的结果被缓存后仍被页面消费成non-cors的响应。
  • cache.addAll(urlsToPrefetch.map(function(urlToPrefetch) { return new Request(urlToPrefetch, { mode: 'no-cors' });})).then(function() { console.log('All resources have been fetched and cached.');});
  • 30X的HTTP状态码尚不支持离线请求重定向, 这是一个已知的issue(https://github.com/w3c/ServiceWorker/issues/1457)。建议在官方支持离线重定向前,根据你的使用场景寻找其他方案。
  • 在使用Service Worker代理HTTP的响应体时,务必记住clone* response,而不要直接消费掉响应体。原因是HTTP response是一个流, 它的内容只能被消费一次。只要我们仍然希望既能让浏览器正确地获得响应体中的内容,又能是它被缓存或者在Service Worker作内容检查,请不要忘记复制一个响应体。




网址导航:

  • Fetch API:https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
  • FetchEvent:https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent
  • postMessage:https://html.spec.whatwg.org/multipage/workers.html#dom-worker-postmessage
  • MessageChannel:https://developer.mozilla.org/en-US/docs/Web/API/MessageChannel
  • Client.postMessage:https://developer.mozilla.org/en-US/docs/Web/API/Client/postMessage
  • LocalStorage请求都是完全同步的:https://stackoverflow.com/questions/20231163/is-html5-localstorage-asynchronous
  • IndexedDB:https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
  • clone:https://fetch.spec.whatwg.org/#dom-response-clone




引用:

  • Chrome官方在线demo:https://github.com/GoogleChrome/samples/tree/gh-pages/service-worker
  • Service Worker简介:https://developers.google.com/web/fundamentals/primers/service-workers
  • Service Worker API - MDN:https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API
  • W3C官方标准:https://w3c.github.io/ServiceWorker/
  • Service Worker相关的资源:https://jakearchibald.github.io/isserviceworkerready/resources.html#moar

相关推荐

STM学习笔记--STM32F10X时钟

一:系统(SYSCLK)时钟3种(注:时钟频率较高)本文引用地址:http://www.eepw.com.cn/article/201609/296750.htmHSI振荡器时钟8MHZHSE振荡...

澜起科技领先启动DDR5时钟驱动芯片试产,助力提升数据速度与稳定性

添加我为微信好友<<<点击左侧,每日精选三只热门板块金股免费领。【澜起科技开启DDR5时钟驱动器芯片试产】澜起科技已启动DDR5第一子代时钟驱动器芯片的生产试验。该产品将用于新...

STM32F030 Nucleo-让MCU全速点灯,其中隐藏的含义(一)

只要是有点基础的骚友,点个灯就是个小KS!但是,我却发现一些猫腻!!!先看程序:从牛卡板卡的硬件原理图得知LD1为ST-Link的RGB指示灯,LD3为牛客板卡的电源指示灯,那么只剩下LD2了,LD2...

西门子-CPU模块的参数设定

右键单击CPU模块所在的行,并选中“对象属性(ObjectProperties…)”选项,可以打开CPU的参数设定页面。通过设定页面不同的标签,可以打开不同的参数设定对象。1.基本参数(Genera...

海湾GST5000主机报时钟电源故障

主板时钟电源主要记录系统的时间,也就是维持系统时钟的准确性。还有记录启动时要用的硬件信息,也就是BⅠOS信息。有好多厂家工控板有独立纽扣电池,如下图一般工控板电池没电,主机开机后系统时间不是准确的时间...

S7-1200 CPU 时钟与 CP 时钟的同步

CPU同步CP时钟按如下步骤组态实现CPU时钟同步CP时钟:1.在CPU属性中激活“通过NTP服务器启动同步时间”,同时激活“CPU与该设备中的模块进行同步”,即实现CPU...

开机需先按F1,开机后时间不准?手把手教你更换纽扣电池解决

台式机开机必按F1?手把手教你更换CR2032纽扣电池,彻底解决BIOS重置问题台式机主板上的CR2032纽扣电池(CMOS电池)是电脑的“记忆电源”,其核心作用是为BIOS芯片持续供电,确保在完全断...

空气钟摆着不动会自己上链,这种神秘装置是如何实现走时的?

我们都知道,积家在钟表领域发明了两个伟大的时计,一个是积家表的翻转表(Reverso),另一个则是积家的空气钟(Atmos),tmos空气钟在其发明近一个世纪后仍然是一项独特而有趣的发明,这种钟是在空...

2013款新帕萨特一键启动不能正常启动一步搞定

2013年购买2013款新帕萨特2.0御尊,一直使用正常,除了正常保养外,2019年更换过火花塞,别的都没更换。2022年2月10日启动时可点火,不能正常启动发动机。还提示驻车制动器故障等,长时间按压...

老板喊你设计一个高效的定时任务系统

【51CTO.com原创稿件】今天想跟大家一起探讨一个听起来很简单的话题:定时任务机制。无非就是一个计时器,到了指定时间就开始跑呗。tooyoung,要是这么简单我还说啥呢,干不就完了。那如果是几千...

三星S6 edge+不一样 双曲面屏新玩儿法

三星GalaxyS6edge+最为耀眼的特点就是它的双曲面侧屏,这是对此前三星S6edge的又一次升级。当然,这一特色为你带来的不仅是看起来爽而已,其实它用起来更爽。今天就为大家详细介绍一下三星...

深度分析:雄鹿队季后赛首轮被淘汰后,字母哥交易时钟正式启动

欢迎关注、点赞、评论、收藏、转发!##【字母哥踩到香蕉皮?再见印第安纳噩梦夜】周二那场加时赛宰雄鹿绝不手软,步行者最后四十秒轰出8-0冲击波。119-118比分活活憋死雄鹿最后一口气——又是五场解决...

用爆火的DeepSeek做了个桌面时钟

最近这DeepSeek是相当火爆啊。大家都在各种宣传它有多厉害,我也很好奇,就学者体验一下。我是个完全不会写代码的小白,没学过没用过,然后看别人都在用AI来替自己完成编程,很简单的样子。先打开浏览器。...

西门子1200PLC基础篇——定时器的具体应用

西门子1200PLC基础篇——定时器的具体应用上一篇我们一起学习了定时器的操作。今天我们用一个案例一起来学习定时器的具体应用。案例要求:Q0.0端停三秒,运行五秒,再停三秒,再运行五秒,依次循环闪烁。...

效率工具:推荐一款极简桌面待办工具

对于职场办公人员来说,每天有各种各样的事项需要处理,如何高效的管理待办事项非常关键。这个时候有一款好用的待办管理软件就可以帮助大家解决这个问题。今天给大家推荐Kite待办这款极简的桌面待办工具,希望...