preface

Now the latest version has reached 9.5.3. Some oF the apis are a little different from before. Most of the tutorials on the Internet are from the old version V7, so I plan to write a simple tutorial to take you through the door in absolute detail.

Library version

The key library versions used in this case are as follows:

"Next", "^ 9.5.2" and "react" : "^ 16.13.1",Copy the code

Node version 12.18.1 (node version >= 10.13)

Initialize the Next. Js project

  1. Create a new folder

For example, learn-nextjs-example is initialized first

npm i -y
Copy the code
  1. Dependency packages required to install
npm i react react-dom next --save
Copy the code
  1. Add script command

In order to develop a faster input NPM command, so the commonly used commands as shortcut commands

"scripts": {
  "dev": "next",
  "build": "next build",
  "start": "next start"
},
Copy the code
  1. Create a Pages folder and run the first page

The Pages folder is where pages are stored, and the files in this folder automatically generate routes.

  • Such as creatingpages/about.js, then the address to access the page ishttp://localhost:3000/about;
  • Such as creatingpages/about/about.js, then the access address ishttp://localhost:3000/about/about.

Now we create pages/index.js, which by default represents the root path

const Home = () = > {
  return <div>Hello Next.js!</div>
}

export default Home
Copy the code

At this time to run

npm rum dev
Copy the code

The React syntax can be used directly without importing import React from ‘React’. Of course you won’t make an error if you do, but you don’t have to.

Routing hop

Page jumps can take two forms, one using the tag Link, and the other is a programmatic route to router.push(‘/’). The redirects are still very much like React, but with the next package

Link

  1. Start by creating two pages

pages/about.js

const About = () = > {
  return <div>About Page</div>
}

export default About
Copy the code

pages/news.js

const News = () = > {
  return <div>News Page</div>
}

export default News
Copy the code
  1. On the front pagepages/index.jsWrite routing jump links
import Link from 'next/link'

const Home = () = > {
  return (
    <div>
      <div>Hello Next.js!</div>
      <div>
        <Link href='/about'>
          <a>about</a>
        </Link>
      </div>
      <div>
        <Link href='/news'>
          <a>news</a>
        </Link>
      </div>
    </div>)}export default Home
Copy the code

The important thing to note here is that the a tag doesn’t have to add the href element, and it won’t work if it does. If the Link tag is replaced by another tag, such as span, it will still be able to jump to the corresponding page. You can know that Link adds the click binding event to the element inside, instead of just adding href to the a tag.

DOM structure:

Also note that there can only be one root element in Link, otherwise it will not know who to bind to.

Programmatic routing jumps

The main jump here is the useRouter hook

Modify pages/index.js to modify a route

import Link from 'next/link'
import { useRouter } from 'next/router'

const Home = () = > {
  const router = useRouter()
  const gotoAbout = () = > {
    router.push('/news')}return (
    <div>
      <div>Hello Next.js!</div>
      <div>
        <Link href='/about'>
          <a>about</a>
        </Link>
      </div>
      <div>
        <button onClick={gotoAbout}>news</button>
      </div>
    </div>)}export default Home
Copy the code

Routing and the cords

The parameter is passed as query

In next.js, only query (? Name =jackylin), cannot pass arguments as (path:name). Modify the pages/index. Js

import Link from 'next/link'
import { useRouter } from 'next/router'

const Home = () = > {
  const router = useRouter()
  const gotoAbout = () = > {
    router.push('/news')}return (
    <div>
      <div>Hello Next.js!</div>
      <div>
        <Link href='/about? name=jackylin'>
          <a>about</a>
        </Link>
      </div>
      <div>
        <button onClick={gotoAbout}>news</button>
      </div>
    </div>)}export default Home
Copy the code

Or you could do it this way

<Link href={{ pathname: '/about', query: { name: 'jackylin' } }}>
Copy the code

After passing the parameter name, the next step is to receive the parameter.

Open Pages /about.js to add code

import { withRouter } from 'next/router'

const About = ({ router }) = > {
  return (
    <div>
      <div>About Page</div>
      <div>The received parameter is {router.query.name}.</div>
    </div>)}export default withRouter(About)
Copy the code

Click about to jump to the About page. The page content is:

The About Page parameter is JackylinCopy the code

At the same time also can see the address bar with parameters: http://localhost:3000/about? name=jackylin

