Start with the address, there is an online demo, currently only support PC: github.com/OrangeXC/ga…

Since there are a lot of articles related to VUE recently, I spare some time to write react projects. React was v15 at that time, but now it is V16. I feel that I can’t keep up with the pace of all frameworks. It is a good habit to look at change every time the high star framework is updated.

Before using Nuxt to write a simple V2EX, today’s protagonist is still SSR server rendering

The Nuxt documentation says it was inspired by next.js, which means that next.js is the elder statesman of the SSR framework.

Why SSR framework

The previous article is always in the official documentation. For those unfamiliar with Next. Js, please refer to Github directly

Whether it’s Next. Js or Nuxt, server-side rendering frameworks have two key features

  • First screen Node.js server rendering
  • Generate a purely static Web site

Which front-end library they are packaged on depends on whether the library itself supports SSR, and then provides the render function.

The reason for using such a library goes without saying, saving on development costs and the hassle of setting up environments and rendering details.

Start directly

Is the project to be implemented based on GANK API, or to see what API is supported by others, click the previous link to view the detailed API

General summary => list, search, request to audit

Lists are divided into many types, and the main menu is also expanded for different types of lists

routing

Routes can be easily defined using known apis

  • / (Home page, latest list of all types of dry goods)
  • / FE (Front dry list)
  • / Android (Android dry Goods list)
  • /ios (ios dry Goods list)
  • /app (App dry Goods list)
  • /expand (expand resources dry goods list)
  • / Videos (Dry goods list)
  • /welfare (list of welfare, high energy ahead, all dry goods…)
  • / Timelien (timeline, which records the date of all updated dry goods in history)
  • /day (details of a day, divided into the above types of TAB list)
  • Uplaod (send dry goods to audit)
  • /search (search page)

Nuxt route configuration file does not need to be manually created. By default, / Pages will be rendered as a page, and the file name is naturally the route name

Now that the routing file is created, the Next step to consider is to pull out the common Layout code. Next. Js provides a layout-component example

We can define the Head,Header, and Footer inside, of course with a slot for the content area {children}.

The layout.js code referenced in Example

import Link from 'next/link'
import Head from 'next/head'

export default ({ children, title = 'This is the default title'}) => (<div> <Head> <title>{title}</title> <meta charSet=' utF-8 '/> <meta name='viewport' content='initial-scale=1.0,  width=device-width' /> </Head> <header> <nav> <Link href='/'><a>Home</a></Link> | <Link href='/about'><a>About</a></Link> | <Link href='/contact'><a>Contact</a></Link> </nav> </header> { children } <footer> {'I`m here to stay'} </footer> </div> )Copy the code

Since ANTD is used to do the UI this time, we should pay attention to the small problems in the display of dynamic navigation. We need to activate the menu dynamically according to the path.

Two solutions:

1. Get the PathName from each routing page in Pages, and get the pathname from the initialization method getInitialProps

  • pathname – path section of URL
  • query – query string section of URL parsed as an object
  • asPath – String of the actual path (including the query) shows in the browser
  • req – HTTP request object (server only)
  • res – HTTP response object (server only)
  • jsonPageRes – Fetch Response object (client only)
  • err – Error object if any error is encountered during the rendering

Calling methods is also simple

static async getInitialProps({ pathname }) {
  return { pathname }
}Copy the code

< layout pathName ={this.props. Pathname}>

Change Meun active in Layout

2. Write an ActiveLink component and encapsulate the original Menu

Before choosing a solution, we still need to check whether there is an example official, so we found a using-with-router

The activelink.js code referenced in Example

import { withRouter } from 'next/router'

// typically you want to use `next/link` for this usecase
// but this example shows how you can also access the router
// using the withRouter utility.

const ActiveLink = ({ children, router, href }) = > {
  const style = {
    marginRight: 10.color: router.pathname === href ? 'red' : 'black'
  }

  const handleClick = (e) = > {
    e.preventDefault()
    router.push(href)
  }

  return (
    <a href={href} onClick={handleClick} style={style}>
      {children}
    </a>)}export default withRouter(ActiveLink)Copy the code

In the withRouter method, you can get router instances, pathName, Query, and so on.

Here I just need to change the style slightly to antD’s className as follows

const ActiveLink = ({ children, router, href }) = > {
  const active = router.pathname === href
  const className = active ? 'ant-menu-item-selected ant-menu-item' : 'ant-menu-item'
  return (
    <li href=The '#' onClick={onClickHandler(href)} className={className} role="menuitem" aria-selected="false">
      {children}
    </li>)}Copy the code

Use the ActiveLink component directly from the Layout component Menu, and you’ve solved all routing and Layout issues

The data flow

The next step is to populate each page’s content

We’re still getting the data in getInitialProps, which is the same method as prefatch, which the server will perform in advance to get the data to render into the template

There is a fetch library isomorphic-fetch which is isomorphic between Node and Browserify. The CLI tool should come with this library, if not, install it in advance.

Here do not have to worry about the fetch API in the server problems, go here to obtain a list of data interface in https://gank.io/api/data/ / {perPage} / {type} {page}

Three variables are type- type, perPage- number of pages perPage, and page- number of pages

Next, List and ListItem can be abstracted into a common component, which can be called on every page. There is no elaboration here. Simply use ANTD Card component, no special function.

The request data portion of each page is basically the same, and the data is stored in props and passed into the List component

This creates a simple one-way flow of data

List page

Fetch Data -> List (inherited from Layout) -> ListItem

Timeline page

Page component (Fetch Data) -> Timeline component (inherited from Layout)

Submit dry goods page

Page component -> Form component (inherited from Layout) -> Post request (send formData)

Search page

