Speed Up Gatsby Performance

∥☆過路亽.° 提交于 2021-02-05 08:01:11

问题


I was given a task at work to increase the performance of my project. Currently, the Google Lighthouse score fluctuates but overall it's not that great of a score so we are trying to figure out how to increase its performance to be able to show off to our leadership.

Our project loads the entirety of the Gatsby site as a single JavaScript bundle. This creates a single page application from the site, which allows new pages to load quickly via JavaScript. But with something as large as our WordPress sites, this produces a very large bundle in the megabytes. This large bundle significantly slows page speed.

I'm not exactly sure on how to tackle offloading this bundle.js but I've found an interesting doc on the subject https://www.gatsbyjs.org/docs/how-code-splitting-works/

While I don't completely understand these docs yet I believe that I edit this async-requires.js file to include multiple export component lines and this should result in multiple javascript bundles instead of the main large one. Perhaps if there are multiple js bundles the site will load faster because it is not just bottle necked by one. So a page can load in the specific bundle it needs to render and async load the one it doesn't need.

Below is some of the code that I think relates to the task at hand. I'm still a bit of a beginner when it comes to gatsby so I'm not exactly sure what I can change here to allow for better performance.

Thank you for the help.

async-requires.js

const preferDefault = m => m && m.default || m

exports.components = {
  "component---src-templates-page-js": () => import("../src/templates/page.js" /* webpackChunkName: "component---src-templates-page-js" */),
  "component---cache-dev-404-page-js": () => import("dev-404-page.js" /* webpackChunkName: "component---cache-dev-404-page-js" */),
  "component---src-pages-404-js": () => import("../src/pages/404.js" /* webpackChunkName: "component---src-pages-404-js" */)
}

src/templates/pages.js

import React from 'react'
import PropTypes from 'prop-types'
import Helmet from 'react-helmet'
import Layout from '../layouts/layout'
import AnalyticsContext, { analyticsEvents } from '../../util/AnalyticsContext'

import Banner from '../WPComponents/Banner'
import CheckmarkList from '../WPComponents/CheckmarkList'
import CopyGrid from '../WPComponents/CopyGrid'
import Drawers from '../WPComponents/Drawers'
import Explainers from '../WPComponents/Explainers'
import Featured from '../WPComponents/Featured'
import Form from '../WPComponents/Form'
import Hero from '../WPComponents/Hero'
import Pricing from '../WPComponents/Pricing'
import PromoApp from '../WPComponents/PromoApp'
import PromoCircles from '../WPComponents/PromoCircles'
import PromoSlider from '../WPComponents/PromoSlider'
import ReachAnimation from '../WPComponents/ReachAnimation'
import Resources from '../WPComponents/Resources'
import SimpleExplainer from '../WPComponents/SimpleExplainer'
import SimpleMedia from '../WPComponents/SimpleMedia'
import Solution from '../WPComponents/Solution'
import Testimonials from '../WPComponents/Testimonials'
import Disclaimer from '../WPComponents/Disclaimer'

