页面加载后在 React 中加载第三方 iframe,使 iframe 不影响 PageSpeed 得分

Load third party iframe in React after page load, so that the iframe does not affect PageSpeed score

我有一个 iframe 加载第三方小部件。我只想在我的页面加载后显示此 iframe,因为我不想减慢我的页面加载速度。我遵循了描述如何执行此操作的 medium article,但他们的解决方案不起作用,因为 onload 函数 finishLoading 从未被调用

export default ({src, width, height}) => {

  const [loading, stillLoading] = useState(true)
  const finishLoading = () => {
      alert('finished loading')
      stillLoading(false)
  }
  ...
  return(
    {loading ? '' : 
      <iframe
        src={src}
        width={width}
        height={height}
        scrolling="no"
        onLoad={finishLoading}
      >
        className={`tpwidget flex-center-row`}>
      </iframe>
    }
  )
}

更新

通过使用 useEffect,我可以让 iframe 在其他一切之后加载(理论上),但我发现删除 iframe 完全提高了我的 PageSpeed 分数,而仅在(使用 useEffect)之后加载 iframe 没有对 PageSpeed 有很大的积极影响。


如果有帮助,域是 suddenlysask.com,第三方小部件是亚马逊广告。

你必须使用 useEffect() 钩子。

useEffect(() => {
  stillLoading(false)
}, [])

在这种情况下,stillLoading(false) 将仅在安装组件后应用。所以,最后你的条件将在加载页面后起作用。

iframe 从不加载!

React 不会首先挂载 iframe,因为它是有条件地挂载的 (loading),而且条件永远不会改变,因为它取决于挂载的 iframe。

您可以在 React 外部加载 iframe 并将其附加到 useEffect 挂钩中:

export default ({src, width, height}) => {

    const container = useRef()
    useEffect(() => {
        const frm = document.createElement('iframe')
        frm.src = src
        frm.width = width
        frm.height = height
        frm.scrolling = 'no'
        container.current.appendChild(frm)
    }, [])

    return(
        <div ref={container} />
    )
}

话虽这么说,我不能告诉你这是否会加快页面加载速度,毕竟 iframe 加载外部内容。但你可以试一试。

更新

我访问了该网站,我确定您正在使用 Gatsby。 Gatsby 正在使用 SSR,React.lazy 和 Suspense 尚不可用于服务器端渲染。如果您想在服务器呈现的应用程序中进行代码拆分,Loadable Components is recommended in React docs. It has a nice guide for bundle splitting with server-side rendering.

有一个 gatsby 插件可以让你的生活更轻松 gatsby-plugin-loadable-components-ssr。安装和配置插件后,您可以像这样使用 loadable

AmazonFrame.js

import React from "react";

const AmazonFrame = ({ src, width, height }) => (
  <iframe src={src} width={width} height={height} scrolling="no"></iframe>
);

App.js

import React from "react";
import loadable from "@loadable/component";

const AmazonFrame = loadable(() => import("./AmazonFrame"), {
  fallback: <div>Loading...</div>
});

function App() {
  return (
    <div>
      <AmazonFrame src="src" width="100%" height="200px" />
    </div>
  );
}

export default App;

import React from "react";
import loadable from "@loadable/component";

const AmazonFrame = loadable(() => import("./AmazonFrame"));

function App() {
  return (
    <div>
      <AmazonFrame fallback={<div>Loading...</div>} />
    </div>
  );
}

export default App;

原回答

您需要使用 Code-Splitting。 Code-Splitting 是 Webpack、Rollup 和 Browserify(通过 factor-bundle)等捆绑器支持的一项功能,它可以创建多个可以在运行时动态加载的捆绑包。

如果您正在使用 Create React App,这已经为您配置好了,您可以立即开始使用。

对您的应用进行代码拆分可以帮助您仅“延迟加载”用户当前需要的内容,从而显着提高应用的性能。虽然您没有减少应用程序中的代码总量,但您避免了加载用户可能永远不需要的代码,并减少了初始加载期间所需的代码量。

这是您的问题的示例解决方案,它将延迟加载亚马逊广告 iframe,因此它不会与您的初始捆绑包一起加载:

AmazonFrame.js

import React from "react";

const AmazonFrame = ({ src, width, height }) => (
  <iframe src={src} width={width} height={height} scrolling="no"></iframe>
);

export default AmazonFrame;

App.js

import React, { Suspense, lazy } from "react";

// React.lazy takes a function that must call a dynamic import(). This must return a Promise
// which resolves to a module with a default export containing a React component.
const AmazonFrame = lazy(() => import("./AmazonFrame"));
function App() {
  return (
    <div>
      {/* The lazy component should then be rendered inside a Suspense component, which allows us to show some fallback
       content (such as a loading indicator) while we’re waiting for the lazy component to load */}
      {/* The fallback prop accepts any React elements that you want to render while waiting for the component to load */}
      <Suspense fallback={<div>Loading...</div>}>
        <AmazonFrame src="src" width="100%" height="200px" />
      </Suspense>
    </div>
  );
}

export default App;

您可以使用 window.onload 函数在运行时加载 iframe。

function loadDeferredIframe() {
    // this function will load the Google homepage into the iframe
    var iframe = document.getElementById("my-deferred-iframe");
    iframe.src = "./" // here goes your url
};
window.onload = loadDeferredIframe;

首先,您可以让一个 iframe 元素指向空白,并且可以在运行时更改 src。

<iframe id="my-deferred-iframe" src="about:blank" />

Google PageSpeed/Lighthouse 在评估性能时不只是分析从服务器发送的网站静态内容。由于此处演示的大多数技术都会尽快加载 iframe(即一旦调用 useEffect 挂钩),您最终会在页面速度测量期间招致 CPU 和网络请求期间。

您可以通过以下几种方法减少此 iframe 对您的分数的影响。很难提前说出影响是什么,因为它取决于 connection/network 和服务器性能——您需要进行测试和试验以获得最佳结果。

  1. 在 iframe 上使用 loading="lazy"。这将指示浏览器在它靠近视口之前不要加载它(例如,如果它在折叠下方,则在用户向下滚动之前根本不会加载)。
  2. useEffect 中设置超时以将 iframe 的加载延迟固定的持续时间,从而降低在页面速度测量期间发生这种情况的可能性 window。例如。:
    const [ready, setReady] = useState(false)
    useEffect(() => { setTimeout(() => { setReady(true) }, 3000) }, [])
    return (<iframe src={ready ? "/your/url" : "about:blank"} loading="lazy" />)
    
  3. 与 #2 类似,仅监听页面上的交互事件(例如滚动或点击),并且仅在该事件发生后才加载广告。

最近添加了一个改进了 iframe 性能的新属性。它被称为loading and it can have 2 values indicating how the browser should load the iframe: eager (load the iframe immediately) and lazy (Defer loading of the iframe until it reaches a calculated distance from the viewport). So, it will be something like: <iframe src={src} loading="eager"/> Read more about it here