NextJS
Next.js - założenia
Framework React produkt firmy Zeit (obecnie Vercel). Wszechstronne narzędzie do budowania dynamicznych stron WWW.
Na reddicie ukazał się post pod nieco prowokacyjnym tytułem: "Does NextJS will be soon obsolete?". Dokładnej odpowiedzi wyjaśniającej podstawowe założenia i działanie udzielił jeden z głównych deweloperów Next.js.
- *Kompilator- komponenty React są kompilowane przy użyciu webpacka i Babel, dzięki temu odbywa się to szybko i zachowane są globalne zależności
- *SSR- On-demand rendering (traditional SSR) każde żadanie powoduje wyrenderowanie unikalnej strony, wymaga to działającego serwera Node.js.
- *Eksport do dokumentów statycznych- (static exporting) wszystkie strony mogą być wyświetlone jako zwykły html.
- *Automatyczne rozdzielenie kodu- (automatic code splitting) cała struktura strony jest podzielona na niewpływające na siebie elementy, które sa wyświetlane dynamicznie niezależnie od ich liczby. Jeżeli z jakimś elementem jest cos nie tak nie wpływa to na resztę.
- *Wymiana modułów- (hot module replacement) automatyczne przeładowanie modułów bez konieczności odświeżania przeglądarki.
- *Routing / prefetching- routing wewnątrz katalogu może przebiegać automatycznie, w stylu SPA, Możliwy jest także prefetching.
- *Dynamiczne wyświetlenie dokumentów- (dynamic loading) jak w React.lazy ale bardziej optymalizowane dla SSR.
- CSS w każdy dostępny sposób.
- Manipulacja nagłówkiem strony.
- Asynchroniczne pobieranie danych przed ich wyświetleniem (data fetching).
- Zarządzenie wyświetlaniem błedów.
- Serverless
- Serverful - możliwe jest także bardziej tradycyjne podejście wszystkie routingi są zarządzane przez jeden serwer.
- Optymalizacja dla produkcji.
- *Optymizacja podczas dewelopmentu- - kompilowanie na żądanie (on-demand); uruchomienie serwera deweloperskiego nie powoduje kompilacji wszystkich składników strony, np podczas każdego zapisu, kompilowane są dopiero kiedy przeglądarka je uruchamia, to znacznie przyspiesza działanie procesu.
- Zones / Microfrontends - można wprowadzać stopniowo, pojedynczymi elementami
- Stabilność zapewniona jest podczas rozwoju przez wszechstronne testowanie .
Użytkownicy Next.js to m.in. Hulu, Marvel, Docker i npm.
Instalacja
Poniższe polecenie w katalogu bieżącym utworzy katalog aplikacji o nazwie projektu i w nim instaluje react, react-dom i next:
npx create-next-app
npx: zainstalowano 1 w 3.446s
✔ What is your project named? … // tu podajemy nazwę projektu
Creating a new Next.js app in // tu pojawia się ścieżka dostępu
Potem widzimy następujące komunikaty, które są instrukcją co zrobić dalej:
Installing react, react-dom, and next using npm...
> core-js@2.6.10 postinstall /sciezka/dostepu/Nextjs/next-app/node_modules/core-js
> node postinstall || echo "ignore"
+ react@16.11.0
+ react-dom@16.11.0
+ next@9.1.2
added 757 packages from 356 contributors and audited 10252 packages in 45.785s
found 0 vulnerabilities
Success! Created next-app-01 at /sciezka/dostepu/Nextjs/next-app
Inside that directory, you can run several commands:
npm run dev
Starts the development server.
npm run build
Builds the app for production.
npm start
Runs the built app in production mode.
We suggest that you begin by typing:
cd next-app
npm run dev
✔ What is your project named? … next-app-js3
✔ Pick a template › Default starter app
Creating a new Next.js app in /media/tadek/Nextjs/next-app-js3.
Installing react, react-dom, and next using npm...
+ react-dom@16.13.1
+ react@16.13.1
+ next@9.3.5
added 782 packages from 307 contributors and audited 8527 packages in 57.389s
29 packages are looking for funding
run \`npm fund\` for details
found 0 vulnerabilities
Initialized a git repository.
Success! Created next-app-js3 at /media/tadek/Nextjs/next-app-js3
Inside that directory, you can run several commands:
npm run dev
Starts the development server.
npm run build
Builds the app for production.
npm start
Runs the built app in production mode.
We suggest that you begin by typing:
cd next-app-js3
npm run dev
Całość to ok 50 MB. Od razu będziemy mieli gita, i .gitignore z dodanymi node_modules. W zależnościach w package.json są tylko: react, react-dom i next.
Budowanie strony
Potem
$ cd katalog_aplikacji
$ npm run dev
> next-app-js3@0.1.0 dev /scieżka_dostępu/katalog_aplikacji
> next dev
[ wait ] starting the development server ...
[ info ] waiting on http://localhost:3000 ...
[ ready ] compiled successfully - ready on http://localhost:3000
[ wait ] compiling ...
[ ready ] compiled successfully - ready on http://localhost:3000
[ event ] build page: /
[ wait ] compiling ...
[ ready ] compiled successfully - ready on http://localhost:3000
I to wszystko, strona/aplikacja jest skompilowana i gotowa do użycia. Polecenie cały czas działa w tle, kompilując aplikacje na bieżąco po każdej dokonanej i zapisanej zmianie. Kiedy wejdziemy na wskazany adres zobaczymy wygenerowany domyślną stronę przygotowana przez Zeit: jest to /pages/index.js.
Najprostszy dokument wygląda tak:
function Home() {
return <div>Welcome to Next.js!</div>
}
export default Home;
}
export default Home;
Jak widać jest to zwykła funkcja, która zwraca JSX i na końcu jej wynik jest eksportowany.
Istotą tworzenia strony jest zagnieżdżanie komponentów dostępnych globalnie w obrębie aplikacji dzięki importom i eksportom. Wszystkie znajdują się w katalogu /components (jego nazwa jest arbitralna, po prostu musi być używana konsekwentnie). W taki sposób można stworzyć np. komponent menu używający elementu link, tutaj przykład menu zawierającego nielinkujące elementy i stylowanie:
import Link from "next/link";
const links = [
{ label: "Tytuł menu", className: "title" },
{ href: "/page01", label: "Strona 01" },
{ href: "/page02", label: "Strona 02" },
].map(link => {
link.key = \`nav-link-$\{link.href\}-$\{link.label\}\`;
return link;
});
const Navbar = () => (
<nav className="navigation_one">
<ul>
{links.map(({ key, href, label, className }) => (
<li key={key} className={className}>
<Link href={href}>
<a>{label}</a>
</Link>
</li>
))}
</ul>
</navl>
);
export default Navbar;
Potem taki komponent nawigacyjny można umieścić w komponencie Header, a ten z kolei w Layout:
import Header from "./header";
import Sidebar from "./sidebar";
import Footer from "./footer";
import "../style.css";
const Layout = props => (
<div className="container">
<Header />
<main>
<Sidebar />
<article className="article">{props.children}</article>
</main>
<Footer />
</div>
);
export default Layout;
W prosty sposób można stworzyć szablon strony, nad którą ma się całkowitą kontrolę. Importy zawsze dotyczą wykorzystywanych właściwości React lub Next.js, komponent Link byl importowany kiedy użyto go w szablonie nawigacji, a style aplikują się do złożonego w całość szablonu layoutu strony. Wszystkie elementy używane globalnie trzymamy w /components, a pliki stron w /pages.
Kluczowe znaczenie ma tu obiekt props.children, czyli miejsce, w którym zostanie umieszczona unikalna treść podstrony. Mając taki schemat możemy utworzyć pliki stron, wg powyższych reguł, które w returnie mają tylko tagi <Layout> i treść:
<Layout>
tu właściwa treść podstrony...
</Layout>
Ponieważ jest to React, kod w returnie (komponent klasowy) musi być zawarty w nawiasach i w jednym nadrzędnym tagu, np. React Fragment.
return (
<>
[kod tu]
</>
)
Albo w funkcji strzałkowej (komponent funkcyjny) w jednym nadrzędnym elemencie HTML
const Home = () => (
<div>
[kod tu]
</div>
CSS
W przypadku CSS mamy dostępne właściwie wszystkie istniejące możliwości. CSS możemy linkować statycznie z HTML-owego elementu szablonu layoutu (tag link).
Można też importować do modułu (import "sciezka/dostepu/styles.css";), ale wtedy trzeba doinstalować moduł next-css
npm install --save @zeit/next-css
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.9 (node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.9: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
+ @zeit/next-css@1.0.1
added 22 packages from 14 contributors and audited 10440 packages in 16.803s
found 0 vulnerabilities
next.config.js - tutaj także załączony raw-loader dla plików .txt i .md.
/- eslint-disable */
const withCss = require("@zeit/next-css");
module.exports = withCss({
publicRuntimeConfig: {
// ...
},
webpack: (config, { isServer }) => {
config.module.rules.push({
test: /\.(md|txt)$/,
use: "raw-loader"
});
return config;
}
});
Można też umieścić CSS w samym pliku szablonu lub podstrony w elemencie JSX:
<style jsx>;{`
tutaj style ...
`}</style>;
App.js - /pages/_app.js
Inaczej niż w Express, Next.js nie ma głównego pliku z konfiguracją takiego jak App.js. A ściśle rzecz biorąc nie jest on udostępniany użytkownikowi, nie mamy do niego dostępu. Ale można go nadpisać własnym: /pages/_app.js (nazwa obligatoryjna). Może on wyglądać tak jak poniżej, tutaj przedstawiony jest _app.js, który jednocześnie jest komponentem Layout:
import React from "react";
import App from "next/app";
import Header from "../components/header";
import Footer from "../components/footer";
import "../styles/style.css";
class Layout extends App {
render() {
return (
[tu JSX]
)
}}
export default Layout;
Taki szablon layoutu ma przewagę nad domyślnym bo jest komponentem klasowym. Trzeba pamiętać, żeby go w plikach stron zaimportować. Używamy go wtedy zamiast dotychczasowego szablonu layout.
Link.active i scrollspy
Next.js niestety nie udostępnia w komponencie Link możliwości automatycznego przypisywania klasy .active dla aktywnego elementu menu. Najprostszym rozwiązaniem jest zastosowanie skryptu autorstwa Flavio Copesa - "Using the router to detect the active link in Next.js". Wystarczy skopiować go do katalogu komponentów pod nazwą Link.js (przykładowo) i zaimportować go w komponencie menu zamiast systemowego komponentu Link.
Stosowanie scrollspy jest w Next.js utrudnione dlatego, że mamy do czynienia z dynamicznie renderowaną stroną w Node.js, który nie posiada obiektów przeglądarki. Tutaj rozwiązaniem jest moduł react-scrollspy. Wystarczy go zainstalować, zaimportować. Komponent Scrollspy jest listą, można mu nadać własną klasę. Wymaga komponentu Link.
Pojawia się jednak problem jeżeli używamy scrollspy jako spisu treści podstrony. Trzeba wtedy umieścić ten komponent w każdym dokumencie, a nie w layoucie i w każdej instancji edytować, ręcznie wpisując i adresy i tytuły linków. Przynajmniej część tej pracy można zautomatyzować. Można przy tym zrezygnować z komponentu Link, trzeba będzie bowiem przepisać szablon komponentu scrollspy. Tytuły odnośników umieszczamy w tablicy, dodajemy funkcję, która pobiera elementy tablicy i tworzy inną tablicę, którą możemy mapować już wewnątrz komponentu na odnośniki. Wygląda to tak:
// import komponentu
import Scrollspy from "react-scrollspy";
// tablica z tytułami rozdziałów dokumentu
let items = [
"Zażółć gęślą jaźń",
'Ścichapęk "o2o27(oo0("',
"Odnośnik 3"
// ... itd
];
// utworzenie pustej, docelowej tablicy
let itemsList = [];
// funkcja rozpisująca pierwotną tablice na docelową
for (let i = 0; i < items.length; i++) {
let item = { key: i, href: `#${items[i]}`, label: items[i] };
itemsList.push(item);
}
// komponent z mapowaniem
const ScrollSpy = () => (
<Scrollspy items={items} currentClassName="current" className="scrollspy">
{itemsList.map(item => (
<li key={item.key}>
<a href={item.href}>{item.label}</a>
</li>
))}
</Scrollspy>
);
// eksport i koniec pliku
export default ScrollSpy;
Dla pełnej automatyzacji wystarczy pobrać tytuły odnośników JS przeglądarki. Zakładając, że treść dokumentu znajduje się w tagu article i każdy rozdział jest zatytułowany nagłówkiem H3, taka funkcja może wyglądać następująco:
const items = [];
const makeList = () => {
const listOfElements = [...document.querySelectorAll("article>h3")];
for (let i = 0; i < listOfElements.length; i++) {
itemsList.push(listOfElements[i].id);
}
Jak widać wymaga to konsekwentnego stosowania konwencji, w której adres hiperłącza jest identyczny z tytułem. Przesłanie jej do powyższego komponentu Node.js to jeszcze jest TODO.
Router
import { useRouter } from "next/router";
import Layout from "../../components/layout";
export default function Post() {
const router = useRouter();
return (
<Layout>
<h1>{router.query.id}</h1>
<p>This is the blog post content.</p>
</Layout>
);
}
Fetch
import Layout from "../components/layout";
import Link from "next/link";
import fetch from "isomorphic-unfetch";
const Index = props => (
<Layout>
<h1>Batman TV Shows</h1>
<ul>
{props.shows.map(show => (
<li key={show.id}>
<Link href="/p/[id]" as={\`/p/$\{show.id}\`}>
<a>{show.name}</a>
</Link>
</li>
))}
</ul>
</Layout>
);
Index.getInitialProps = async function() {
const res = await fetch("https://api.tvmaze.com/search/shows?q=batman");
const data = await res.json();
// console.log(\`Show data fetched. Count: $\{data.length}\`);
// console.log(data);
return {
shows: data.map(entry => entry.show)
};
};
export default Index;
import Link from "next/link";
import Layout from "../components/layout";
const PostLink = props => (
<li>
{/- <Link href={`/post?title=${props.title}`}> */}
<Link href="/p/[id]" as={`/p/${props.id}`}>
<a>{props.id}</a>
{/- <a>{props.title}</a> */}
</Link>
</li>
);
export default function Index() {
return (
<Layout>
<h2>Hello Next.js</h2>
<ul>
<PostLink id="Post title blah01" />
<PostLink id="This is the next post title" />
<PostLink id="Not a last, not just a next. Just" />
</ul>
</Layout>
);
}
Odnośniki
- Next.js - The React Framework | zeit/next.js - Introducing Create Next App
- react-next-boilerplate/react-next-boilerplate
- Bulletproof Next Be an expert in Next.js
- Learn Next.js – A free video course on React and Next.js.
- DevNews Tag Nextjs
Tutoriale
- The Net Ninja "Next.js Tutorial for Beginners" [YT playlist 14 filmów]
- Samuraj Programowania Mateusz Domański "Krótki kurs Next.js" [YT 48:15] | "Krótki kurs Next.js cz.2" [YT 1:25:07]
- Academind "Next.js Crash Course for Beginners 2021 - Learn NextJS from Scratch in this 100% Free Tutorial!" [YT 3:14:16]
- The new @nextjs layout by example: 👇
Artykuły
- "What Is Next Js"
- "Next.js Vs. Gatsby Vs. Create React App"
- "Why I chose React + Next.js for My Next Website"
- Medium: The most insightful stories about Nextjs | Francisco Suarez "Next.JS What is it?" | "Headless WordPress + Next.js — What We Learned" | "Build a Next.js Website in 4 Steps" | Christopher Heppell "Next.js development with instant dev-database provisioning" | "Next.js Website Boilerplate" | "Next.js development with instant dev-database provisioning" | jlei523 "6 tips: Using Next.js for your next web app" | "Next.js — React Server-Side Rendering Done Right" | Flavio Copes "Discover Next.js and write server-side React apps the easy way" | "Move over Next.js and Webpack!!" | "Nextjs for everyone — with some basic knowledge of React" | "We migrated to Next.js to serve our home page 7.5× faster" | Ryosuke "NextJS Tip - Using Media (Images & Fonts) in CSS" | Andréas Hanss "Back-office with NextJS (React) + ElasticUI (EUI)" | Brandon Duffany "The Hitchhiker’s Guide to Next.js" | Akash Raju M "Easily deploy express API as a serverless function for free using ZEIT" | Ali Nawaz "Hacking Next.js for better PageSpeed scores" | Giuliano Kranevitter "Why you should use Gitlab + Now + Next.js" | Danstan Onyango "NextJS OAuth with Passport and Github" | Xiaoru Li "🔑 Passwordless Authentication with Next.js, Prisma, and next-auth" | Jakub Skoneczny "The easiest way to internationalize your NextJS application! 🌎"
- "Getting Started with React and Next.JS — Part #1 of 11"
- Simon Knott "Integrating Next.js and NestJS"
- Flavio Copes: sAll the Next.js tutorials
- Robin Wieruch: "Redux Persist with Next.js by Example"
- Mehul Mohan "Next.js production tips and checklist"
- Lessons Learned Building in Next.js: "Part 1: Introduction and Initial Gotchas" | "Part 2: Ajax, Styling, and Routing"
- morioh: Alfie Kemp "What is Next.js and How it Works?" | "Getting Started with Next.js - The React Framework" | "What's new in Next.JS v9.2"
- web.dev: Houssein Djirdeh "Improved Next.js and Gatsby page load performance with granular chunking"
- Catalin Pit "A Comprehensive Guide On Setting Up Next.js With TypeScript And TailwindCSS"
- Ruben Leija: "A guide to navigating with Next JS links" | "How to redirect on the server side with Next.JS" | "How to add robots.txt and sitemaps to the latest version of Next.js"
- Next.js Practical Introduction: "Pages and Layout" | "Styling and Theming" | "Navigation and Routing" | "Authentication Tutorial"
- TechSolutionsHere.com: "How To The Active Menu Based On URL In Next.JS?" | "How To Setup Redux In React Next Application"
- Maxime Castres "Build a blog with Next (React.js), Strapi and Apollo"
- Flavio Copes The Next.js Handbook | "How to install Next.js"
- "Using a pipe function in Next.js getServerSideProps"
- Isaac Scarrott "Next.js Server-Side Route Authentication Using Cookies"
- Cassidy Williams "Next.js 101: What you should know"
- Lee Robinson "Building a Blog with Cosmic CMS and Next.js"
- Tomasz Łakomy "Build a site from scratch with Next.js, TypeScript, Emotion and Netlify"
GraphQL
Youtube
- *Kanały- Bruno Antunes [YT 3]
- *Playlisty- codedamn "Next.js Tutorials" [YT playlist 18 filmów] | Leigh Halliday "Next.js" [YT playlist 12 filmów] | Imran Sayed "Next.js Tutorials for Beginners | Server Side React | Next JS Advanced | Tutorial Series | SSR | Server Side Rendering" [YT playlist 9 filmów] | Bruno Antunes "Next.js Building a Car Trader Application" [YT playlist 6 filmów] | Artur Chmaro "Serverless" [YT playlist 11 filmów] | Watch and Learn "Making Websites With Next.js And Strapi" [YT playlist 20 filmów] | Waldev "NextJs Tutorial" [YT playlist 4 filmy] | Ado Kukic "Next.js" [YT playlist 5 filmów] | Lee Robinson "Mastering Next.js" [YT playlist 13 filmów] | Patrick M "Next.js" [YT 3 filmy]
- *Filmy- freeCodeCamp.org "Next.js for Beginners - Full Course" [YT 2:38:24] | Traversy Media "Next.js Crash Course 2021" [1:09:44] | Artur Chmaro "⚡️ Next.js czyli web framework przyszłości (Promeet #30)" [YT 1:21:39] | Code Bushi "Next.js Server Side Rendering and getInitialProps" [22:03] | "liveCoding #1.1 Publishing a website to the internet in less than 30 minutes with now/nextjs/react" [YT 31:36] | Lee Robinson "Why Next.js Is the Future of React" [YT 9:28]
Narzędzia
- Next.js Boilerplate
- xeoneux / next-dark-mode Enable dark mode for Next.js apps
- Tixpire / strapi-middleware-nextjs Strapi Middleware to use nextJS as the front end framework for strapi.