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

谷歌性能主管最新的有关LCP的文章

cac55 2024-09-20 12:52 24 浏览 0 评论

原文地址:philipwalton.com/articles/dy…

动态LCP优先级:根据历史调整

2022年12月28日

年初,chrome新增Priority Hints API,允许开发者在img、script和link等元素上设置fetchpriority="high"以保证优先加载。我个人非常认可这个api。我认为如果网站使用这个api,能最快速、最便捷的提高LCP。

不过也有小弊端,为了使用该api,就必须提前知道页面的LCP是哪个元素。

对于绝大多数内容都是静态内容的网站,LCP元素很容易知道。但是对于复杂网站,特别是有很多动态的、个性化甚至是UGC内容,可能就很难提前知道LCP元素是哪一个。

知道LCP元素最可靠的办法便是真实加载页面,然后查看通过LCP的api查看。但在这时,元素已经渲染到屏幕上了,再添加fetchpriority已经为时已晚。

真的如此吗?

尽管对于当前访问者,添加属性可能已经晚了,但是对于下一个访问者,是不一样的。难的是从上一个用户收集LCP数据,然后利用数据为未来做加载优化。尽管这听起来很复杂,但通过以下几个简单步骤,还是可以实现的:

  1. 在每个页面执行js脚本,检测LCP元素,发送到服务器
  2. 服务器存储LCP元素数据,便于将来参考
  3. 针对页面访问,检查页面是否有LCP元素,如有则对该元素添加fetchpriority属性。

尽管实现方法很多,我目前知道最好的是使用Cloudflare Workers, the Worker KV datastore和Worker HTMLRewriter API,目前都有免费的方法。

本文我将详细介绍。此外为了方便,我也把代码简化做了例子,但如果想看完整方案,可以参照github链接

Step 1: 识别LCP元素发送到服务器

为了根据访问数据动态设置LCP元素的优先级别,第一步应该是判断LCP元素,标识该元素后在随后的用户访问中匹配该元素。

使用web-vitals 识别LCP元素很容易,而且第3版本还包含一个 属性,能够暴漏所有LCP信息,下面便是一个例子。

// Import from the attribution build.
import {onLCP} from 'web-vitals/attribution';

// Then register a callback to run after LCP.
onLCP(({attribution}) => {
  // If the LCP element is an image, send a request to the `/lcp-data`
  // endpoint containing the page's URL path and LCP element selector.
  if (attribution.lcpEntry?.element?.tagName.toLowerCase() === 'img') {
    navigator.sendBeacon(
      '/lcp-data',
      JSON.stringify({
        url: location.pathname,
        selector: attribution.element,
      })
    );
  }
});
复制代码

上面的代码例子中,attribution.element值是css选择器能够标识LCP元素。比如,在下面My Challenge to the Web Performance Community的页面中,通常将发送以下信息:

{
  "url": "/articles/my-challenge-to-the-web-performance-community/",
  "selector": "#post>div.entry-content>figure>a>img"
}
复制代码

注意,通常会被发送,不是100%发送。因为根据屏幕尺寸,当前页面最大元素不一定总是该图片。比如,下图就展示了在传统PC和移动端不同视口下最常见的LCP元素。

如上图所示,PC端最大可见元素通常是图片,但在移动端通常就是第一段文本。

讲到这里需要强调一点,正如代码例子展示的,虽然同一个页面在不同用户可能LCP元素也可能是不同的,所以任何动态LCP方案都需要考虑这一点,我也会在文章后面讲解我是如何处理这个问题的。

Step 2: 存储 LCP 数据以便将来参考

第一步中的代码将当前页面的LCP数据发送到 /lcp-data端口,下一步是创建数据接收的处理方法并存储数据,以便将来访问使用。

因为我在网站上使用cloudflare worker,对我来说最好的方式是使用KV存储。KV 存储是key/value数据结构,可以同步到cloudflare的边缘节点上,也正是因为如此,所以读取也非常快,不过在更新数据时,不能立刻同步到所有边缘节点上,所以数据可能不是最新的。

在我这个案例中,数据不是最新完全不是问题,因为该功能主要在于提升性能,任何的请求延迟都可能达不到初衷,同时,因为Priority hints严格来讲是锦上添花,即便LCP并非实时也不影响。

为了使用cloudfalre kv 存储的边缘节点,首先需要创建存储然后设置binding,设置完成后,便能读写存储做简单的.get() 和 .put()操作了:

