preface

Next. Js is a lightweight React server rendering application framework. With Next. Js, you can optimize SEO, speed up first screen loading…

Initialize the project

Create the Next. Js project manually

  • New Folder

    mkdir nextDemo
    cd nextDemo
    npm init
    Copy the code
  • Installing dependency packages

    yarn add react react-dom next
    # OR
    npm i -S next react react-dom
    Copy the code
  • Modify the package. The json

    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "dev" : "next" ,
        "build" : " next build",
        "start" : "next start"
    },
    Copy the code
  • Create a Pages folder and try to develop the home page code

    • Next specifies that a route is automatically created for files written to pages

    • Create a new index.js file and write the following code. Next will create/route automatically

      function Index(){ return ( <div>Hello World! </div> ) } export default IndexCopy the code
  • After previewing yarn Dev in the browser, you can access it in the corresponding port of the browser

Use create-next-app scaffolding

  • Ready to step

    npm i -g create-next-app
    npm i -g npx
    # OR
    yarn global add create-next-app
    yarn global add npx
    Copy the code
  • Create the Next. Js project

    NPX create-next-app next-demo # next-demo is the project name CD next-demo yarn dev # If successful, it can be accessed under port 3000Copy the code

Next. Js basic application

Pages Creating a route

Next will automatically register /frontend routes. If you want to create a new frontend route, you just need to adjust the directory structure under Pages

├ ─ ─ pages │ ├ ─ ─ blog # level routing │ | ├ ─ ─ firstblog. Js # 2 routing/blog/firstblog │ | ├ ─ ─ secondblog. Js routing/blog/secondblog # 2Copy the code

Routing based

Link label indicates route redirection

  • Create a new Pages/pagea.js demonstration

    Export default ()=>(<> <div> This is PageA </div> <Link href="/"><a> </a> < / a >)Copy the code
  • Note:

    1. The Link tag does not render anything by default, so the tag to render is also written inside the tag
    2. Link Label passinghrefProperty to jump
    3. The Link internal code does not allow sibling tags to be juxtaposed and must nest a layer of parent tags

Router module is used to programmatically route jumps

  • In the pages/index. Js demonstration

    Import Router from 'next/ Router 'export default ()=>(<> <div> This is Index </div> <button onClick={()=> Router.push('/pageA')}> </button> </>Copy the code

Routing and the cords

  • Note: In Next, you can only pass parameters as query, i.e.? Id =1, not /:id

    import React from 'react' import Link from 'next/link' import Router from 'next/router' const Index = () => { function gotoPageA(){ Router.push({ pathname: '/pageA', query: { id: 4 } }) } return( <> <div> This is Index </div> <div> <Link href="/pageA? id=1"><a>go to pageA, id is 1</a></Link> <Link href={{ pathname: '/pageA', query: {id: 2} }}><a>go to pageA, id is 2</a></Link> <button onClick={()=> Router.push('/pageA? id=3')}>go to pageA, id is 3</button> <button onClick={gotoPageA}>go to pageA, id is 4</button> </div> </> ) } export default IndexCopy the code
  • Receives routing parameters in the component

    Import {withRouter} from 'next/router' const pageA = ({router})=>{return () const pageA = ({router}) <> <div>This is pageA.</div> <div>id is {router.query.id}</div> </> ) } export default withRouter(pageA)Copy the code

Route map

