Author: Yi Heng

preface

When you open the website of Nex.js, the first thing you see is its Slogan and the introduction:

The React Framework for Production

Next.js gives you the best developer experience with all the features you need for production: hybrid static & server rendering, TypeScript support, smart bundling, route pre-fetching, and more. No config needed.

Next. Js provides all the features and best practices required for a production environment, including build-time prerendering, server-side rendering, route preloading, intelligent packaging, zero configuration, and more. Among them, Nex.js has become one of the most popular frameworks in the React ecosystem with its excellent built-time rendering and server-side rendering capabilities. In this article, we will introduce the three pre-render modes provided by Next. Js and the mixed render mode to see how Next. Js does pre-render.

Three pre-render modes

Ordinary single-page application only has one HTML, and the HTML returned by the first request does not contain any page content. Therefore, the JS bundle needs to be requested through the network and rendered. The whole rendering process is completed on the client side, so it is called client Side Rendering (CSR). Although this rendering method is very fast in subsequent pages, there are two obvious problems:

  1. Too much white screen time: The page will remain blank until the JS bundle returns. If the bundle is too large or the network is not good, the experience will be even worse
  2. SEO is not friendly: when the search engine visits the page, it will only look at the CONTENT in THE HTML. By default, it will not execute JS, so it can not grab the specific content of the page

The three pre-rendering modes provided by Next. Js all pre-render the page content on the server side before the CSR starts, so as to avoid the problems of too long white screen time and SEO unfriendly.

SSR

To solve these two problems, SSR(Server Side Rendering) was created. SSR is a direct real-time isomorphic rendering of the page that the current user visits on the server, and the returned HTML contains the specific content of the page, improving the user experience. React provides support directly from the framework level, simply calling the renderToString(Component) function to get HTML content.

Nex.js provides the getServerSideProps asynchronous function to get additional data in the SSR scene and return it to the component for rendering. GetServerSideProps gets the Context of each request. For example:

export default function FirstPost(props) {
  // Get the data in props
  const { title } = props;
  return (
    <Layout>
      <h1>{title}</h1>
    </Layout>)}export async function getServerSideProps(context) {
  console.log('context', context.req);
  // Simulate the data
  const title = await getTitle(context.req);
  // Return the data in the props object
  return {
    props: {
      title
    }
  }
}
 
Copy the code

While the SSR solution solves two of the problems of CSR, it also introduces another problem: the need for a server to host real-time requests, rendering, and responses to pages, which undoubtedly increases the cost of server-side development and maintenance. In addition, for some relatively static scenes, such as blogs, official websites, etc., their content is relatively certain, change is not frequent, each time through the server side rendering out the content is the same, undoubtedly a lot of unnecessary server resources are wasted. Is there a way to make these pages static? This is where static site generation (SSG, also known as pre-render at build time) is born.

SSG

SSG(Static Site Generation) refers to the pre-rendered page at application compile build time and generates Static HTML. After the generated HTML static resources are deployed to the server, the browser can not only request the HTML with the page content for the first time, but also do not need the server to render and respond in real time, which greatly saves the server operation and maintenance costs and resources.

Next. Js turns SSG on for each page by default. For scenarios where the content of a page depends on static data, it is possible to export an asynchronous function called getStaticProps per page, which collects and returns the data required by that page component. When the getStaticProps function is executed, the page component can retrieve the data from the props and perform static rendering. Here is an example of using SSG in static routing:

// pages/posts/first-post.js
function Post(props) {
	const { postData } = props;
  
  return <div>{postData.title}</div>
}

export async function getStaticProps() {
  // Simulate to get static data
	const postData = await getPostData();
  return {
  	props: { postData }
  }
}
Copy the code

How does nex.js do SSG for dynamic routing scenarios? Next. Js provides the getStaticPaths asynchronous function. In this method, it returns a Paths array containing the page data that the dynamic route needs to pre-render at build time. Here’s an example:

// pages/posts/[id].js
function Post(props) {
	const { postData } = props;
  
  return <div>{postData.title}</div>
}

export async function getStaticPaths() {
  // Return the page data that this dynamic route might render, such as params.id
  const paths = [
    {
      params: { id: 'ssg-ssr'}}, {params: { id: 'pre-rendering'}}]return {
    paths,
    // Matches a route that has not yet generated a static page and returns a 404 page
    fallback: false}}export async function getStaticProps({ params }) {
  // Use params.id to get the corresponding static data
  const postData = await getPostData(params.id)
  return {
    props: {
      postData
    }
  }
}
Copy the code

When we execute the nextjs build, we can see that the package consists of two HTML pages, pre-rendering. HTML and SG-ssr.html, which means that when we execute SSG, It loops through the array of Paths returned by the getStaticPaths function, pre-rendering page components one by one and generating HTML.

├ ─ ─ server | ├ ─ ─ chunks | ├ ─ ─ pages | | ├ ─ ─ API | | ├ ─ ─ index. The HTML | | ├ ─ ─ index. The js | | ├ ─ ─ index. The json | | └ ─ ─ posts | | ├ ─ ─ [id]. Js | | ├ ─ ─ first - post. HTML | | ├ ─ ─ first - post. Js | | ├ ─ ─ the pre - rendering. # pre-rendered HTML generated pre - rendering pages | | ├ ─ ─ The pre - rendering. Json | | ├ ─ ─ SSG - SSR. # pre-rendered HTML generated SSG - SSR page | | └ ─ ─ SSG - SSR. JsonCopy the code