Page component -> Input component + empty ListItem component (inherited from Layout) -> GET request (get keyword corresponding query list data) -> ListItem component

Mobx

Given that data flow is simple, why bother with global state management?

In React, props are one-way and can only be passed down and cannot be modified

We need paging here, but the first screen is props, so we can’t update the props value after the page swap, so we can’t execute getInitialProps again

The simplest and crude way is to give up the dynamic switching data of SPA. Every time we router. push({some page}/{per page}/{current page}), we go back to the MVC version of routing switching before liberation.

Antd provides Pagination after Pagination. The list they return doesn’t tell you totalCount. Without totalCount, there’s no way to know how many pages there are.

Good embarrassing question, this page can not do, angry face ~~~

Is not can’t do, to think about this question can be loadMore, right to load more, when loaded into the last page (i.e., a list of length less than perPage) or the page happens exactly to match perPage but empty array for the next page, we give a hint, no more.

When it comes to the concat array in the props list, we have to introduce global state to solve this problem. Both Redux and MOBx can solve this problem. Note that the use of next.

Let’s go to Example, with Mobx

The store.js code referenced in example

export function initStore (isServer, lastUpdate = Date.now()) {
  if (isServer) {
    return new Store(isServer, lastUpdate)
  } else {
    if (store === null) {
      store = new Store(isServer, lastUpdate)
    }
    return store
  }
}Copy the code

This code is too simple to explain, just call initStore when initializing the page. IsServer passes the req parameter of getInitialProps. Judge the req

Then an action is launched on loadMore

@action loadMoreList = (more) = > {
  this.list = this.list.concat(more)
}Copy the code

The problem is that the handleScroll method in the List component is a little rough and ready. It works, but there are issues like multiple triggers, no compatible code (it will be improved later), and the code is released for everyone to laugh about

handleScroll () {
  if (document.documentElement.offsetHeight + document.documentElement.scrollTop > document.documentElement.scrollHeight - 50) {
    this.handleLoadMore()
  }
}Copy the code

Other code interested can directly take the warehouse to see, no reading difficulty.

The form submission

Speaking of fetch lists for other pages, there is nothing to make them all get requests. Fetch simply sends a GET request without declaring the request type.

The fetch operation post also simply sets method to POST

The reason for the separate chapter on form submission is that there are some problems with submitting the form, since the fetch emulates the FORM’s POST request

Read the issue: github.com/matthew-and…

Began to doubt life, tried all methods POST, also went through, but the interface returned MSG is not received parameters.

I thought I’d go back to the stupid method of concatenating parameters one by one, but I didn’t think of a more elegant way to give the code, and welcome discussion

handleSubmit = (e) = > {
  e.preventDefault()

  this.props.form.validateFieldsAndScroll(async (err, values) => {
    if(! err) {this.setState({ submitLoading: true })

      let strList = []

      Object.keys(values).forEach(item= > {
        strList.push(`${item}=${values[item]}`)})const res = await fetch("https://gank.io/api/add2gank", {
        method: "POST".headers: {
          'Content-Type': 'application/x-www-form-urlencoded'
        },
        body: strList.join('&')})const json = await res.json()

      if (json.error) {
        message.error(json.msg)
      } else {
        message.success(json.msg)
      }

      this.setState({ submitLoading: false})}})}Copy the code

Readers who have seen the website also found that there is a prompt at the top of the submission form page, asking everyone to use the publishing dry goods interface of the third-party API provider GANK civilized, and submit the real good content. If you want to test the interface, please go to the default debug mode. Here again, thank you for your cooperation

The micro interaction

Now that the functions are almost complete, we should make more efforts in micro-interaction. Those who have used NUXT know that NUXT has a built-in Loading bar. When switching routes, there will be a Loading bar at the top of the page, which makes the experience better.

This feature is not built into next.js, and the page will look very strange. Clicking to switch routes will not respond, and the page will jump again after a pause, while getting initialization data.

Nprogress is officially recommended

The key code is as follows, written in layout.js component

Router.onRouteChangeStart = (url) = > {
  console.log(`Loading: ${url}`)
  NProgress.start()
}
Router.onRouteChangeComplete = (a)= > NProgress.done()
Router.onRouteChangeError = (a)= > NProgress.done()Copy the code

In this way, the whole website looks much more fashionable. There is a Loading bar at the top of the page for switching router and a Loading icon at the upper right corner

online

The organization behind next.js is called Zeit. On their website, their favorite product is Now, a rapid deployment tool that also offers three free services for free users, including Docker, Node, etc

Deploying node projects in 5 minutes is much easier than Heroku

Now is used here. First install now-CLI

Deploy with one command at the root of the project

nowCopy the code

The online path will not be posted, always pay attention to the website address above Github, because every deployment without binding domain name is the project name + random hash domain name, binding domain name needs money.

As for the line on so much, there are questions welcome exchange.

In the future

The next step is to solve several problems

  • Load more bugs
  • Mobile terminal support
  • Show pictures directly on welfare page (click to play in full screen)
  • Beautify the timeline style

When it comes to welfare page, I thought I would not add it, because individual people who write demo specifically carried out the welfare list to make it into sister App, since it is a dry goods concentration camp, it should be more technical elements, welfare is secondary.

conclusion

So far, a next. Js version of Gank has been completed. I feel that the development tools are becoming more and more useful now. But to give domestic learners a Chinese version of the example, at the same time the article will be implemented when the problems encountered.

I orange is also in the continuous learning, this is the first time to contact learning next. Js written project, article or project shortcomings welcome correction, thank you for reading!

This post has been updated to my personal blog, orangexc.xyz

The asynchronous community as the top IT professional book community, issued high quality technical books, for this article call hope can be promoted, I want this book JavaScript framework design (2nd edition).