18

I have a website that I'm trying to optimize lighthouse page speed ranking. I just switched from SSR with nginx caching to next export using exportPathMap and getInitialProps (also with nginx caching).

Specific page of interest gets heavy traffic

After switching to static, the first content image appears loads in 2-2.5s for "slow 3G". However JS execution time takes like 3-6 seconds.

enter image description here

Questions:

  1. Why does script evaluation take 3-6s when I am using a static export, I was under the impression it would be quite fast?

  2. Are there ways to optimize nextjs JS execution time? Or maybe a webpack setting?

5
  • When I open that page with javascript disabled I do get all the content, scripts are loaded and executed before the user can interact with the page but all static html content seems to be there on initial load.
    – HMR
    Commented May 12, 2020 at 10:13
  • Maybe trying lazy import some modules that you know that could be really big so your users can have a lower script evaluation. I would also suggest some audits through profiling dev tools.
    – lndgalante
    Commented May 13, 2020 at 9:34
  • I assume this includes all the additional ad serving scripts right? Commented May 13, 2020 at 18:23
  • Yes this includes the ad serving scripts, those are going to slow the page, not much I can do about it. @HMR I did add lazy loading on everything but the rewriter section and it didn't make any different, I think it's just the rewriter section logic. I'll see if I can lazy load parts of it, not sure if lazy loading is supported for static export, worth looking into. Commented May 14, 2020 at 21:01
  • Want to try using lazyload?
    – Ozik Jarwo
    Commented May 15, 2020 at 2:37

2 Answers 2

13
+50

Try wrapping some heavy modules like this:

import dynamic from 'next/dynamic';
import { Spinner } from './spinner';

const MyLazyLoadedHeavyComponent = dynamic(() => import('./my-heavy-component'), {
  ssr: false,
  loading: () => <Spinner />
});

export const MyQuicklyLoadingPage: FC = () => {
  return (
    <div>
      <h1>Welcome to the page!</h1>
      <p>You'll see this text</p>
      <p>Before we load the heavy stuff below</p>
      <p>Large markdown files containing lots of images, etc.</p>
      <MyLazyLoadedHeavyComponent />
    </div>
  );
};

I also use this sometimes for modules that can't be rendered with SSR.

Also, evaluate whether something like attempting to use Preact will improve performance. I don't know how easy to do that is with nextJS. I found this https://github.com/developit/nextjs-preact-demo

6
  • 3
    Hey Charley, I accepted since the bounty only has 30 minutes left, but it doesn't actually help me too much. I've actually used this dynamic set up without success on 90% of the application, i'm going to attempt to use it on the 10% of the heavier part and see what happens. As for preact, I have considered, but I have some complex hook logic in some locations that I don't believe is supported from preact, specifically on one page. My pages are completely static and the logic is only run when you're using a text input area which shouldn't be too heavy. Any other suggestions are appreciated :) Commented May 15, 2020 at 14:37
  • Saddly I tried to improve it with this, and nothing :( I set most of the page to ssr false with next export without success :( It did reduce my JS size, but time didn't change much. Commented May 19, 2020 at 3:32
  • @KevinDanikowski did you find any solution for this issue?
    – MK Patel
    Commented May 19, 2021 at 9:39
  • @Mayank-Dolphin nope :/ I implemented static export without success. I haven't tried useStaticProps yet which I plan to try in the future. Commented May 20, 2021 at 1:31
  • 1
    @Steve Its the loading state component. I renamed <PreDynamicState /> to <Spinner /> to hopefully be more clear. Commented Feb 22, 2023 at 22:01
1

Have you tried this suggestion by Next.js? I think it's exactly your case.

<input
  type="text"
  placeholder="Country search..."
  className={styles.input}
  onChange={async e => {
    const { value } = e.currentTarget
    // Dynamically load libraries
    const Fuse = (await import('fuse.js')).default
    const _ = (await import('lodash')).default

    const fuse = new Fuse(countries, {
      keys: ['name'],
      threshold: 0.3
    })

    const searchResult = fuse.search(value).map(result => result.item)

    const updatedResults = searchResult.length ? searchResult : countries
    setResults(updatedResults)

    // Fake analytics hit
    console.info({
      searchedAt: _.now()
    })
  }}
/>
2
  • 1
    what's the difference between this and next Dynamic, seems to be the same intentions except that Fuse can be used even later Commented Apr 5, 2022 at 15:13
  • Next Dynamic slicing allows you to segment React components, the await import() inside a function allows the code inside the import to go into a separate bundle. webpack.js.org/guides/code-splitting Commented Mar 7 at 14:35

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.