Das Next beste Update auf Next.js 12

26.01.2022Ricky Elfner
Mobile Next.js JavaScript Frontend Framework Hands-on Rust How-to

Next.js wurde 2016 von Vercel veröffentlicht. Dabei startete es jedoch als Framework, welches sich auf Server-Side-Rendering fokussierte. Mit der Version 12, welche am 26. Oktober 2021 released wurde, handelt es sich um den grössten Schritt seit es Next.js gibt. Dazu ist auch zu erwähnen, obwohl es sich um solch ein grosses Update handelt ist es komplett Backwards-kompatibel. Dabei soll es sehr Entwickler-freundlich und ebenfalls intuitiv zu nutzen sein. Vercel selbst schreibt, dass es das React-Framework ist, welches am besten für Produktion genutzt werden sollte.

Willkommen Next.js 12

Nun wollen wir Ihnen natürlich auch die einzelnen Features näher bringen und Ihnen zeigen, warum dieses Update eine solch grosse Veränderung mit sich bringt.

Rust Compiler

Zuerst fangen wir mit dem neuen Compiler an. Denn dabei handelt es sich nun um einen Compiler, welcher in Rust geschrieben wurde, genauer gesagt wird der Compiler auf der Codebasis von SWC (Speedy Web Compiler) aufgebaut. Dieser ersetzt den weitverbreiteten Babel-Compiler. In dem Blog-Beitrag von Vercel geben sie an, dass das Nachladen bis zu 3x schneller ist und auch der Build-Vorgang sogar bis zu 5x schneller ist. Dies ist natürlich abhängig wie gross die Codebasis ist. Hier sollte man jedoch auch erwähnen, dass Libraries wie styled-components, emotion oder relay bisher noch nicht von dem neuen Compiler unterstützen werden. Diese sollen selbstverständlich aber auch bald verfügbar sein. Über Opt-in können, innerhalb Ihrer Konfigurationen, auch Minifications aktiviert werden, welche bis zu 7x mal so schnell sein sollen wie Terser.

Support von Middleware & Edge Functions

Der typische Use-case von Middleware ist, dass Code nur noch unter bestimmten Voraussetzungen ausgeführt werden darf. Dies ist dadurch möglich, dass von jedem Request die Response verarbeitet werden kann. Hierfür gibt es die Technik von Rewriting, Redirecting, hinzufügen von Headern oder das Streamen von HTML.

Da Next.js in der Version 12 nun die Verwendung von Middleware unterstützt, können Sie auch die sogenannten Edge-Funktionen nutzen. Das bedeutet, dass Ihr Code so nah wie möglich in der Nähe des Benutzers ausgeführt wird. Vor dieser Version musste man sich direkt beim Entwickeln zwischen CDN oder SSR entscheiden, wenn man schnellere Apps entwickeln wollte. Dies ist möglich da Edge-Funktionen standardmässig global bereitgestellt werden. Hierfür wird eine V8-Runtime verwendet, welche eine leistungsstarke JavaScript und WebAssembly Engine enthält. Ebenfalls werden Web-API-Standards wie fetch unterstützt. Dadurch sollen bis zu 100x schnellere Startzeiten möglich sein, im Vergleich zu Node.js innerhalb von Containern oder auf virtuellen Maschinen.

Beispiele von Middleware sind folgende:

  • Authentication
  • Bot Protection
  • Redirects Browser Support
  • Feature Flags
  • A/B Testing
  • Server-Side Analytics
  • Logging

Zu diesem Zweck bietet Vercel selbst eine Seite mit verschiedenen Anwendungsbeispielen für Edge-Funktionen mit der Verwendung von Middleware.

ES Modules & URL Imports