// Read from the store.
const myValue = await store.get('my-key');

// Write to the store.
await store.put('my-key', 'Updated value...');
复制代码

在我的LCP存储上,我希望能根据用户请求的页面来查询LCP元素选择器,所以key是页面url,value是该url的LCP元素选择器。

需要记住的是,因为LCP元素在不同的屏幕设备上可能也不相同,就需要根据在lcp数据上加上设备类型。为了判断设备类型(mobile或pc),我根据 ?sec-ch-ua-mobile在请求时的header来判断 。尽管这一请求头只在chromium为基础的浏览器才兼容,要注意本身priority hints这个api也是如此,所以这个案例中已经足够用了。

参照上面内容,pc端的kv 键值对如下:

Key

desktop:/articles/my-challenge-to-the-web-performance-community/

Value

#post>div.entry-content>figure>a>img

移动端的数据大概如下:

Key

mobile:/articles/my-challenge-to-the-web-performance-community/

Value

#post>div.entry-content>p

以下是完整的存储LCP数据的worker代码:

export default {
  async fetch(request, env) {
    if (url.endsWith('/lcp-data') && request.method === 'POST') {
      return storePriorityHints(request, env.PRIORITY_HINTS);
    }
  },
};

async function storePriorityHints(request, store) {
  const {url, selector} = await request.json();

  // Determine if the visitor is on mobile or desktop via UA client hints.
  const device =
    request.headers.get('sec-ch-ua-mobile') === '?1' ? 'mobile' : 'desktop';

  // The key is the device joined with the URL path. If the LCP element
  // can vary by more than just the device, more granularity can be added.
  const key = `${device}:${url}`;

  // If the new selector is different from the old selector, update it.
  const storedSelector = await store.get(key);
  if (selector !== storedSelector) {
    await store.put(key, selector);
  }

  // Return a 200 once successful.
  return new Response();
}
复制代码

下面解释一下上述代码为什么这么写:

  1. export fetch函数处理逻辑,包括检查请求url的请求方式(post)同时满足接口路径(/lcp-data)
  2. 如果都满足,调用storePriorityHint方法根据PRIORITY_HINTS的kv数据存储。
  3. storePriorityHint会提取url和选择器的值,同时根据请求头是sec-ch-ua-mobile确定设备类型
  4. 随后检查KV存储利用key查找LCP的元素选择器并用key将设备和url关联。
  5. 如果能找到选择器,或者已存储的选择器与当前json内容不同,就会更新选择器。

Step 3: 添加匹配fetchpriority数据到将来的请求上

每个页面和设备的LCP数据存储后,就能够在给未来访问中动态的给img元素添加fetchpriority属性。

我之前提到使用HTMLRewriter能实现,因为能便捷使用selector-based API 来重写HTML,轻松找到之前存储的正好符合选择器的img元素。

逻辑如下:

  1. 对于每个页面的请求,根据页面url、sec-ch-ua-mobile请求头来判定当前页面LCP数据的key,然后再KV存储中查找该数据。
  2. 同时,请求当前页面的HTML
  3. 如果当前页面/设备存储了LCP数据,则创建 HTMLRewriter实例,并找到匹配的元素选择器添加fetchpriority 属性。
  4. 如果未存储LCP数据,则按照正常返回页面

以下是代码:

export default {
  async fetch(request, env) {
    // If the request is to the `/lcp-data` endpoint, add it to the KV store.
    if (url.endsWith('/lcp-data') && request.method === 'POST') {
      return storePriorityHints(request, env.PRIORITY_HINTS);
    }
    // For all other requests use the stored LCP data to add the
    // `fetchpriority` attribute to matching <img> elements on the page.
    return addPriorityHintsToResponse(request, env.PRIORITY_HINTS);
  },
};

async function addPriorityHintsToResponse(request, store) {
  const urlPath = new URL(request.url).pathname;
  const device =
    request.headers.get('sec-ch-ua-mobile') === '?1' ? 'mobile' : 'desktop';

  const hintKey = `${device}:${encodeURIComponent(urlPath)}`;

  const [response, hintSelector] = await Promise.all([
    fetch(request),
    store.get(hintKey),
  ]);

  // If a stored selector is found for this page/device, apply it.
  if (hintSelector) {
    return new HTMLRewriter()
      .on(hintSelector, new PriorityHintsHandler())
      .transform(response);
  }
  return response;
}