In the example above, the route parameter id? Is passed in the form of query. Id =1, but in real development, it is more common to pass the path: ID as the parameter id, so that the URL looks more like real development, so we can use route mapping to solve this problem

  • Use of the AS attribute

    import Link from 'next/link' import Router from 'next/router' export default () => { gotoPageA = () => { // Router.push({pathName: '/pageA', query: {id: router.push ({pathName: '/pageA', query: {id: 2}}} '/ pageA / 2) return (< > {/ * as attributes are used to achieve routing map * /} < Link href = "/ pageA? id=1" as="/pageA/1"><a>go to pageA, id is 1</a></Link> <button onClick={gotoPageA}>go to pageA, id is 2</button> </> ) }Copy the code
  • Note: After the route mapping is processed using the AS attribute, the page URL will change accordingly. At this point, you can try to refresh the page and find a 404 access failure.

The reason for this is that the route is not a real route. The real route is passed as a query (/pageA? Id =1), and after the route mapping is used, the URL of the address bar becomes /pageA/1. After the page is refreshed, next will render the server, and the server will render the corresponding file according to the mapped route, but this file does not exist. For example, the server will look for the 1.js file in the Pages /pageA directory, but the file does not exist, so a 404 error will occur. Solution: Since the error is in the server rendering process, of course, in the server code to resolve. This will be discussed later.

Route hook event

Import Link from 'next/ Link 'import Router from 'next/ Router' // Const events = ['routeChangeStart', 'routeChangeComplete', 'routeChangeError', 'beforeHistoryChange', 'hashChangeStart', 'hashChangeComplete' ]; function makeEvent(type){ return (... args) => { console.log(type, ... args) } } events.forEach(event => { Router.events.on(event, makeEvent(event)); }); export default () => { return ( <> <Link href="/pageA"><a>go to pageA</a></Link> <Link href="#one"><a>go to part one</a></Link> <Link href="/404"><a>go to 404</a></Link> </> ) }Copy the code

getInitialProps

In real development, remote data is usually requested after the component is initialized. For example, vue mounted and React componentDidMount. The getInitialProps method is provided in next.js to get remote data, rather than in the component’s lifecycle. Example:

  • yarn add axios

  • import axios from 'axios' const pageA = ({ data })=>{ return ( <> <div>initial props: {data}</div> </> ) } pageA.getInitialProps = async () => { const {status, data} = await axios(url); If (status === 200){return {data} // as props. Data outgoing}} export default pageACopy the code
  • Class component

    import React from "react" class PageA extends React.Component { static getInitialProps = async () => { const {status, data} = await axios(url); If (status === 200){return {data} // as props. Data outgoing}}; render() { const { data } = this.props return ( <> <div>initial props: {data}</div> </> ) } } export default PageA;Copy the code
  • Note:

    1. getInitialPropsThe default argument passed is an object with the following attributes:

    For details, see github + pathname-URL + query – URL + asPath – route mapping. Path displayed in the browser (including the query part) + REq – HTTP request object (available only on the server) + RES – HTTP return object (available only on the server) + jsonPageRes – Data response object (available only on the client) + ERR – Any error objects during rendering 2. This method can be executed during both server-side and client-side rendering. + If the server performs the first screen rendering, it will execute getInitialProps of the route component and return the corresponding props parameter. After the component has been rendered, the server will return the props parameter. The client does not execute getInitialProps of the component again. When the client uses the Link tag or API method to jump to the routing component, the client executes getInitialProps of the corresponding routing component, and the server does not execute 3. GetInitialProps is called only for a routing component placed in the Pages directory. This method is invalid for child components

CSS page

useStyle JSXWriting page CSS

  • Next cannot reference CSS files by default and needs to be configured separately, but can be written using Style JSX

  • The sample

    export default () => { const fontColor = 'red' return ( <> <div className="test">This is pageA.</div> <div ClassName ="test2">This is pageA.</div> `}</style> <style jsx> {` .test { color: blue; } .test2 { color: ${fontColor}; } `} </style> </> ) }Copy the code
  • Note: After adding Style JSX code, Next will automatically generate a random class name jsX-xxxx to prevent CSS global contamination

Importing CSS files

For global styles in development, CSS files are often introduced instead of the

  • yarn add @zeit/next-css

  • New next. Config. Js

    const withCss = require('@Zeit/next-css'); if(typeof require ! == 'undefined'){ require.extensions['.css'] = file => {} } module.exports = withCss({})Copy the code
  • Create a new CSS file in the public or static directory. Import ‘.. /public/test.css’

Styled Components extension: Integration, styled components

Styled – Components is a library implemented based on Css In Js. You can use tag templates to style components, a popular React style scheme.

  • Yarn add style-components babel-plugin-styled- Components Copies the codeCopy the code
  • Edit babelrc.

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

    import styled from 'styled-components'
    
    const Title = styled.h1`
            color: yellow;
            font-size: 40px;
        `
    
    export default () => (
        <>
        	<Title>This is a page</Title>
        </>
    )
    Copy the code

Dynamically loading modules & Components (lazy loading)

Asynchronous loading module

  • yarn add moment

  • The sample

    import React, {useState} from 'react' export default () => {const [time,setTime] = useState(date.now ()) // Note async await to complete module loading Const handleChangeTime = async ()=>{const moment = await import('moment') // load moment module dynamically SetTime (moment.default(date.now ()).format()) // Use default} return (<> <div> current time :{time}</div> <button) OnClick ={handleChangeTime}> Change time format </button> </>)}Copy the code

    This can be observed in the console panel, where 1.js, the content of momNet.js, is loaded only when the button is clicked

Asynchronously loading components

  • Create a new component /one.js child

    Export default ()=><div>Lazy Loading Component</div> copies codeCopy the code
  • Used in the parent component

    Import React, {useState} from 'React' import dynamic from 'next/dynamic' const One = dynamic(import('.. Export default () => {const [time,setTime] = useState(date.now ()) // async await Const handleChangeTime = async ()=>{const moment = await import('moment') // load moment module dynamically SetTime (moment.default(date.now ()).format('YYYY-MM-DD'))) return (<> <div> current time :{time}</div> <button OnClick ={handleChangeTime}> Change time format </button> <One /> </>)}Copy the code

The custom Head

One of the reasons for using Next is to optimize SEO. For different pages, you sometimes need to customize the Head to better support SEO

  • The sample

    import Head from 'next/head'
    
    export default () => (
        <>
            <Head>
                <title>PageA</title>
                <meta charSet='utf-8' />
            </Head>
            <div>This is pageA</div>
        </> 
    )
    Copy the code

Custom App

In development, we sometimes need to customize Layout components and use them globally. Or keep some public state and pass some custom data to the page… This can be done in the pages/_app.js file

import App, {Container} from 'next/app' class MyApp extends App {// Get global data from app.getInitialprops. This method is called every time a page switch is performed Static async getInitialProps({Component, CTX}){// getInitialProps can skip implementation, which will execute the default method // After customizing app.getInitialprops You must determine if the page component has getInitialProps // If it does, you must execute it to get the returned parameters, Go to < Component > / / this page components to get the corresponding props let pageProps if (Component. GetInitialProps) {pageProps = await Component. GetInitialProps return (CTX)} {pageProps}} to render () {/ / Component of each page Component const {Component, pageProps } = this.props return ( <Container> <Component { ... pageProps } /> </Container> ) } } export default MyAppCopy the code

Integrate Koa with Next

Integration of Koa

  • yarn add koa

  • Create server.js in the root directory as the entry file to start next and KOA at the same time

    const Koa = require('koa'); const next = require('next'); Const dev = process.env.node_env! == 'production'; const app = next({ dev }); const handle = app.getRequestHandler(); // App.prepare ().then(() => {const server = new Koa(); Use (async (CTX, next) => {// ctx.req, ctx.res is node's native request/response object await handle(ctx.req, ctx.res); ctx.respond = false; }); server.listen(3000, () => { console.log('koa server listening on 3000'); }); });Copy the code
  • Modify the package. The json

    "scripts": {
        "dev": "nodemon server.js",
        "build": "next build",
        "start": "next start"
     }
    Copy the code

Resolve server render 404

As mentioned earlier, when the page is refreshed after routing mapping is used, a 404 condition may occur during server rendering. Here’s the solution

const Koa = require('koa'); const Router = require('koa-router'); const next = require('next'); const dev = process.env.NODE_ENV ! == 'production'; const app = next({ dev }); const handle = app.getRequestHandler(); app.prepare().then(() => { const server = new Koa(); const router = new Router(); / * - focus on the start - * / router. Get ('/a / : id, async (CTX) = > {const id = CTX. Params. Id; await handle(ctx.req, ctx.res, { pathname: '/a', query: { id } }); ctx.respond = false; }); / * - focus on the end -- * / server. Use (router) routes ()); server.use(async (ctx, next) => { await handle(ctx.req, ctx.res); ctx.respond = false; }); server.listen(3000, () => { console.log('koa server listening on 3000'); }); });Copy the code

4. Integrate Ant Design with Next

  • yarn add @zeit/next-css

  • Create next. Config. js to support CSS file import

    const withCss = require('@Zeit/next-css'); if(typeof require ! == 'undefined'){ require.extensions['.css'] = file => {} } module.exports = withCss({})Copy the code
  • Yarn add babel-plugin-import ANTd load as required

  • Create.babelrc in the root directory

    {
      "presets": ["next/babel"],
      "plugins": [
        [
          "import",
          {
            "libraryName": "antd"
          }
        ]
      ]
    }
    Copy the code
  • Introduce antD global CSS files

    Create 'pages/_app.js' import App from 'next/ App' import 'antd/dist/antd. CSS 'export default AppCopy the code

PM2 deploys the Next. Js application

Deployment online this piece is really nothing to say, simple words directly from a Node service can be, a bit more complex will include alarm restart and so on, all depends on personal situation.

The development environment

## Build yarn Build with webPack ## Build yarn Dev with webPack Start yarn Start (Start a Web server in production mode)Copy the code

The production environment

Tips: Because the production environment requires continuous application running, use the PM2 daemon to continuously deploy the front-end application

Pm2 --name: name of the application --interpreter: Pm2 start yarn --name "client" -- Interpreter bash -- start # Start script pm2 startupCopy the code

At this point, the front-end application is running with PM2, execute

pm2 list
Copy the code

Check the PM2 process status. If status is online, the PM2 process is in normal state. If status is in other states, the PM2 process is abnormal

Pm2 restart XXX pm2 restart XXXCopy the code

Assignment: PM2 common command

1. Global installation

npm install pm2 -g 
or 
yarn add pm2 -g
Copy the code

2. Pm2 starts

pm2 start xxx 
Copy the code

3. Pm2 View processes

Pm2 list pm2 show 1 or # pm2 info 1Copy the code

4. Pm2 monitoring

pm2 monit
Copy the code

5. Pm2 stops

Pm2 stop all stop all processes in the Pm2 list pm2 stop 0 Stop all processes in the PM2 listCopy the code

6. The PM2 restarts

Pm2 restart 0 restart all processes in the Pm2 listCopy the code

7. Pm2 deletes a process

Pm2 delete all delete all processes in the pm2 listCopy the code

Docker deployment

The preparatory work

  1. Package to generate the.next directory
  2. BUILD_ID is the same version as Server static

Docker-based Node application

Generate Dockerfile &.dockerignore

Touch Dockerfile// Generate a docker fileCopy the code

Dockerfile configuration

FROM node:14
# Create app directory
WORKDIR /usr/src/app
COPY package.json ./
COPY yarn.lock ./
RUN yarn install
COPY . .
EXPOSE 3000
CMD [ "yarn", "start" ]
Copy the code

. Dockerignore configuration

node_modules
*.log
Copy the code

reference

Build the mirror

-t yourUsername/node-web-app docker run -p 49160:8080 -d yourUsername/node-web-app Docker logs ID view if failure

Host is a pit, be careful

Buying a server

Aliyun chooses its own server

  1. Create an instance

  2. SSH root@public IP address // Log in to the server

  3. Ssh-copy-id root@public IP address // Upload a private key

  4. Modifying the host file

    Public IP variable name // Next login is very simple, directly use SSH root@ variable name to copy the code

  5. Create a separate user

SSH root@id// log in to adduser username// adduser su - username// switch to SSH username@id// log in to ssh-copy-id username@id as userCopy the code

6. Docker docker official website 7. User add group (solve non-root user can not run docker)

Cat /etc/group Check the group usermod -a -g docker usernameCopy the code
  1. Install Node && YARN (for packaging)
    curl -fsSL https://deb.nodesource.com/setup_12.x | sudo -E bash -
    sudo apt-get install -y nodejs
    sudo apt-get install gcc g++ make
    curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | sudo tee /usr/share/keyrings/yarnkey.gpg >/dev/null
    
    echo "deb [signed-by=/usr/share/keyrings/yarnkey.gpg] https://dl.yarnpkg.com/debian stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
    
    sudo apt-get update && sudo apt-get install yarn
Copy the code
  1. Internal docker-ization of the server
Touch. Env. local// Add the local file as required for the project, // Install dependency yarn // package code yarn build // create folder mkdir blog-data // Enable database docker run --network=host -v /home/xiong/blog-data:/var/lib/postgresql/data -p 5432:5432 -e POSTGRES_USER=blog -e POSTGRES_HOST_AUTH_METHOD=trust -d Postgres :12.2 YARN Typeorm :build // Package Typerom YARN Migration :run// Create a table // Generate an image docker build -t aslanxiong/node-web-app. Docker run --network=host -p 35:3000 -d aslanxiong/node-web-appCopy the code
  1. Ali cloud security policy group adds the port to be accessed

Using deployment scripts

Sh file chmod +x bin/deploy.sh // Add the executable permission SSH xiong@nextjs "sh /home/..." // Run the server script or SSH xiong@nextjs 'bash -s' < bin/deploy.sh// Run the local scriptCopy the code
  1. Use nginx
Docker run --name nginxNextjs -d nginx:1.19.1 docker run --name nginxNextjs --network=host -v /home/xiong/app/nginx.conf:/etc/nginx/conf.d/default.conf -v / home/xiong/app/next/static / : / home/xiong/nginx/HTML / _next/static / - d nginx: 1.19.1Copy the code