An dem Feature von ES Modules wurde eigentlich schon in der vorherigen Version gearbeitet. Doch nun kommt das Feature von URL Imports noch dazu. Diese Kombination bietet Ihnen die Möglichkeit, direkt verschiedene Tools / Packages von einem CDN einzubinden, ohne diese selbst zuvor zu installieren oder builden zu lassen. Dabei muss natürlich das Package in einer “ready-for-production”-Version bereitgestellt werden. Anschliessend kann dieses direkt über die URL mit eingebunden und verwendet werden. Man sollt auf jeden Fall noch erwähnen, dass dieses Feature momentan noch in einer experimentellen Version vorliegt. Durch diese neue Funktion ist es Ihnen möglich die Grösse der Bundles um einiges zu reduzieren.

Default Komponenten

Während der Vorstellung von Next.js wurde auf einige Standardkomponenten genauer eingegangen, diese wollen wir Ihnen auch nicht vorenthalten. Mit Standardkomponenten ist in diesem Fall gemeint, dass anstatt die üblichen Tags Komponenten verwendet werden. Dadurch ist es möglich einige weitere Funktionen bereitzustellen, die den Entwicklern das Leben erleichtern sollen. Hierzu gehören beispielsweise diese Komponenten:

  • Link
  • Image
  • Script

Unten bei den Praxisbeispielen werden wir diese etwas genauer anschauen. Doch jeder Entwickler kennt das Problem, dass man solche Standardkomponenten nicht so oft nutzt. Dies kann aus verschiedenen Gründen passieren, zum Beispiel weiss man oft gar nicht, welche Alternativen es gibt oder man vergisst solche schnell wieder, wenn sie nicht oft zum Einsatz kommen. Deshalb hat Next.js bereits in ihrer vorherigen Version ihr Conformance System vorgestellt. Dadurch soll dem Entwickler Lösungen und Regeln aufgezeigt werden, um so verschiedene Features gezielter und auch öfters zu verwenden, um eine bessere Applikation zu schreiben. Verwendet man beispielsweise ein a-Tag anstatt der Link-Komponente wird ein Fehler angezeigt, sobald man npm run lint laufen lässt.

Dies wäre der Output für diesen Fall:

1
2
./pages/defaultComponents/examples.tsx
26:11  Error: Do not use the HTML <a> tag to navigate to /. Use Link from 'next/link' instead. See: https://nextjs.org/docs/messages/no-html-link-for-pages.  @next/next/no-html-link-for-pages

Vercels Plattform

Wenn Sie Ihre Applikation bei Vercel selbst deployen, gibt es einige nennenswerte Features, die Sie ohne grossen Aufwand nutzen können.

Next.js Live

Vercel bietet Ihnen mit Next.js Live eine Möglichkeit von über all auf der Welt mit Ihrem Team gleichzeitig Ihren Code anzupassen, zu chatten oder beispielsweise auch zu zeichnen. Und dies alles in Echtzeit. Dafür ist nur ein Deployment auf der Vercel Plattform notwendig, sowie ein Browser.

Figure: Quelle: 26.01.2022: https://vercel.com/live

Next.js Analytics

Wenn man selbst eine Applikation hat und diese mehreren Benutzern zur Verfügung stellt, möchte man natürlich auch wissen, wie schnell oder auch stabil diese läuft. Hier gibt es das Analytics Feature, um verschiedene Kennwerte zu überwachen. Anhand der gesammelten Werte wird ein Score berechnet, welcher widerspiegelt, wie gut Ihre Applikation performt. All diese Funktionen können Sie verwenden, ohne überhaupt irgendetwas an Ihrem Code zu verändern. Bei diesem Feature ist es nicht einmal notwendig, dass Ihre Applikation bei Vercel deployt wurde, jedoch benötigen Sie dann einen kostenpflichtigen Pro oder Enterprise-Plan. Zusätzlich zu Next.js Applikationen kann dies auch mit Nuxt.js oder Gatsby Anwendungen genutzt werden.

Figure: Quelle: 26.01.2022: https://vercel.com/_next/image?url=https%3A%2F%2Fassets.vercel.com%2Fimage%2Fupload%2Fq_auto%2Ffront%2Finsights%2Fdashboard.png&w=1920&q=75

Vercel Checks