class PriorityHintsHandler {
  #applied = false;
  element(element) {
    // Only apply the `fetchpriority` attribute to the first matching element.
    if (!this.#applied) {
      element.setAttribute('fetchpriority', 'high');
      this.#applied = true;
    }
  }
}
复制代码

可以通过在pc端访问网站页面 查看例子,应该能看到第一章图片使用了fetchpriority="high"属性,需要注意的是源代码中没有该属性,这个属性只有在之前用户上报该图片是LCP元素才会生效,你在访问时可能就能看到。

必须着重说明一下,该属性是被添加到了HTML上,而不是使用客户端js加载的。可以通过curl请求页面html源代码,在相应的html文件中应该能看到fetchpriority属性。

curl https://philipwalton.com/articles/my-challenge-to-the-web-performance-community/
复制代码

同时,源代码中没有这个属性,是cloudflare根据之前的访问情况添加的。

重要提醒

我认为大型网站在这上面能获益良多,但是有一些重要提醒需要关注一下。

首先,在上面的策略中,页面LCP元素需要在HTML源码中能找到,换言之,LCP元素不能是js动态引入的,也不能是通过data-src而非src等常见技术:

<img data-src="image.jpg" class="lazyload" />
复制代码

In addition to the fact that it’s always a bad idea to lazy load your LCP element, any time you use JavaScript to load images, it won’t work with the declarative fetchpriority attribute.

总是懒加载LCP元素可能是很糟糕的方法,所以任何时候使用js加载图片,使用fetchpriority这个属性其实无效。

此外,如果访问者不同,lcp元素变动不大,则使用这种方法收益最大。如果访问者不同,LCP元素不同,则一个访问者的LCP元素则不会匹配下一个访问者。

如果网站是这种情况,你可能就需要在LCP数据key中添加user ID;但是这种情况只在用户频繁反复访问网站的情况才值得。如果不是这种情况,可能不会有收益。

验证技术是否有效

同任何性能方案一样,观测影响和验证技术是否有效也非常重要。其中一个验证的方式是测量特定网站结果的精确度,也就是说,对于动态加载 fetchpriority的情况,有多少更正了元素。

可以使用下面代码验证:

import {onLCP} from 'web-vitals/attribution';

onLCP((metric) => {
  let dynamicPriority = null;

  const {lcpEntry} = metric.attribution;

  // If the LCP element is an image, check to see if a different element
  // on the page was given the `fetchpriority` attribute.
  if (lcpEntry?.url && lcpEntry.element?.tagName.toLowerCase() === 'img') {
    const elementWithPriority = document.querySelector('[fetchpriority]');
    if (elementWithPriority) {
      dynamicPriority =
        elementWithPriority === lcpEntry.element ? 'hit' : 'miss';
    }
  }
  // Log whether the dynamic priority logic was a hit, miss, or not set.
  console.log('Dynamic priority:', dynamicPriority);
});
复制代码

如果曾动态加载fetchpriority,一旦页面出现fetchpriority属性,但非LCP元素,则添加错误。

你可以使用 “hit rate”验证匹配逻辑的有效情况,如果在一段时间内缺失很大,则有可能需要调整或取消。

总结

如果读者感觉这一技术有用,可以考虑尝试或者分享给你认为可能有用的人。我希望像cloudflare一样的CDN商能够自动使用这个技术,或者作为一项用户选可配置的特性。

此外,希望本文能对用户在LCP这方面有所启发,能了解LCP这种动态指标的实质是非常依靠用户的行为。虽然永远提前知道LCP元素不太可能,但是性能技巧在一定程度上能够有所收益,也需要适当的调整。


作者:Yestodorrow
链接:https://juejin.cn/post/7213636024101716029
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关推荐

无力吐槽的自动续费(你被自动续费困扰过吗?)

今天因为工作需要,需要在百度文库上下载一篇文章。没办法,确实需要也有必要,只能老老实实的按要求买了个VIP。过去在百度文库上有过类似经历,当时为了写论文买了一个月的VIP,后面也没有太注意,直到第二个...

百度文库推出“文源计划”创作者可一键认领文档

11月7日,百度文库发布了旨在保护创作者权益的“文源计划”。所谓“文源计划”,即为每一篇文档找到源头,让创作者享受更多的权益。据百度文库总经理李小婉介绍,文源计划分为三部分,分别是版权认证、版权扶持和...

