Recently, I wanted to tackle a Node.js framework.

Hoping to find a framework that can help us do most of the things well, you can get started and develop quickly. Unlike traditional Express, Koa requires a large amount of middleware. With that in mind, Google it and you get — nex.js. Finally completed a simple blog system, code address: github.com/Maricaya/ne… The preview address is http://121.36.50.175/. Have to say SSR is really fragrant, almost no white screen time, loading very fast.

To record the learning process, the code for this article is here.

Let’s take a look at what next-js is.

Next. Js is a full stack framework

Next. Js is a lightweight React server-side rendering application framework.

It supports multiple rendering modes: client-side rendering, static page generation, and server-side rendering.

Using Next. Js to implement SSR is an easy thing to do, and we don’t have to write webpack configuration ourselves. Next.

weaknesses

Many of the advantages of Nex.js have been discussed above, but every framework has its imperfections, especially in the Node.js community.

As a backend framework, Nex.js doesn’t provide any functionality for manipulating databases at all, so it has to work with other frameworks. (such as Sequelize or TypeORM).

It also doesn’t provide testing functionality, so you need to match it with Jest or Cypress.

Now that we have a basic understanding of Next. Js, let’s follow the official website to do a simple project.

Create a project

# nextjs-blog-1 is the name of our project
npm init next-app nextjs-blog-1
Copy the code

Select the Default Starter app.

Go to nextjs-blog-1 and use the command line to start the project yarn dev.

If you see the following page 👇, your project has started successfully.

Let’s add TypeScript to the project!

Start the TypeScript.

The first step is to install TypeScript.

yarn global add typescript
Copy the code

Create tsconfig. Json

Then we run TSC –init to get tsconfig.json, which is the TypeScript configuration file.

Next, install the type declaration file and restart the project.

yarn add --dev typescript @types/react @types/node
yarn dev
Copy the code

Then we change the file name index.js to index.tsx.

Create the first article

Create the posts folder at the root, where we’ll put our posts.

Create posts/first-post.tsx file, write code:

// The first article
import React from "react"
import {NextPage} from 'next';

const FirstPost: NextPage = () = > {
  return (
    <div>First Post</div>)}export default FirstPost;
Copy the code

Visit http://localhost:3000/hosts/first-post can see page at this time.

Link Quick Navigation

The official website describes the Link quick navigation.

A little understanding of the front end of the students may have such a problem, is not there a tag to navigate, Next. Js why to superfluous effort.

According to the website, Link provides quick navigation. Let’s do an experiment to see how it differs from an A tag.

First, use a tag and Link tag to navigate in the project, so that the home page and the first article can jump to each other.

index.tsx

<h1 className="title">First article<a href="/posts/first-post">A Click here</a>
	<Link href="/posts/first-post"><a >Link Click here</a></Link>
</h1>
Copy the code

/posts/first-post.tsx

// Go back to the home page<hr/>
<a href="/">A Click here</a>
<Link><a href="/">Link Click here</a></Link>
Copy the code

Click the A tag. Every time you enter the first-post and index pages, the browser will request all the HTML, CSS and JS again.

Next, using the Link TAB to navigate, something amazing happened. The browser only sent two requests.The second request is webpack, so there is only one real request, first-post.js.

Repeatedly jump between the two pages, and the browser makes no requests other than Webpack.

What exactly does nex.js do? What’s the difference between fast navigation and traditional navigation?

The traditional navigation

Let’s first take a look at how traditional navigation from Page1 to Page2 is implemented 👇When the first page page1 is accessed, the browser requests HTML, and then loads CSS and JS in turn.

When the user clicks the A tag, it is redirected to Page2, the browser requests HTML, and then loads CSS and JS again.

Link Quick Navigation

Take a look at the same process, and how quick navigation is implemented in Next. Js.First visit Page1, browser download HTML, and then load CSS, JS in turn. These are the same as traditional navigation.

But when the user clicks the Link tag, Page1 executes a JS, which parses the Link tag, and then requests page2.js of Page2, which is HTML + CSS + JS of Page2.

After requesting page2.js, it will return to page1 page, update page2’s HTML, CSS, JS to page1. So we’re going to update page1 to page2.

So the browser doesn’t visit Page2 itself, but Page1 gets the content of Page2 through Ajax.

advantages

So, Link quick navigation (client navigation) has so many advantages:

  • Instead of refreshing the page, AJAX requests new page content.
  • Does not request duplicate HTML, CSS, JS.
  • Automatically insert new content in the page, delete old content.
  • It is extremely fast because it saves some requests and parsing.

Isomorphism code

What is isomorphism?

Isomorphism refers to developing a program that can run on different platforms. In this case, THE JS code can run on both the Node.js Web server and the browser.

So the code runs on both sides.

As an experiment, let’s write a console.log(‘aaa’) in the component.

As a result, both the Node console and Chrome Console print aaa.

Pay attention to the differences between

But not all code runs on both sides.

Code that needs to be triggered by the user, for example, will only run on the browser side.

Our code can’t be written arbitrarily, it has to work on both ends. For example, if there is no such object in Node.js, an error will be reported.

advantages

Reduce code development and increase code duplication.

  • One piece of code can run on both the browser and the server, so the amount of code is reduced.
  • Business logic does not need to be maintained on both the browser and the server, reducing the possibility of errors.

Global configurationHead, Metadata, CSS

Head

title

We want the title of the page to be different, how do we configure it?

So let’s configure the title in the Head, and the Head will write the title for us.

<Head>
  <title>My blog</title>
</Head>
Copy the code

Metadata

Same thing with meta

<Head>
	<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover"/>
</Head>
Copy the code

TSX and first-post. TSX are two files. Should I write it twice? Is there a uniform way to write?

Global configuration

Create pages/_app.js, copy the code from the official website, write our tie and restart Yarn dev.

export default function App({ Component, pageProps }) {
  // Component
  return <div>
<Component {. pageProps} / >
</div>
}
Copy the code

Component is our index and first-post; PageProps is an option for the page and is currently an empty object.

Export Default Function App is the root component of each page. The App is not destroyed when the page is switched, the components in the App are destroyed. We can use the App to save the global state.

CSS

Again, the global CSS is placed in _app.js. Because the App is not destroyed when you slice the page, you can only write partial CSS elsewhere.

imprort '.. /styles/global.css'.Copy the code

Absolute reference

Write relative path is a bit cumbersome, can specify the root directory write absolute path? Looking at the official website, I found that Next. Js provides a similar function.

Configure tsconfig.json and define the root directory.

{
  "compilerOptions": {
    "baseUrl": "."}}Copy the code

Restart the project and you can definitely introduce CSS:

imprort 'styles/global.css'.Copy the code

Static resource

Next recommends putting it in public/, but I don’t recommend it because filename changes are not supported.

For those of you who have front-end knowledge, changing the name of the file is not supported, which will affect our caching strategy.

If static resources in public are not cached, then every request for resources will be made to the server, resulting in a waste of resources.

But with caching, we have to update the resource name every time we update the static resource, otherwise the browser will still load the old resource.

So, we create /assets in the root directory to place the static resources, and we need to configure WebPack in nex.js.

Customize the Webpack configuration by creating next. Config. js in the root directory according to the official website.

The picture

Configuration image – loader

The configuration file – loader.

Install yarn add –dev file-loader.

next.config.js

module.exports = {
  webpack: (config, options) = > {
    config.module.rules.push({
      test: /\.(png|jpg|jpeg|gif|svg)$/,
      use: [
        {
          loader: 'file-loader'.options: {
            // img path name.hash.ext
            // For example, 1. PNG
            / / _next/static / 1.29 fef1d3301a37127e326ea4c1543df5. PNG
            name: '[name].[contenthash].[ext]'.// Disk path
            outputPath: 'static'.// The site path is
            publicPath: '_next/static'}}}])return config
  }
}
Copy the code

Go straight to next-images

If you don’t want to configure it yourself, you can use next-images directly.

yarn add --dev next-images
Copy the code

next.config.js

const withImages = require('next-images')

module.exports = withImages({
  webpack(config, options) {
    return config
  }
})
Copy the code

Method of use

<img src={require('./my-image.jpg')} / >Copy the code

TypeScript

Now the imported image file is still getting an error because we’re using TypeScript, and TypeScript doesn’t know how to interpret the imported image.

Next-images has been kind enough to prepare the image module definition file.

So, we just need to add a reference to type next-images in the next-env.d.ts file.

/// <reference types="next-images" />
Copy the code

More other files

Find the Loader yourself and configure next. Config.js, or see if it is packaged as the Next plug-in.

These are the purview of WebPack and you can explore them on your own. This article is not wordy.

Next.js API

So far, our index and posts/ first-posts have been HTML pages.

But in real development we need to request apis like /user, /shops, etc., which return JSON format strings. How do you do that in next-js?

Use the API pattern of nex.js.

Using the Next. Js API

demo

The default API path is/API /v1/ XXX, we create a test interface demo.ts.

Code in the API directory runs only in Node.js, not in the browser.

demo.tsx

// ts is the type
import {NextApiHandler} from 'next';

const Demo:NextApiHandler = (req, res) = > {
	// Other operations are the same as js
  res.statusCode = 200;
  res.setHeader('Content-Type'.'application/json');
  res.write(JSON.stringify({name: 'dog'}));
  res.end();
};