Mit diesem Tool soll verhindert werden, dass es überhaupt zu Fehlern oder zu schlechten Performance-Ergebnissen durch Codefehler kommt. Mit Checks werden automatisch beim Deployment alle Seiten überprüft, bei denen es Änderungen gab. Somit werden nicht immer alle Seiten noch einmal gecheckt, um keine unnötige Zeit zu verlieren. In welchem Zustand sich Ihre Applikation wird anhand Virtual Experience Score (VES) bewertet. Dieser wird durch verschiedene Werte berechnet.

  • FCP - First Contentful Paint

    • beschreibt wie schnell die Seite geladen wird oder wenn der erste Inhalt der Seite dargestellt wird
  • LCP - Largest Contentful Paint

    • beschreibt wie man die Ladegeschwindigkeit wahrnimmt oder bis der gesamte Inhalt der Seite geladen ist
  • CLS - Cumulative Layout Shift

    • beschreibt wie viele Elemente sich noch einmal bewegen, nachdem der User sie sehen kann
  • TTI

    • beschreibt wie lange der Haupt-Thread blockiert war, um eine Eingabe verhindern zu können
  • TBT - Total Blocking Time

    • dies wird anstellte von FID (First Input Delay) verwendet, da dies in einer simulierten Umgebung nicht gemessen werden kann. TBT misst stattdessen die Zeit zwischen dem FCP und der TTI.

Figure: Quelle: 26.01.2022: https://vercel.com/_next/image?url=%2Fdocs-proxy%2Fstatic%2Fdocs%2Fconcepts%2Fdeployments%2Fchecks%2Frunning-checks-step.png&w=1080&q=75

Hands-on

Nun wollen wir Ihnen natürlich zusätzlich noch einige Anwendungsbeispiele für die neuen Funktionen aus dem Release von Next.js 12 zeigen. Dafür erstellen Sie am besten erst einmal eine neue Next-App.

1
npx create-next-app@latest --typescript

Damit wir auch schnell noch ein etwas schöneres Design haben, nutzen wir hierfür das Framework Chakra, welches einige Standardkomponenten bereits mit ausliefert.

1
npm i @chakra-ui/react @emotion/react @emotion/styled framer-motion

Nachdem die Installation abgeschlossen ist, muss die Datei app.tsx angepasst werden, da es für die Verwendung von Chakra einen ChakraProvider um das Component-Tag benötigt. Dies ist notwendig, damit alle Designs und Funktionen auch funktionieren.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
//app.tsx

import '../styles/globals.css'
import type { AppProps } from 'next/app'
import { ChakraProvider } from "@chakra-ui/react"

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ChakraProvider>
        <Component {...pageProps}/>
    </ChakraProvider>
  )
}

export default MyApp

Innerhalb des index.tsx werden als Erstes für das Beispiel einige Todo-Items benötigt, diese werden in dem Beispiel todo genannt. Die Variable item, wird dafür genutzt, um neue Todo-Items zu erstellen, denn darin wird den eingegebenen Wert gespeichert. Anschliessend werden die beiden Methoden removeItem und AddItem benötigt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
//index.tsx
import type { NextPage } from 'next'
import {Button, Flex, ListItem, UnorderedList, Input} from '@chakra-ui/react'
import {useState}from'react'

const Home: NextPage = () => {
  
  const [todos, setTodos] = useState(['Todo Item 1','Todo Item 2','Todo Item 3']);
  const [item, setItem] = useState("");

  function removeItem(todoName: string) {
    setTodos(
      todos.filter((todo) => {
        return todo != todoName;
      })
    );
  }

  function AddItem() {
    if(item != "" && !todos.includes(item)){
      let tmp = todos;
      tmp.push(item);
      setTodos(tmp);
      setItem("");
    }
  }
  return (
    <Flex 
      justifyContent="center"
      alignItems="center"
      width="100%"
      flexDirection="column"
    >
      <UnorderedList>
      {todos.map((todo) => {
        return(
        <ListItem key={todo}>
        {todo}
        <Button ml={10} mb={5} onClick={()=> {removeItem(todo)}}>Remove Item</Button>
        </ListItem>)
      })}
      </UnorderedList>
      <Input
        placeholder="To Do Content"
        value={item}
        m={10}
        onChange={(event) => {
          setItem(event.target.value)
        }}>
      </Input>
      <Button onClick={AddItem}>Add Item</Button>
    </Flex>  
  )
}

