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
- Create a new folder
For example, learn-nextjs-example is initialized first
npm i -y
Copy the code
- Dependency packages required to install
npm i react react-dom next --save
Copy the code
- 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
- 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 creating
pages/about.js
, then the address to access the page ishttp://localhost:3000/about
; - Such as creating
pages/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
- 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
- On the front page
pages/index.js
Write 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, butmoment
The 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 ~