Next.js

 

Next.js is a lightweight framework for static and server‑rendered applications. https://nextjs.org

Server-side rendering

// create-react-app dev
curl localhost:3000

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="theme-color" content="#000000">
    <!--
      manifest.json provides metadata used when your web app is added to the
      homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
    -->
    <link rel="manifest" href="/manifest.json">
    <link rel="shortcut icon" href="/favicon.ico">
    <!--
      Notice the use of  in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>React App</title>
  </head>
  <body>
    <noscript>
      You need to enable JavaScript to run this app.
    </noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  <script type="text/javascript" src="/static/js/bundle.js"></script></body>
</html>
// create-react-app prod
curl localhost:3000

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#000000">
<link rel="manifest" href="/manifest.json"><link rel="shortcut icon" href="/favicon.ico">
<title>React App</title>
<link href="/static/css/main.c17080f1.css" rel="stylesheet">
</head>
<body><noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root">
</div><script type="text/javascript" src="/static/js/main.d1cb61c2.js">
</script>
</body>
</html>
// next.js app
curl localhost:3000

<!DOCTYPE html>
<html>
<head>
<meta charSet="utf-8" class="next-head"/><link rel="preload" href="/_next/static/development/pages/index.js" as="script"/>
<link rel="preload" href="/_next/static/development/pages/_app.js" as="script"/>
<link rel="preload" href="/_next/static/development/pages/_error.js" as="script"/><link rel="preload" href="/_next/static/runtime/webpack.js" as="script"/><link rel="preload" href="/_next/static/runtime/main.js" as="script"/></head>

<body>
<div id="__next"><div data-reactroot="">Welcome to next.js!</div></div>
<script src="/_next/static/development/dll/dll_4a2ab6ce0cb456fbfead.js"></script><script>__NEXT_DATA__ = {"props":{"pageProps":{}},"page":"/","pathname":"/","query":{},"buildId":"development"};__NEXT_LOADED_PAGES__=[];__NEXT_REGISTER_PAGE=function(r,f){__NEXT_LOADED_PAGES__.push([r, f])}</script><script async="" id="__NEXT_PAGE__/" src="/_next/static/development/pages/index.js"></script><script async="" id="__NEXT_PAGE__/_app" src="/_next/static/development/pages/_app.js"></script><script async="" id="__NEXT_PAGE__/_error" src="/_next/static/development/pages/_error.js"></script>
<script src="/_next/static/runtime/webpack.js" async=""></script><script src="/_next/static/runtime/main.js" async=""></script></body></html>

Next.js

  • Projet open source (https://github.com/zeit/next.js/)
  • "Framework" car propose une structure
  • Nuxt.js pour Vue
  • Inspiré par PHP pour la simplicité

Quelques commandes

// package.json

"scripts": {
  "dev": "next",
  "build": "next build",
  "start": "next start"
}
// pages/index.js -> localhost:3000
export default () => <div>👋la bouffe front 😻</div>

Par convention, les composants dans le dossier /pages sont automatiquement routés et code splitté

// pages/cow.js -> localhost:3000/cow
import cowsay from 'cowsay-browser'

export default () =>
  <pre>
    {cowsay.say({ text: '🐄 there!' })}
  </pre>

css

Par défaut styled-jsx, un peu comme .vue

Possibilité d'importer css, .scss, .less ou .styl

export default () =>
  <div>
    Hello world
    <p>scoped!</p>
    <style jsx>{`
      p {
        color: blue;
      }
      div {
        background: red;
      }
      @media (max-width: 600px) {
        div {
          background: blue;
        }
      }
    `}</style>
    <style global jsx>{`
      body {
        background: black;
      }
    `}</style>
  </div>

Récupérer des données

Composants

import React from 'react'

export default class extends React.Component {
  static async getInitialProps({ req }) {
    const res = await fetch('https://api.github.com/repos/zeit/next.js')
    const json = await res.json()
    return { stars: json.stargazers_count }
  }

  render() {
    const { stars } = this.props;
    return (
        <div>
            <p>
            👋 la bouffe front 😻
            </p>
            <p>
            Next stars: {stars}
            </p>
            <Link prefetch href="/cow">
              Meuh
            </Link>{' '}
        </div>
    
    )
  }
}

Possibilité d'exporter en HTML

// package.json

"scripts": {
  "export": "next export",
  "serve": "serve -s out"
},

Serveur custom

const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')

const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
  createServer((req, res) => {
    // Be sure to pass `true` as the second argument to `url.parse`.
    // This tells it to parse the query portion of the URL.
    const parsedUrl = parse(req.url, true)
    const { pathname, query } = parsedUrl

    if (pathname === '/a') {
      app.render(req, res, '/b', query)
    } else if (pathname === '/b') {
      app.render(req, res, '/a', query)
    } else {
      handle(req, res, parsedUrl)
    }
  }).listen(3000, err => {
    if (err) throw err
    console.log('> Ready on http://localhost:3000')
  })
})

Un petit projet de démo

  • Serveur express
  • Appels à une API externe
  • Mise en cache avec LRU-Cache

Avantages

  • Vitesse d'affichage pour l'utilisateur 🚄
  • SEO bon et cohérent 🔎
  • Encapsule Webpack 📦
  • Code splitting ✂️
  • Mises à jour régulières 🔥
  • Utilisé en prod et validé @hubside ✅

Inconvévients

  • Besoin d'un serveur node 🖥️
  • Peut avoir de mauvaise perf serveur 🐢(ReactDOMServer.renderToString appel CPU synchrone)
  • Parfois des petits breaking change sympa 💥
  • Complexifie le workflow 🕵️‍♂️

Quelques ressources

Exos avec QCM : https://nextjs.org/learn/

7 principles of rich web application : https://rauchg.com/2014/7-principles-of-rich-web-applications

Devtips : https://www.youtube.com/playlist?list=PLqGj3iMvMa4K-QGq9uaF3BbPojp4xyoRw

BOW 2018 - Nuxt.js : https://www.youtube.com/watch?v=VmYcHNIp4tM

Liste d'APIs publiques : https://github.com/toddmotto/public-apis

Repo perso avec les exemples utilisés dans la prez : https://github.com/alexbrbr/prez-next

Next.js

By Alexandre BARBIER