有开放大学学号的同学,百度文库高校版可以用了。

还在网上找百度文库的下载方式,只要从身边的朋友在读开放大学的,那他(她)的学号就可以登陆到国家开放大学图书馆,还使用百度文库高校版来下载。与百度文库稍有不同,但足够使用了。现转国图链接如下:htt...

搜索资源方法推荐(搜索资源的方法)

今天msgbox就要教大家如何又快又准的搜到各类资源,第一点,排除干扰百度搜索出来啊经常前排展示它的产品以及百度文库,如何去除呢?很简单,后面输入空格减号百度文库,比如你搜高等数学百度文库很多,只要后...

一行代码搞定百度文库VIP功能(2021百度文库vip账号密码共享)

百度文库作为大家常用查资料找文档的平台,大多数文档我们都可以直接在百度文库找到,然而百度文库也有让人头痛的时候。好不容易找到一篇合适的文档,当你准备复制的时候他却提示你需要开通VIP才能复制~~~下载...

百度文库文档批量上传工具用户说明书

百度文库文档批量上传工具用户说明书1、软件主要功能1、批量上传文档到百度文库,支持上传到收费、VIP专享、优享以及共享。2、支持自动分类和自动获取标签3、支持多用户切换,一个账户传满可以切换到...

百度文库现在都看不到文档是否上传成功,要凉了吗?

打开知识店铺,百度文库文档里显示都是下载这一按键,上传的文档也看不到是否成功?咋情况,要取消了吗?没通过审核的也不让你删除,是几个意思,想通吃吗?现在百度上传文档也很费劲,有时弄了半天的资料上传审核过...

微信推广引流108式:利用百度文库长期分享软文引流

百度文库相对于百度知道、百度百科来说,操作上没那么多条条框框,规则上也相对好把握些。做一条百度知道所花费的精力一般都会比做一条百度文库的要多些,老马个人操作下来觉得百度文库更好把握。但见仁见智吧,今天...

职场“避雷”指南 百度文库推出标准化劳动合同范本

轰轰烈烈的毕业季结束了,众多应届生在经过了“职场海选”后,已正式成为职场生力军的一员。这一阶段,除了熟悉业务,签订劳动合同、了解职场福利也迅速被提上日程。而随着国人法律意识的增强,百度文库内《劳动合同...

《百度文库》:素材精选宝库(百度文库官网首页)

《百度文库》:独特功能助力选择高质量素材在当今信息爆炸的时代,如何高效地获取并利用有价值的素材成为了许多人面临的挑战。而《百度文库》作为百度公司推出的一款在线文档分享平台,凭借其丰富的资源、强大的功能...

深度整合和开放AI能力 百度文库和网盘推出内容操作系统「沧舟OS」

【TechWeb】4月25日消息,Create2025百度AI开发者大会上,百度文库和百度网盘推出全球首个内容操作系统——沧舟OS。基于沧舟OS,百度文库APP全新上线「GenFlow超能搭子」...

女子发现大二作业被百度文库要求付费下载,律师:平台侵权,应赔偿

近日,28岁的黎女士在百度百科搜索家乡的小地名时,发现了自己在大二完成的课题作业。她继续搜索,发现多个平台收录了该文,比如豆丁网和文档之家等,有的还设置了付费或积分下载。2月15日,九派新闻记者以用户...

2016杀入百度文库的新捷径,只有少数人才知道的喔

百度的产品在SEO优化中的分量真不用多说,其实很多人都像我一样一直在找捷径。但是我经常发现很多人都是在用死方法。比如发贴吧发帖而不知道去申请一个吧主,知道自问自答而不知道去申请一个合作资格。口碑和贴吧...

百度文库付费文档搜索方法(百度文库付费文档搜索方法有哪些)

一直以来,百度文库中无论是个人中心还是个人主页,都没有像淘宝一样的店内搜索功能,连最近新开的知识店铺也没有设计店内搜索功能,这无论是对上传用户还是下载用户都不方便,上传用户想要搜索自己的文档无法办到...

供读者免费使用!泰达图书馆机构版百度文库新年上新啦

在泰达图书馆读者使用百度文库数字资源不需要VIP,免-费-用!惊不惊喜?快来了解一下吧……新年伊始,为满足区域企业、高校、科研院所以及居民群众在教学、科研及学习过程中,对各类文献资源的需求,泰达图书馆...

取消回复欢迎 发表评论: