I didn’t write for almost a whole year, mainly because I was lazy and busy with my work. But I want to send one by the end of the year. I hope I can work harder next year. In fact, it is not that there is nothing to write, but it is still very difficult to go deep into a thing.

Next. Js is a react isomorphic library, and many articles regard it as a scaffold, which is not bad. However, I personally think that next. I went back to practice the development of NextJS after work these two days. Encountered some pits, but also some gains here to record.

The request data

Nextjs has no client lifecycle and only one static method, getInitialProps, so that’s the only way to get the interface data. The returned data from getInitialProps is the props for this component. GetInitialProps has two parameters: req and RES, the familiar HTTP parameters. As an aside, existing Node Web frameworks use REQ as input and RES as output, adding various middleware.

So I personally feel that the component form of NextJS is too suitable for stateless components. Here is a simple example code:

// Get the list of movies and render
class MovieList extends Component {
	static async getInitialProps(){
  	const data = await getMovieList()
    return {
    	list: data
    }
  }
  
  render () {
  	return (
    	<div>
        {this.props.list.map(movie => {
        	<MovieCard key={movie.id} movie={movie}>
        })}
      </div>)}}Copy the code

Of course, this is ultimately just a list of outputs from the server. We may have other operations, such as deleting and loading the next page, but none of these operations need to be performed on the server. Add a few corresponding methods.

Routing management

Nextjs routing is based on the file system and is fairly clear and simple, such as adding a movie-detail component under the Pages folder and writing the appropriate code to access the /movie-detail route. At first, I think this routing form is too elegant, but after a long time, I will find many problems.

Nested routing

For example, if I want to create /user/profile routes, this is actually easy to solve, is the pages folder under the nested line:

A named routing

Second, there is no official implementation of a named path. What is a named path? Nextjs follows in the footsteps of React-Router4 in this respect. Nuxtjs, vuejs’s homogeneous framework, does not have this problem because vue-Router itself manages routes uniformly. Regardless of whether the situation is good or bad, look for a solution.

According to the examples and documentation I found, there are currently two solutions:

Use query instead of named paths

Query exists on the NextJS router.

So when we want to access the named route page we can say, “Query,” pass in the ID /movie-detail, okay? Id = XXX:

// Movie details page

class MovieDetail extends Component {
	static async getInitialProps({ req }) {
    const { id } = req.query
    const detail = await getDetail(id) 
  	return {
    	detail
    }
  }
  
  render () {
  	return (
    	// do anything you want)}}Copy the code

The custom server address

Using Query to pass parameters used to work, but it was too inelegant and inconsistent with rest thinking. So the Next community found another solution, using custom Server.

Before we get into the specifics, nextJS isn’t a static resource scaffold at the end of the day, and next will eventually deploy node services on its own. Nextjs actually has an HTTP service built in. If we didn’t use custom Sever, the built-in service would still be fine for rendering pages.

But if our Node doesn’t just render pages, it also needs to write interfaces. The situation at this point is very similar to the traditional backend development model: you need to not only write the interface but also render the page.

It was clear that nextJS’s built-in HTTP service couldn’t do the job, and we needed a more sophisticated Web framework. After all, professional matters are left to professionals. This is where custom Server comes in. Nextjs also has a series of examples:

So how does custom Server solve the named path problem? We borrowed the rendering capabilities of NextJS. Here, express is used as an example. The specific code is as follows:

// server.js
const express = require('express')
const next = require('next')

constdev = process.env.NODE_ENV ! = ='production'
const app = next({ dev, quiet: false })
const handle = app.getRequestHandler()
const SERVE_PORT = process.env.SERVE_PORT || 8001

app.prepare().then((a)= > {
  const server = express()

  server.get('/movie-detail/:id'.async (req, res) => {
    // Render the movie-detail component
    const html = await app.renderToHTML(req, res, '/movie-detail', req.query)
    res.send(html)
  })

  server.get(The '*', (req, res) => handle(req, res))

  server.listen(SERVE_PORT, err => {
    if (err) throw err
    console.log(`> Ready on http://localhost:${SERVE_PORT}`)})})Copy the code

The above is the brief code of server.js, of course, we also need to do the same in the component, the code is as follows

// /pages/movie-detail.jsx
// Movie details page

class MovieDetail extends Component {
	static async getInitialProps({ req }) {
    const { id } = req.params
    const detail = await getDetail(id) 
  	return {
    	detail,
      id
    }
  }
  
  render () {
  	return (
    	// do anything you want)}}Copy the code

Page caching

For CSR’s React application, rendering time of 100ms is not too much of a problem, but 100ms is clearly intolerable on the server side. First of all, client rendering does not cause a waste of server resources, in fact, it does not cause too much to the server. But the server side is a different story. Once you get a lot of users, it’s going to cause all sorts of problems, so page caching is really necessary.

Where the specific page cache is not the scope of our consideration, the same page cache also needs to use custom Server, the specific server framework depends on it. Here to lru-cache as an example to do a simple page cache, in fact, to other such as Redis is also no problem.

constdev = process.env.NODE_ENV ! = ='production'

const next = require('next')
const express = require('express')
const LRUCache = require('lru-cache')

const ssrCache = new LRUCache({
  max: 1000.// cache item count
  maxAge: 1000 * 60 * 60.// 1 hour
})

const app = next({ dev, quiet: false })

const handle = app.getRequestHandler()

const SERVE_PORT = process.env.SERVE_PORT || 8001

app.prepare().then((a)= > {
  const server = express()

  server.get('/'.async (req, res) => {
    renderAndCache(req, res, '/', { ...req.query })    
  })

  server.get('/movie-detail/:id'.async (req, res) => {
    renderAndCache(req, res, '/movie-detail', { ...req.query })
  })

  server.get(The '*', (req, res) => handle(req, res))

  server.listen(SERVE_PORT, err => {
    if (err) throw err
    console.log(`> Ready on http://localhost:${SERVE_PORT}`)})})const getCacheKey = req= > `${req.url}`

// Cache and render the page, whether to re-render or use cache
async function renderAndCache(req, res, pagePath, queryParams) {
  const key = getCacheKey(req)
  if (ssrCache.has(key)) {
    res.setHeader('x-cache'.'HIT')
    res.send(ssrCache.get(key))
    return
  }

  try {
    const html = await app.renderToHTML(req, res, pagePath, queryParams)

    // Something is wrong with the request, let's skip the cache
    if(res.statusCode ! = =200) {
      res.send(html)
      return
    }

    // Let's cache this page
    ssrCache.set(key, html)

    res.setHeader('x-cache'.'MISS')
    res.send(html)
  } catch (err) {
    app.renderError(err, req, res, pagePath, queryParams)
  }
}
Copy the code

RenderAndCache is the key. This determines whether the page is cached, and if so, prints the cached content. Otherwise re-render. As for the cache time and cache size depends on personal Settings, here is not repeated.

The deployment of online

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.

I am used to starting node services using Supervisor.

conclusion

Having said that, in fact, there are relevant examples in official documents, as my personal record of stepping on pits.

For NextJS, I think if it is a demonstration application, it should be used boldly. Not only development is fast and fun, but also blocking the WebPack configuration, why not?

If it is functional, such as a series of drawing components, it is not necessary to complete the use of canvas or other client rendering, but nextJS has no life cycle, using NextJS may be quite pits.

I highly recommend this for personal development. Why waste your life configuring WebPack.

For a completely static application, I recommend gatsbyJS. How to use it is another topic.

If there is error, lightly spray. over