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

IT技术 javascript reactjs iframe gatsby pagespeed
2021-05-02 16:33:55

我有一个iframe加载第三方小部件的工具。我只想iframe在我的页面加载后显示这个,因为我不想减慢我的页面加载速度。我遵循了一篇描述如何执行此操作中等文章,但他们的解决方案不起作用,因为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 上。


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

4个回答

更新

我访问了该网站,我确定您正在使用 Gatsby。Gatsby 正在使用 SSR,而 React.lazy 和 Suspense 尚不可用于服务器端渲染。如果您想在服务器渲染的应用程序中进行代码拆分,建议使用React docs 中的Loadable Components它有一个很好的指南,用于使用服务器端渲染进行包拆分

有一个 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;

原答案

您需要使用代码拆分代码拆分是 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;

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

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

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

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 会加载外部内容。但你可以试一试。

你必须使用useEffect()钩子。

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

在这种情况下,stillLoading(false)只有在安装组件后才会应用。因此,最后您的条件将在加载页面后起作用。