const PageTemplate = props => {
  const { pageContext, data, location } = props
  const components = (pageContext.acf && pageContext.acf.section_page) || []
  let helmet
  const { yoast } = pageContext

  if (yoast) {
    const {
      title,
      metadesc,
      opengraph_title,
      opengraph_description,
      opengraph_image,
      canonical,
    } = yoast

    helmet = (
      <Helmet
        title={title || ' '}
        meta={[
          {
            name: 'robots',
            content: 'noindex',
          },
          {
            name: 'description',
            content: metadesc || ' ',
          },
          {
            property: 'og:title',
            content: opengraph_title || ' ',
          },
          { property: 'og:site_name', content: title || ' ' },
          { property: 'og:type', content: 'website' },
          {
            property: 'og:description',
            content: opengraph_description || ' ',
          },
          {
            property: 'og:image',
            content: opengraph_image && opengraph_image.source_url,
          },
          canonical
            ? {
                property: 'og:url',
                content: canonical || ' ',
              }
            : {},
        ]}
      />
    )
  }

  return (
    <AnalyticsContext.Provider
      value={{
        ...analyticsEvents,
      }}
    >
      <Layout location={location}>
        {helmet}
        {components.map(component => {
          switch (component.__typename) {
            case 'WordPressAcf_hero':
              return <Hero key={component.id} {...component} />
            case 'WordPressAcf_featured':
              return <Featured key={component.id} {...component} />
            case 'WordPressAcf_solution':
              return <Solution key={component.id} {...component} />
            case 'WordPressAcf_resources':
              return <Resources key={component.id} {...component} />
            case 'WordPressAcf_simplemedia':
              return <SimpleMedia key={component.id} {...component} />
            case 'WordPressAcf_promoapp':
              return <PromoApp key={component.id} {...component} />
            case 'WordPressAcf_reach_animation':
              return <ReachAnimation key={component.id} {...component} />
            case 'WordPressAcf_promoslider':
              return <PromoSlider key={component.id} {...component} />
            case 'WordPressAcf_promocircles':
              return <PromoCircles key={component.id} {...component} />
            case 'WordPressAcf_testimonials':
              return <Testimonials key={component.id} {...component} />
            case 'WordPressAcf_banner':
              return <Banner key={component.id} {...component} />
            case 'WordPressAcf_explainers':
              return <Explainers key={component.id} {...component} />
            case 'WordPressAcf_copygrid':
              return <CopyGrid key={component.id} {...component} />
            case 'WordPressAcf_drawers':
              return <Drawers key={component.id} {...component} />
            case 'WordPressAcf_simpleexplainer':
              return <SimpleExplainer key={component.id} {...component} />
            case 'WordPressAcf_disclaimer':
              return <Disclaimer key={component.id} {...component} />
            case 'WordPressAcf_pricing':
              return (
                <Pricing key={component.id} {...component} /> 
              )
            case 'WordPressAcf_checkmarklist':
              return <CheckmarkList key={component.id} {...component} />
            case 'WordPressAcf_form':
              return <Form key={component.id} {...component} />
            default:
              console.log('Could not recongize type:', component.__typename)
              return
          }
        })}
      </Layout>
    </AnalyticsContext.Provider>
  )
}

PageTemplate.propTypes = {
  pageContext: PropTypes.shape({
    acf: PropTypes.object,
    media: PropTypes.shape({
      edges: PropTypes.array,
    }),
  }),
}

export default PageTemplate

pageCreators.js

const path = require('path')
const genericPageTemplate = 'src/templates/page.js'

const pageCreator = templatePath => (actions, pageContext) => {

  actions.createPage({
    component: path.resolve(templatePath),
    path: pageContext.pagePath,
    context: {
      ...pageContext,
    },
  })
}

module.exports = {
  createGenericPage: pageCreator(genericPageTemplate),
}

createPages.js

const { createGenericPage } = require('./pageCreators')

const generatePages = allWordpressPage => {
  return allWordpressPage.edges.map(edge => edge.node)
}

module.exports = (data, actions) => {
  if (!data) {
    console.error('createPages()', 'Error', '`data` is undefined')
    throw new Error('Error retrieving data: data is undefined')
  }

  const { allWordpressPage } = data

  const pages = allWordpressPage && generatePages(allWordpressPage)

  if (!pages) {
    console.error(
      'createPages()',
      'Error',
      'Could not build pages. allWordpressPage was falsy'
    )
    throw new Error('Error retreiving data: allWordpressPage was falsy')
  }

  pages &&
    pages.forEach(page => {

      // skip the 'modules' page
      if (page.pagePath === '/modules/') {
        return;
      }

      createGenericPage(actions, page)
    })
}

gatsby-node.js