Programmatic routing jumps pass parameters

Modify the gotoAbout method in pages/index.js

const gotoAbout = () = > {
  router.push({
    pathname: '/news'.query: {
      info: 'learning Next. Js,}})}Copy the code

pages/news.js

import { withRouter } from 'next/router'

const News = ({ router }) = > {
  return (
    <div>
      <div>News Page</div>
      <div>The received parameter is {router.query.info}.</div>
    </div>)}export default withRouter(News)
Copy the code

A hook for routing changes

Here is not a detailed expansion, direct code to speak:

import { useEffect } from 'react'
import Link from 'next/link'
import { useRouter } from 'next/router'

const Home = () = > {
  const router = useRouter()
  const gotoAbout = () = > {
    router.push({
      pathname: '/news'.query: {
        info: 'learning Next. Js,
      },
    })
  }

  useEffect(() = > {
    const handleRouteChangeStart = url= > {
      console.log('routeChangeStart Route starts to change, URL :', url)
    }

    const handleRouteChangeComplete = url= > {
      console.log('routeChangeComplete End of route change, URL :', url)
    }

    const handleBeforeHistoryChange = url= > {
      console.log('beforeHistoryChange fires before changing browser history, URL :', url)
    }

    const handleRouteChangeError = (err, url) = > {
      console.log('routeChangeError' jumps to error:${err}, url:${url}`)}const handleHashChangeStart = url= > {
      console.log('hashChangeStart Hash route mode jump starts when executed, URL :', url)
    }

    const handleHashChangeComplete = url= > {
      console.log('hashChangeComplete Hash route mode jump is complete when url:', url)
    }

    router.events.on('routeChangeStart', handleRouteChangeStart)
    router.events.on('routeChangeComplete', handleRouteChangeComplete)
    router.events.on('beforeHistoryChange', handleBeforeHistoryChange)
    router.events.on('routeChangeError', handleRouteChangeError)
    router.events.on('hashChangeStart', handleHashChangeStart)
    router.events.on('hashChangeComplete', handleHashChangeComplete)

    return () = > {
      router.events.off('routeChangeStart', handleRouteChangeStart)
      router.events.off('routeChangeComplete', handleRouteChangeComplete)
      router.events.off('beforeHistoryChange', handleBeforeHistoryChange)
      router.events.off('routeChangeError', handleRouteChangeError)
      router.events.off('hashChangeStart', handleHashChangeStart)
      router.events.off('hashChangeComplete', handleHashChangeComplete)
    }
  }, [])

  return (
    <div>
      <div>Hello Next.js!</div>
      <div>
        <Link href='/about? name=jackylin'>
          <a>about</a>
        </Link>
      </div>
      <div>
        <button onClick={gotoAbout}>news</button>
      </div>
    </div>)}export default Home
Copy the code

Style-jsx writes page CSS styles

We create a header component and import pages/index.js, creating components/header.js under the root directory

const Header = () = > {
  return (
    <div>
      <div className='header-bar'>Header</div>
    </div>)}export default Header
Copy the code

Pages /index.js imports the Header component

import Header from '.. /components/header'

const Home = () = > {
  return (
    <div>
      <Header />
    </div>)}Copy the code

Next, you style the Header. Next. Js has built-in support for the Style JSX syntax, which is one of the csS-in-JS solutions

const Header = () = > {
  return (
    <div>
      <div className='header-bar'>Header</div>
      <style jsx>
        {`
          .header-bar {
            width: 100%;
            height: 50px;
            line-height: 50px;
            background: lightblue;
            text-align: center;
          }
        `}
      </style>
    </div>)}export default Header
Copy the code

Run style in effect

If you look at the DOM structure, you’ll see that next.js automatically adds a random class name (JsX-XXXXxxx) to prevent global CSS contamination.

Add a global CSS file

Create a folder public to store static files such as images and CSS files. Note that only the directory named public can store static resources and provide external access. New file public/static/styles/common CSS

body {
  margin: 0;
  padding: 0;
  font-size: 16px;
}
Copy the code
  • Custom App

Next. Js uses the App component to initialize the page, and we can override the App component to control the page initialization. The functions of this App are generally as follows:

  • Keep the layout persistent between page transitions
  • Keep state while switching pages
  • Custom error handling with componentDidCatch
  • Inject extra data into the page
  • Adding a Global CSS

All we need to do is add global CSS styles, so we need to override the original default App by creating pages/_app.js first

import '.. /public/static/styles/common.css'

/** * Component refers to the current page, which is updated each time the route is switched. * pageProps is an object with an initial property preloaded into your page by one of our data getters, otherwise it would be an empty object */
export default function MyApp({ Component, pageProps }) {
  return <Component {. pageProps} / >
}
Copy the code

Restart the service, running, global style added successfully.

Integrated style – components

If we want the project to support style-components, we need to configure it ourselves. We rewrite the code by custom Document, that is, the _document.js file, which will only be called during server-side rendering, mainly used to modify the Document content of server-side rendering, generally used with third party CSS-in-JS scheme.

  • Install required libraries
npm i styled-components --save
Copy the code

Compile styled – components

npm i babel-plugin-styled-components --save-dev
Copy the code

New file. Babelrc

{
  "presets": ["next/babel"],
  "plugins": [["styled-components", { "ssr": true }]]
}
Copy the code

Create _document.js and rewrite it to override it. The important thing to notice here is that you have to inherit Document to rewrite it

import Document from 'next/document'
import { ServerStyleSheet } from 'styled-components'

export default class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const sheet = new ServerStyleSheet()
    const originalRenderPage = ctx.renderPage
    try {
      ctx.renderPage = () = >
        originalRenderPage({
          enhanceApp: App= > props= > sheet.collectStyles(<App {. props} / >})),const initialProps = await Document.getInitialProps(ctx)

      return {
        ...initialProps,
        styles: (
          <>
            {initialProps.styles}
            {sheet.getStyleElement()}
          </>),}}finally {
      sheet.seal()
    }
  }
}
Copy the code

The details of the configuration can be found in the official documentation: next.js

Next we add the style using styled- Components, opening pages/about.js

import { withRouter } from 'next/router'
import styled from 'styled-components'

const Title = styled.h1` color: green; `

const About = ({ router }) = > {
  return (
    <div>
      <Title>About Page</Title>
      <div>The received parameter is {router.query.name}.</div>
    </div>)}export default withRouter(About)
Copy the code

Run, as shown below:

The way data is obtained

All four apis can only be used in files in the Pages folder

getStaticProps

When page content depends on external data

Next. Js recommends that we use static generation whenever possible, because all pages can be built once and hosted to the CDN, which is much faster than having the server render pages on every page request.

For example, some of the following types of pages:

  • Marketing page
  • Personal blog
  • Product list
  • Static documents

If a page uses static generation (SSG), the corresponding HTML file for the page is generated at build time. HTML files will be reused on each page request and can also be cached by the CDN. This one, like Hexo Gatsby, generates static files and json files.

As for Gatsby, it is based on the upper framework of React. For details, you can read my blog: Take you to Gatsby by Hand

Next. Js will automatically optimize the application and output static HTML as much as possible. This will only be disabled if you add the getInitialProps method to your page component, as described below.

For example, we can execute commands to view

npm run build
Copy the code

The default packaging mode is SSG:

● (SSG) automatically generated as static HTML + JSON (uses getStaticProps)

Will find.next/server/pages/There are a few more HTML files below, and these are the HTML files that are statically constructed.

Create a new file called pages/blog.js

const Blog = ({ posts }) = > {
  return <div>title: {posts.title}</div>
}

// This function is called at build time
export async function getStaticProps() {
  // Call the external API to get the content
  const res = await fetch('https://jsonplaceholder.typicode.com/todos/1')
  const posts = await res.json()

  // The 'posts' argument will be received at build time
  return {
    props: {
      posts,
    },
  }
}

export default Blog
Copy the code

Go to http://localhost:3000/blog, found that the page can get normal to the request results:

title: delectus aut autem
Copy the code

getStaticPaths

When the path of the page depends on external data

This way, and the above is some different with http://localhost:3000/blog access path when we already wrote a js page file, but if we want to according to obtain the data dynamically generated article path, it needs to use the API, This API is usually used in conjunction with getStaticProps.

Pages /posts/[id].js

With id identifies a single post, such as you visit http://localhost:3000/posts/1 you show id of 1.

The code is as follows:

const Post = ({ post }) = > {
  return (
    <div>
      <div>The article id: {post. Id}</div>
      <div>{post.title}</div>
    </div>)}// Build the route
export async function getStaticPaths() {
  // Call the external API to get the list of blog posts
  const res = await fetch('https://jsonplaceholder.typicode.com/todos')
  const posts = await res.json()

  // Generate all pre-rendered paths from the list of posts
  const paths = posts.map(post= > `/posts/${post.id}`)

  // Fallback is false, which means that any path not in getStaticPaths will result in a 404 page.
  return { paths, fallback: false}}// Get single page blog data
export async function getStaticProps({ params }) {
  // If the route is /posts/1, then params.id is 1
  const res = await fetch(`https://jsonplaceholder.typicode.com/todos/${params.id}`)
  const post = await res.json()

  // Pass the data of the blog post to the page using the props parameter
  return { props: { post } }
}

export default Post
Copy the code

Pull down the data of 200, all that is dynamically constructed 200 routing, now we visit the 50th posts, enter http://localhost:3000/posts/50 directly

Page display:

Article ID: 50 Blog title: Cupiditate necessitatibus Ullam aut Quis dolor VoluptateCopy the code

Let’s pack again, execute NPM run build (error if the package is likely to be poor network and request link, because have to request a lot of pages, if it is network problem just try a few times more), you’ll find the packing time longer, because to build more static pages, we request to return to the 200 data, So you should generate 200 static HTML files and corresponding JSON data files.

Let’s look at the 1. Json file, which is the requested data

{"pageProps":{"post":{"userId":1,"id":1,"title":"delectus aut autem","completed":false}},"__N_SSG":true}
Copy the code

getServerSideProps

The HTML of the page is regenerated each time the page is requested

If the page cannot be pre-rendered before the user requests it, the above “static generation” may not be appropriate, or the page data may need to be updated frequently and the page content may change with each request. At this point, there are two options:

  • Use “static generation” with client-side rendering: You can skip pre-rendering parts of the page and fill them with client-side JavaScript
  • Use server-side rendering: Next-.js to pre-render for each page request. Because the CDN cannot cache the page, it will be slower, but the pre-rendered page will always be up to date.

Because server-side rendering results in slower performance than “static generation,” use this feature only when absolutely necessary.

Create a new file called pages/server-blog.js

const Blog = ({ posts }) = > {
  return <div>title: {posts.title}</div>
}

// Runs on every page request but not at build time.
To set up a page to use server-side rendering, export the getServerSideProps function
export async function getServerSideProps() {
  // Call the external API to get the content
  const res = await fetch('https://jsonplaceholder.typicode.com/todos/1')
  const posts = await res.json()

  // The 'posts' argument will be received at build time
  return {
    props: {
      posts,
    },
  }
}

export default Blog
Copy the code

When we run NPM run build, we find that the corresponding HTML file — server-blog.html is not generated this time, because we specified server side rendering, it will not be executed when the request is built, it is not generated statically.

getInitialProps

GetInitialProps is an API that will run before the page is rendered. If the request is contained under the path, the request is executed and the required data is passed to the page as props. When the direct page is accessed for the first time, getInitialProps is run on the server side. After loading, the page is hosted by the client, using the client route to jump, and then the getInitialProps of the page is executed on the client side.

Recommended: getStaticProps or getServerSideProps. If you are using Next. Js version 9.3 or later, we recommend that you use getStaticProps or getServerSideProps instead of getInitialProps. These new ways of getting data allow you to have fine control between static generation and server-side rendering.

Create a new file called pages/initial-blog.js

import Link from 'next/link'

const InitialBlog = ({ post }) = > {
  return (
    <div>
      <h1>getInitialProps Demo Page</h1>
      <div>{post.title}</div>
      <Link href='/'>
        <a>Return to the home page</a>
      </Link>
    </div>
  )
}

InitialBlog.getInitialProps = async ctx => {
  const res = await fetch('https://jsonplaceholder.typicode.com/todos/1')
  const post = await res.json()
  return { post }
}

export default InitialBlog
Copy the code

Then modify the pages/index.js code, add a Link tag can jump back to the page

<div>
  <Link href='/initial-blog'>
    <a>Enter the Initial Blog</a>
  </Link>
</div>
Copy the code

Then directly address bar enter http://localhost:3000/initial-blog, before rendering the page content, first performs getInitialProps method, the result of the execution of the page components to again, begin to render a page. Since we are visiting the page for the first time, the first screen is rendered for the server.

The page content is as follows:

GetInitialProps Demo Page Obtained title: delectus aut AutemCopy the code

Open the Network panel of your browser and find the request whose Name is initial-blog. You will see that the HTML of the request response is the same as that of the page.

Click to return to the home page, and then click to enter Initial Blog from the home page to re-enter the page. It is noted that at this time, the page is no longer accessed by our browser through the address bar, but is accessed through the front-end route.

If you look at the request from the Network panel, you can see that the data requested by getInitialProps is returned instead of the HTML page content, indicating that the page has been rendered by the client. This is called SSR rendering. The first screen is rendered and returned to the server, and the page jump is rendered by the client.

The custom Head

I believe that a very important reason for choosing Next. Js is to use SEO to facilitate crawler retrieval. One way is to write TDK (title, description, keyword) in the page, and Next

Modify components/header.js to add code to the outermost div root element

<Head>
  <title>Next. Js tutorial -- JackyLin</title>
  <meta name='viewport' content='initial - scale = 1.0, width = device - width' />
</Head>
Copy the code

If you open http://localhost:3000/, you will see that the page header tag title has been changed.

LazyLoding implements lazy loading of modules/components

Lazy loading refers to loading the JS file of a component/module only when it is needed or should be loaded. It is also called asynchronous loading.

If the content of the page file is too large, it may be slow to open for the first time, which requires optimization. Lazy loading is one of the ways.

Lazy loading module

Start by installing a library that handles dates and times

npm i moment --save
Copy the code

New pages/time. Js

import { useState } from 'react'
import moment from 'moment'

const Time = () = > {
  const getTime = () = > {
    setNowTime(moment().format('YYYY-MM-DD HH:mm:ss'))}const [nowTime, setNowTime] = useState(' ')
  return (
    <div>
      <button onClick={getTime}>Get the current time</button>
      <div>Current time: {nowTime}</div>
    </div>)}export default Time
Copy the code

Let’s say we have a lot of content on this page, and then you might not have to click to get the time, butmomentThe module still loads, which is a bit of a waste of resources.

So we want to load the moment module when we click the button, asynchronously, rather than as soon as the page comes in.

Click the button to get the time, and you can see the Network panel and see that the moment code is packaged into a 1.js file, which enables lazy loading. This reduces the size of the main JavaScript package and leads to faster loading.

import { useState } from 'react'

const Time = () = > {
  const getTime = async() = > {const moment = await import('moment')
    setNowTime(moment.default().format('YYYY-MM-DD HH:mm:ss')) // You can't use moment() as default
  }
  const getTime = () = > {
    setNowTime(moment().format('YYYY-MM-DD HH:mm:ss'))}const [nowTime, setNowTime] = useState(' ')
  return (
    <div>
      <button onClick={getTime}>Get the current time</button>
      <div>Current time: {nowTime}</div>
    </div>)}export default Time
Copy the code

Lazily loaded component

Lazy-loading components require the introduction of the next/ Dynamic module

Create a new component components/ Content.js

const Content = () = > {
  return <div>content</div>
}

export default Content
Copy the code

Introducing the pages/time. Js

import { useState } from 'react'
import dynamic from 'next/dynamic'

// Lazy loading of custom components
const Content = dynamic(() = > import('.. /components/content'))

const Time = () = > {
  const getTime = async() = > {const moment = await import('moment')
    setNowTime(moment.default().format('YYYY-MM-DD HH:mm:ss')) // You can't use moment() as default
  }
  const [nowTime, setNowTime] = useState(' ')
  return (
    <div>
      <button onClick={getTime}>Get the current time</button>
      <div>Current time: {nowTime}</div>
      <Content />
    </div>)}export default Time
Copy the code

Looking at Network, you can see that the Content component is newly packaged separately into a JS file

Packaging production environment

perform

npm run build
Copy the code

Once the packaging is complete, run the server

npm run start
Copy the code

Then view the page, packaging success!

This tutorial code address: Take you through the NextJS code

Reference:

  • Server side rendering Styled-Components with NextJS


  • Ps: Personal technical blog Github warehouse, if you feel good welcome star, give me a little encouragement to continue writing ~