export default Home;

Im Anschluss können Sie den lokalen Server starten und im Browser http://localhost:3000/ aufrufen und die Funktionen einer ToDo-Liste ausprobieren.

Erstellen einer Middleware

Am besten lässt sich dies an zwei einfachen Beispielen zeigen. Fangen wir mit einer einfachen Variante an, welche einfach die Response ändert.

Für das erste Beispiel erstellen Sie zunächst einmal ein File mit dem Namen _middleware.ts unter dem Ordner pages.

1
2
3
4
5
6
7
.
...
├── pages
│   ├── _app.tsx
│   ├── _middleware.ts
│   ├── index.tsx
...

Dieses erstellte _middleware-File wird bei jeder Route unter der Route /pages aufgerufen. Somit sollten Sie sich sicher sein, dass dies auch auf jeder anderen Seite notwendig ist. Innerhalb des folgenden Beispiels würde nun nach jedem Request als Response die Nachricht Hello, from b-nvoa ausgegeben werden, anstatt die normale Seite zu laden.

1
2
3
4
5
6
7
//_middleware.ts
import { NextFetchEvent, NextRequest } from 'next/server'
import { stringify } from 'querystring';

export function middleware(req: NextRequest, ev: NextFetchEvent) {
  return new Response('Hello, from b-nvoa!')
}

Anstelle hier eine festgeschriebene Response zurückzugeben, würde hier beispielsweise eine Überprüfung eines JWT-Tokens deutlich mehr Sinn machen. Ruft man nun die Startseite der Applikation wieder auf, dann sieht man nicht mehr die ToDo-Liste, sondern die Nachricht aus der Middleware.

Für dieses Beispiel sollte das vorherige Beispiel auskommentiert werden, da diese Middleware sonst wie bereits erwähnt für alle Routes zählt. Denn nun wollen wir ihnen ein weiteres Beispiel mit redirect zeigen. Hierfür erstellen Sie zunächst einmal eine neue Route, indem Sie einen Ordner mit dem Namen der gewünschten Route anlegen. In diesem Beispiel wird es die Route myos sein. Innerhalb davon benötigen Sie für unser Beispiel eine Standardseite mit dem Namen myosx.tsx und eine Datei mit dem Namen _middleware.ts, welche anschliessend als Middleware speziell für die Route dient. Da wir in diesem Beispiel ein redirect nutzen wollen, benötigt es ein weiteres File mit dem Namen [os].tsx. Anhand diesem soll serverseitig ein Routing stattfinden, ohne das es der Benutzer merkt. Nun sollen solche eine Ordnerstruktur haben:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
...
├── pages
│   ├── _app.tsx
│   ├── _middleware.ts
│   ├── api
│   ├── index.tsx
│   └── myos
│       ├── [os].tsx
│       ├── _middleware.ts
│       └── myos.tsx
...

Für die Standardausgabe der Seite definieren wir einfach einen Text wie diesen hier:

1
2
3
4
5
6
7
8
//myosx.tsx

export default function Myosx() {

  return (
    <h1 >There is no OS.</h1>
  )
}

Bei dem Import von useRouter handelt es sich um eine React-Hook, deshalb ist es auch im nächsten Schritt notwendig, eine neue Instanz zu erstellen. Anhand der URL, welche innerhalb der Middleware neu geschrieben wird, können wir das Betriebssystem auslesen und zurückgeben.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
//[os].tsx

import {useRouter} from "next/router"

export default function Os() {
  const router = useRouter()

  const os = router.query.os
  return (
    <h1 >Your OS: {os}</h1>
  )
}

