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

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

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

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


“为什么同样是视频网站,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



添加资源到缓存对象中:

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头,按照需要筛选或者修改回复体的内容;
  • 按需缓存响应结果;


流程代码示例:


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

相关推荐

一键生成高颜值图表!让你的文字瞬间有画面感,职场人必备!

哈喽,打工人们!忙碌的周中,大熊又来给你们带来一个超实用的效率神器啦!这次的宝藏网站绝对是那种用过就离不开的"真香"型产品!假设你明天就要做重要汇报,可面对一大堆密密麻麻的文字材料,你...

批量将 Word 转换为 PDF/Excel/Txt/图片等多种格式

Word文档是我们工作中经常会打交道的一种文档格式,我们也经常会有需要对Word文档进行格式转换的需求,比如将Word格式转换为PDF、将Word文档转换为Excel、将Word...

绝了!一键用AI生成高颜值动态PPT(附详细步骤+Prompt)

大家好,我是一名酷爱研究AI的产品经理,最近我有个新发现:那些花了你3天做出来的PPT,现在用AI可以1小时搞定!而且颜值还高!为什么AI做PPT比传统方式效率高10倍?我用一张图就能告诉你:AI生成...

ztext - 简单几行代码创建酷炫3D特效文字的开源JS库

把网页上的文字变成酷炫的3D风格,还能制作旋转动效,有了ztext.js,只需要几行代码。ztext能做什么ztext.js是一个能把常规的平面文字变成3D样式的前端开源代码库,让开发者...

文字内插入小图片,也太可爱了吧(文字中怎么插图片)

图文排版H5手机版秀米有小伙伴留言问添加图片的时候可不可以把图片添加到文字之间比如下面这句话中的小贴纸图片后面可以接着输入文字其实吧这就是咱们的『文字内插入小图片』功能嘛可以用来在文字内加个表情包又...

Linux环境下C++代码性能分析方法(linux怎么写c++代码)

技术背景在开发C++应用程序时,找出代码中运行缓慢的部分是进行性能优化的关键。在Linux系统上,有多种工具和方法可用于对C++代码进行性能分析,每种方法都有其特点和适用场景。实现步骤手动中断调试法在...

SVG互动图文,让你的文章更有趣!教你4种简单易学的黑科技玩法!

如果你是一个公众号创作者,那么你一定想知道如何让你的文章更加吸引人,更加有趣,更加有创意。你可能已经尝试过各种图文排版技巧,但是你是否知道,有一种黑科技可以让你的文章变得更加酷炫,更加互动,更加爆款?...

Videoscribe怎么实现实心中文汉字的手绘制作

很多朋友在制作手绘视频的时候,不知道怎么输入实心的中文汉字,之前我们已经给大家分享了怎么输入汉字的方法,但是有一点遗憾的是输出的汉字是空心的手绘展示,在视觉上并不是非常的美观。经过大家不断的探索,终于...

一款用于将文本转化成图表的现代化脚本语言

大家好,又见面了,我是GitHub精选君!今天要给大家推荐一个GitHub开源项目terrastruct/d2,该项目在GitHub有超过10.3kStar,用一句话介绍该项目就是:...

探秘 Web 水印技术(制作水印网站)

作者:fransli,腾讯PCG前端开发工程师Web水印技术在信息安全和版权保护等领域有着广泛的应用,对防止信息泄露或知识产品被侵犯有重要意义。水印根据可见性可分为可见水印和不可见水印(盲水印)...

不忍心卸载的五款神仙工具(不忍心卸载的五款神仙工具是什么)

001.效率工具uTools-装机必备的生产力工具集uTools是一款非常强大的可以装下几乎所有效率工具的电脑生产力工具集,目前拥有Windows、Mac和Linux三个版本。软件界面...

「SVG」飞花令!这份最高检工作报告“超有料”

原标题:【SVG】飞花令!这份最高检工作报告“超有料”栏目主编:秦红文字编辑:沈佳灵来源:作者:最高人民检察院...

svg|2025政府工作报告,有没有你关心的数据?

··<setattributeName="visibility"begin="click+0s"dur="1ms"fill="freeze"restart="never"to="hi...

videoscribe只能输入英文,如何输入中文文本?

videoscribe只能输入英文,如何输入中文文本?打开VideoScribe软件,打开要添加中文字体的位置。打开Photoshop并在文件中创建一个新的透明背景图层。注意:必须是透明背景层。...

五个流行的SVG在线编辑器(svg编辑工具)

随着响应网络的发展,越来越多的高质量的SVG在线编辑器被公众所熟知。SVG矢量图形也越来越受欢迎,以便在任何设备上呈现图像,甚至一些易于使用的SVG在线编辑器,可以替代PS,本文总结了五种流行的SVG...