/**
 * Implement Gatsby's Node APIs in this file.
 *
 * See: https://www.gatsbyjs.org/docs/node-apis/
 */

const fs = require('fs')

const queryAll = require('./util/queryAll')
const createPages = require('./util/createPages')

exports.createPages = ({ graphql, actions }) => {
  return graphql(queryAll)
    .then(res => {
      if (res.errors) {
        res.errors.forEach(error => {
          console.error('Error:', error.message)
        })
      }

      createPages(res.data, actions)
    })
    .catch(error => {
      console.error('failed to create pages:', { error })
    })
}

exports.sourceNodes = ({ actions, schema }) => {
  const { createTypes } = actions

  const additionalTypeDefs = fs.readFileSync(`type-defs.gql`, {
    encoding: `utf-8`,
  })
  createTypes(additionalTypeDefs)
}

// temporary fix for dev env: https://github.com/gatsbyjs/gatsby/issues/11934#issuecomment-469046186
exports.onCreateWebpackConfig = ({ getConfig, stage }) => {
  const config = getConfig()
  if (stage.startsWith('develop') && config.resolve) {
    config.resolve.alias = {
      ...config.resolve.alias,
      'react-dom': '@hot-loader/react-dom',
    }
  }
}

回答1:


I looked into this a lot about 4 months ago and this is what I found, but some of the reasons below were due to bugs in how lighthouse determined pagespeed on Gatsby websites, so a few may not true anymore (for example, using fadeIn={false} and loading="eager" on images, and using a tags instead of Link from gatsby-link. Please leave a comment or edit if one of these tips is no longer true.


  • using gatsby-plugin-preact (big and easy change)

  • using <a> tags instead of gatsby-link

  • using gatsby-plugin-purge-css (removes all you unused CSS. Useful if you're using a CSS framework like bootstrap)

  • using fadeIn={false} and loading="eager" on Gatsby Images, or setting the duration of the fade in to be lower: durationFadeIn={250}

  • Preconnecting to certain 3rd party sites with gatsby-plugin-preconnect

  • If you have a background image, split it into 2 images, one for above the fold and one for below the fold (your initial view of the page has to load less at the beginning)

  • Optimizing my above the fold images manually before letting gatsby optimize them. This was a website I found helpful for doing that, but you may be able to find a good open source software for that.

  • Loading third party iframes after only after the user scrolls past them. Ex:

       ...
       const ref = useRef()
       const onScreen = useOnScreen(ref, '0px')
       let showWidget
       if (onScreen){
           showWidget = true
       }
       ...
       return (
           <div ref={ref}>
               {showWidget && <ThirdPartyIframe /> }
           </div>
       )
    

Other tips I've read about include

  • Using inline styling (although I've heard Gatsby does this automatically)

  • Using a 301 redirect instead of 307 (If this applies to you)

  • Not using Typography.js

  • Possibly using an S3 & Cloudfront and not Netlify to host the website


RESOURCES TO READ FURTHER

I created a reddit post where I posted something similar, I'd recommend reading the comments underneath. It references this github thread which is pretty popular, and I found this post to be the most helpful on the thread.

As well, here's a number of questions I posted related to increasing the lighthouse score for Gatsby projects. You shouldn't need them with the information listed above, but maybe they'll be useful or you'll learn something from them

  • Largest contententful paint (LCP) on lighthouse is a p tag. (Using gatsby)
  • Pagespeed insights avoid chaining critical request only lists Initial navigation followed by my domain twice
  • Reduce initial server response time with Netlify and Gatsby
  • Largest contentful paint is one big gatsby-background-image and very slow, PageSpeed Insights tells me to preconnect third-party origins and then tells me they are not used by the browser after preconnecting
  • PageSpeed Insights diagnostic “Ensure text remains visible during webfont load”
  • Load third party iframe in React after page load, so that the iframe does not affect PageSpeed score


来源:https://stackoverflow.com/questions/60065177/speed-up-gatsby-performance

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!