SSG is a good solution to the problem of white screen time and SEO unfriendly, but it is only suitable for more static page content scene, such as the official website, blogs, etc. It can seem a little helpless when the page data is updated frequently or the number of pages is large, since static builds don’t have the latest data and can’t enumerate a large number of pages. At this point, you need Incremental Static Regeneration.

ISR

Incremental Static Regeneration (ISR) from Next. Js allows you to regenerate the HTML of each page while the application is running, rather than having to rebuild the entire application. This way, you can use SSG features even if you have a lot of pages. Generally speaking, you need to use getStaticPaths and getStaticProps together to use an ISR. Here’s an example:

// pages/posts/[id].js
function Post(props) {
	const { postData } = props;
  
  return <div>{postData.title}</div>
}

export async function getStaticPaths() {
  const paths = await fetch('https://... /posts');
  return {
    paths,
    // The page request degradation strategy, in this case, does not degrade, wait for the page generated before returning, similar to SSR
    fallback: 'blocking'}}export async function getStaticProps({ params }) {
  // Use params.id to get the corresponding static data
  const postData = await getPostData(params.id)
  return {
    props: {
      postData
    },
    // Enable ISR to regenerate pages every 10 seconds at most
    revalidate: 10,}}Copy the code

During the build phase of the application compilation, the identified static pages are generated, consistent with the SSG execution process above.

Add a revalidate property to the object returned by getStaticProps to enable the ISR function. In the example above, specifying revalidate = 10 means that the static HTML will be regenerated at most 10 seconds. When a browser requests a page that has been rendered at build time, it will first return cached HTML. After 10 seconds, the page will be rerendered. After the page is successfully generated, the cache will be updated and the browser will get the latest rendered page content when it requests the page again.

Static HTML is generated immediately when a browser requests a page that was not generated at build time. During this process, the fallback field returned by getStaticPaths has the following options:

  • fallback: 'blocking': does not degrade and requires user requests to wait until the static generation of a new page is completed, which is cached
  • fallback: true: demote, return the demoted page first, when the static page is generated, it will return a JSON for the CSR of the demoted page to use, after the second rendering, the complete page is out

In the example above, the fallback scheme is used (‘blocking’), which is actually similar to the SSR scheme in that it blocks the render, but with more caching.

If fallback is ‘blocking’, new paths not returned by getStaticPaths will wait for the HTML to be generated, identical to SSR (hence why blocking), and then be cached for future requests so it only happens once per path.

Not all scenarios are suitable for using ISR. For scenes with high real-time requirements, such as news and information websites, SSR may be the best choice.

Mixed render mode

Next. Js not only supports SSR, SSG, CSR, ISR, but also supports mixed use of rendering modes. Three hybrid rendering modes are described below.

SSR + CSR

As mentioned above, SSR seems to have solved the problem of CSR, but is THERE no place for CSR at all? It’s not. With CSR, page switches do not need to be refreshed, and the entire HTML content does not need to be rerequested. In this case, we can take advantage of each other’s strengths and make up for each other’s weaknesses, so we have the SSR + CSR scheme:

  • First loading page to go SSR: to ensure the first screen loading speed at the same time, and meet the demands of SEO
  • Page switch to go CSR: Next. Js will initiate a network request, executegetServerSidePropsFunction to render the page after it returns the data

The organic combination of the two can greatly reduce the pressure and cost of the back-end server, but also improve the speed of page switching, and further improve the user experience. In addition to Nex.js, there are other frameworks that also use SSR + CSR schemes, such as ice-js and so on.

SSG + CSR

As mentioned above, SSR requires high server operation and maintenance costs. For some static websites or websites with low real-time requirements, it is not necessary to use SSR. If SSG is used instead of SSR and SSG + CSR scheme is used, will it be better?

  • Static content SSG: For more static content in a page, such as navigation bars, layouts, etc., static HTML can be pre-rendered at build time
  • Dynamic content goes CSR: Generally will be inuseEffectRequest the interface to get dynamic data, and then re-render the page

Although in terms of experience, dynamic content can appear only after re-rendering the page, which is not as good as SSR experience, while avoiding the high server cost brought by SSR, it can also ensure that the first screen rendering time is not too long. Compared with pure CSR, it still improves user experience.

SSG + SSR

As mentioned in the ISR scheme introduced above, the essence of ISR is SSG + SSR:

  • Static content go SSG: A relatively static page is pre-rendered to generate HTML at build time, and static HTML is returned when the browser requests it
  • Dynamic content goes SSR: The browser requests an unpre-rendered page, generates the page through SSR rendering at run time, then returns it to the browser and caches the static HTML, and returns it directly the next time the cache is hit

Compared with SSG + CSR, ISR allows dynamic content to be released directly, further improving the experience of first visiting a page. Reduce unnecessary static page rendering and save some back end server costs compared to SSR + CSR.

conclusion

This article begins with a look at the three pre-render modes offered by Next. Js: SSR, SSG, and ISR, and explains the pros and cons of each and what scenarios they might be suitable for. The Next section describes the three hybrid rendering modes currently supported by Nex.js and compares them to other rendering modes.

In general, there is no perfect rendering scheme, which requires trade-offs and trade-offs according to the actual scene.

Reference links:

  • Next. Js official documentation
  • Next. Js: SSR Support for Enterprise-level Frameworks
  • What are SSR, SSG, ISR and DPR Doing?