Jetzt ist es noch die notwendig, URL so anzupassen, damit unser [os].tsx auch die Variable für das Betriebssystem auslesen kann. Um ein Rewrite auch durchführen zu können, benötigt es den Import von NextResponse. Innerhalb der Funktion wird das Betriebssystem aus dem Request ausgelesen und anschliessend findet der rewrite auf die zuvor bestimmte Seite statt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
//_middleware.ts

import { NextResponse } from 'next/server'

const middleware =  (req: any, ev: any) => {
  const os = req.ua.os.name
  return NextResponse.rewrite(`/myos/${os}`)
};

export default middleware;

Über die Entwickler-Tools können wir dies nun mal als iPad darstellen lassen und sehen, dass nun der Inhalt der Seite angepasst wurde ohne Anpassungen der URL im Browser.

ES Modules und URL Imports

Möchte man URL Imports nutzen, muss die Funktion über next.config.ts freigegeben werden, da es sich noch um eine experimentelle Funktion handelt.

1
2
3
4
5
6
7
8
//next.config.ts

module.exports = {
  experimental: {
    urlImports: ['https://cdn.skypack.dev'],
  },

}

Nun wir das skypackExample.tsx erstellt, welche den Import von dem Skypack CDN enthält. Ebenfalls wird hier über einen Button die Funktion confettiaufgerufen.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
//skypackExample.tsx

import confetti from 'https://cdn.skypack.dev/canvas-confetti'
import { useEffect } from 'react'

export default function SkypackExample() {
  useEffect(() => {
    confetti()
  })
  return <p>Hello</p>
}

Default Komponenten

Anstatt dem üblichen a-Tag wird bei Next.js die Link-Komponente angeboten. Diese bietet die Möglichkeiten von automatischen pre-fetching und pre-rendering. Ebenfalls heisst es, dass hierdurch das SEO-Verbessert wird. Des Weiteren können clientseitig Übergänge zwischen den verschiedenen Routen ermöglicht werden.

Weitere Beispiele für Konfigurationen können Sie in der next.js Dokumentation finden.

Images

Ebenfalls gibt es nun eine Image-Komponente anstatt des src-Tags. Hier sind out-of-the-box automatische Layout Optionen wie Vergrösserung je nach Viewport möglich. Ebenfalls gibt es Einstellungen wie loader-Funktionen, die eine URL zurückliefern mit Einstellungen zu src, width und quality. Oder Sie können einen Blur-Übergang nutzen, bis das Bild die optimale Qualität geladen hat. Viele weitere Einstellungen finden Sie auch wieder in der Doku.

Script

Zum Schluss ist noch die Script-Komponente zu erwähnen, welche das src-Tag ablöst. Hier können Sie über das Attribut strategy bestimmen, wann ein 3rd-party Script geladen wird. Oder mithilfe von onLoad können Sie ein Script ausführen, sobald es geladen ist.

1
2
3
4
5
<Script
  id="stripe-js"
  src="https://js.stripe.com/v3/"
  onLoad={handleLoad}
/>

Fazit

Sollte man sich zuvor schon mit verschiedenen JavaScript-Frameworks auseinandergesetzt haben, kennt man die neuen Features von Next.js im Endeffekt schon. Deshalb merkt man, dass solche Funktionen auch diesem Framework sehr gut tun werden. Ebenfalls muss man auch noch anmerken, dass einige der neuen Features sogar noch als experimental eingestuft werden. Man muss auf jeden Fall sagen, dass dieses Update dem Entwickler und aber auch dem Endnutzer sehr entgegenkommen, da ein Unterschied in der Geschwindigkeit erkennbar ist. Ein weiterer Punkt über den man sich im Klaren sein sollte, dass Features wie Live und Analytics hauptsächlich nutzbar sind, wenn man seine Applikation auch bei Vercel hostet.

Ricky Elfner

Ricky Elfner – Denker, Überlebenskünstler, Gadget-Sammler. Dabei ist er immer auf der Suche nach neuen Innovationen, sowie Tech News, um immer über aktuelle Themen schreiben zu können.