export default Demo;
Copy the code

accesshttp://localhost:3000/api/demoTo get the data.

posts

Next, let’s complete a formal blog API, the posts interface.

First, prepare the blog file, create a MarkDown document in the root directory, and write several blogs in MD format.

We then parse the data from the MD file with the help of gray-matter.

The lib/posts. TSX file exports JSON data.

import path from "path";
import fs, {promises as fsPromise} from "fs";
import matter from "gray-matter";

export const getPosts = async() = > {const markdownDir = path.join(process.cwd(), 'markdown');

  const fileNames = await fsPromise.readdir(markdownDir);

  const x = fileNames.map(fileName= > {
    const fullPath = path.join(markdownDir, fileName);
    const id = fileName.replace(fullPath, ' ');
    const text = fs.readFileSync(fullPath, 'utf8');
    const {data: {title, date}, content} = matter(text);
    return {
      id, title, date
    }
  });
  console.log('x');
  console.log(x);
  return x;
};
Copy the code

Now that the data is out of the way, the posts API simply takes the data from the code above and returns it to the front end. pages/api/posts.tsx

import {NextApiHandler} from 'next';
import {getPosts} from 'lib/posts';


const Posts: NextApiHandler = async (req, res) => {
  const posts = await getPosts();
  res.statusCode = 200;
  res.setHeader('Content-Type'.'application/json');
  res.write(JSON.stringify(posts));
  res.end();
};
export default Posts;
Copy the code

Ps: Next. Js is based on Express, so it supports Express middleware. If you have complex operations, you can use Express middleware.

Next. Js renders in three ways

Now let’s do the front end, using three rendering methods.

Client rendering

Rendering that is performed only in the browser.

This is the original front-end rendering, where the page is rendered after the browser has acquired JavaScript and CSS files. Routing is client-side routing, which is by far the most common SINGLE page application of SPA.

disadvantages

But this approach creates two problems. The first is white screen. The current solution is to add Loading to the page before AJAX gets corresponding. Second, SEO is not friendly, because the search engine will not perform JS by default when visiting the page, and can only see HTML, not AJAX request data.

code

pages/posts/BSR.tsx

import {NextPage} from 'next';
import axios from 'axios';
import {useEffect, useState} from "react";
import * as React from "react";

type Post = {
    id: string.id: string.title: string
}
const PostsIndex: NextPage = () = > {
    // [] indicates that only the first render is requested
    const [posts, setPosts] = useState<Post[]>([]);
    const [isLoading, setIsLoading] = useState(false);
    useEffect(() = > {
        setIsLoading(true);
        axios.get('/api/posts').then(response= > {
          setPosts(response.data);
          setIsLoading(false);
        }, () = > {
            setIsLoading(true); }}), []);return (
        <div>
            <h1>The article lists</h1>
            {isLoading ? <div>In the load</div> :
                posts.map(p => <div key={p.id}>
                {p.id}
            </div>)}
        </div>)};export default PostsIndex;
Copy the code

Visit http://localhost:3000/posts/BSR, if the network is bad, bad for a long time.

Since the data is not originally on the page, it is rendered on the page after an Ajax request.

The list of articles is rendered on the front end, which we call client-side rendering.

SSG Static Site Generation

We do a blog site where everyone sees the same list of articles.

So why do you need to render it once on everyone’s browser?

Can you render it on the back end and request it directly from the browser?

In this case, N renders become 1 render, and N client renders become 1 static page generation.

This process is called dynamic content statics.

The advantages and disadvantages

This way can solve the white screen problem, SEO problem.

However, in this way, all users request the same content, and no user-related content can be generated.

GetStaticProps To get posts

Obviously, it’s best not to get posts through AJAX on the back end.

Our data is in the folder, we can just read it, we don’t have to send AJAX.

So how do you get posts?

To export the data, use the getStaticProps method provided by nex.js. The NextPage props parameter automatically gets the exported data.

Take a look at the code:

SSG.tsx

import {GetStaticProps, NextPage} from 'next';
import {getPosts} from '.. /.. /lib/posts';
import Link from 'next/link';
import * as React from 'react';

type Post = {
  id: string.title: string
}

type Props = {
  posts: Post[];
}
// props = props
const PostsIndex: NextPage<Props> = (props) = > {
  const {posts} = props;
	// Both front and rear consoles can print -> isomorphism
  console.log(posts);
  return (
    <div>
      <h1>The article lists</h1>
      {posts.map(p => <div key={p.id}>
        <Link href={` /posts/ ${p.id} `} >
          <a>
            {p.id}
          </a>
        </Link>
      </div>)}
    </div>
  );
};

export default PostsIndex;
SSG / / implementation
export const getStaticProps: GetStaticProps = async() = > {const posts = await getPosts();
  return {
    props: {
      posts: JSON.parse(JSON.stringify(posts))
    }
  };
};
Copy the code

Visit http://localhost:3000/posts/SSG, page access successfully.

Why doesn’t the front end get the data through AJAX?

We only passed the posts data to the server, why can we print it out on the front end?

Let’s look at the page at this point:

Now the front end can get to posts without AJAX, just get the data from __NEXT_DATA__. This is the benefit of isomorphic SSR: the data from the back end can be passed directly to the front end, and the front end json.parse can immediately get posts.

GetStaticProps Specifies the time to static

In the development environment, getStaticProps is run once on each request to make it easier for us to change the code and run it again.

In a production environment, getStaticProps only runs at build time, which provides a copy of HTML for all users to download.

Come experience the production environment and package our project.

yarn build
yarn start
Copy the code

After packaging, we get three types of files:

  • λ (Server) SSR does not automatically create HTML

  • – (Static) automatic HTML creation (found you didn’t use props)

  • ● (SSG) automatic creation of HTML + JSON (props)

These three files are created: posts.html = posts.js + posts.json

  • Posts.html contains static content for direct user access
  • Post.js also contains static content for quick navigation (corresponding to HTML)
  • Json contains the data, which is combined with posts.js to get the page

So why not just put the data in posts.js? Obviously, to make posts.js accept different data.

This feature comes into play when we present each blog post with the same style but different content.

summary

  • If the dynamic content is not relevant to the user, then it can be static in advance.
  • GetStaticProps lets you get the data, static content + data (fetched locally) and you get the full page. Instead of static content + dynamic data (AJAX fetch).
  • Statics are implemented during the YARN build
  • advantages
    • The production environment gives the full page directly
    • The first screen will not be blank
    • Search engine can see page content (SEO convenience)

Server Rendering (SSR)

What if the page is relevant to the user? This situation is more difficult to static ahead of time.

So what do we do?

  • Either client rendering, drop down update
  • Either render the service, pull down AJAX updates (no white screen

advantages

This way can solve the white screen problem, SEO problem. User-related content can be generated (with different results for different users).

code

This is basically the same as the SSG code, but instead of using getServerSideProps.

Write a piece of code that shows what the current user’s browser is.

import {GetServerSideProps, NextPage} from 'next';
import * as React from 'react';
import {IncomingHttpHeaders} from 'http';

type Props = {
  browser: string
}
const index: NextPage<Props> = (props) = > {
  return (
    <div>
      <h1>Your browser is {props. Browser}</h1>
    </div>
  );
};
export default index;

export const getServerSideProps: GetServerSideProps = async (context) => {
  const headers:IncomingHttpHeaders = context.req.headers;
  const browser = headers['user-agent'];
  return {
    props: {
      browser
    }
  };
};
Copy the code

getServerSideProps

In both development and production environments, you run getServerSideProps after the request comes in.

Go back to getStaticProps and see the difference.

  • Development environment, each request after the arrival of the run, convenient development
  • Production environment, run at build time

parameter

  • Context of type NextPageContext
  • Context.req /context.res can obtain requests and responses
  • Usually you just use context.req

Principle of SSR

Finally, let’s look at how SSR actually works.

We all know that SSR renders static content in advance. Is this static content rendered on the server side or on the client side?

How many render times? Once or twice?

Refer to the React SSR official documentation

It is recommended to call the renderToString() method on the back end to render the entire page as a string.

The front end then calls the hydrate() method, mixing the string passed by the back end with its own instance, keeping the HTML and attaching an event listener.

This is the main way Next. Js implements SSR, which renders HTML on the back end and adds a listener on the front end.

The front end will also render once to ensure that the front and back ends render the same. If the results are inconsistent, the console will alert us with an error.

conclusion

  • Create a projectNPM init next-app Project name
  • Quick navigation<Link href=xxx><a></a></Link>
  • Isomorphic code: one copy of code running on both ends
  • Global components:pages/_app.js
    • Global CSS: import in _app.js
  • Custom head: Use a component
  • Next. Js API: All in the /pages/ API directory
  • Three rendering methods: BSR, SSG, SSR
    • Static content outputs HTML directly, no terminology.
      • Dynamic content

      Terminology: Client-side rendering, rendered to HTML via An AJAX request.

      • Dynamic content statics

      Term: SSG, getStaticProps for getting user-irrelevant content

      • Static user-related dynamic content

      The term: SSR, get requests by getServerSideProps Disadvantages: Can’t get client information, such as browser window size