ukryj menu
SPEC
aktualizacja: 2022-02-11 09:17:48
0. film na YT
https://youtu.be/moSrrB1qDeQkopiuj
- React zajmuje się wyłącznie klientem, do pełnej aplikacji potrzeba warstwy serwerowej, autentykacji, routingu etc
- NextJS - używa Reacta do budowy komponentów
Najważniejsze możliwości NextJS
a) server side rendering
- nie jest to client - server w osobnych aplikacjach tylko jeden projekt zawierający nextjs i kod serwera
- jest wbudowany mechanizm do tworzenia REST API
- zoptymalizowane pod kątem silników wyszukiwarek, które widzą właściwą zawartość html zamiast js-a- pobranie danych i render strony html odbywa się na serwerze
b) file based routig
- w NextJS struktura routera jest zapewniana przez strukturę folderów i plików w katalogu /pages na serwerze, co jest zrozumiałe i działa intuicyjnie jak na zwykłych stronach html
c) full stack framework
d) składnia es6
2. start pracy
tworzymy aplikację w dowolnym folderze, chwilę to trwa:
npx create-next-app app001kopiuj
po zainstalowaniu można uruchomić domyślną aplikację na dev serwerze, która jest kompilowana do folderu .next
npm run devkopiuj
lub zbudować produkcyjną wersję i ją uruchomić
npm run build
npm startkopiuj
3. aplikacja - struktura plików
{appDir}
|
|__ pages // Miejsce w którym zaczyna się routing aplikacji
|__ api // W Next.js możemy zrobić REST API i jego obsługa powinna znaleźć się w tym folderze
|__ index.js // jest to strona widoczna pod adresem root "/"
|__ public // do przechowania danych statycznych jpg, favicon, js
|__ components // komponenty bez swojego rout-a wykorzstywane na stronach /pages
|__ styles // style
|__ global.css // styl globalny dla całej aplikacji
|__ <page/component>.module.css // styl dla określonej strony - musi zostać zachowana konwencja nazewnictwakopiuj
|
|__ pages // Miejsce w którym zaczyna się routing aplikacji
|__ api // W Next.js możemy zrobić REST API i jego obsługa powinna znaleźć się w tym folderze
|__ index.js // jest to strona widoczna pod adresem root "/"
|__ public // do przechowania danych statycznych jpg, favicon, js
|__ components // komponenty bez swojego rout-a wykorzstywane na stronach /pages
|__ styles // style
|__ global.css // styl globalny dla całej aplikacji
|__ <page/component>.module.css // styl dla określonej strony - musi zostać zachowana konwencja nazewnictwakopiuj
Nazewnictwo:
pages - z małej litery
Komponenty- z Dużej
Style - <komponent>.module.css
w tej chwili istotny jest folder /pages
zwróćmy uwagę że nie ma pliku index.html - odpowiednie podstrony renderowane są przez nextjs w locie, w odpowiedzi na requesty
dla jasności pracy, na początku usuwamy folder pages/api oraz styles/Home.module.css
oraz zawartość pages/index.js
4. index.js - wejście do aplikacji
main page - route /
function Home() {
return <h1>simple home page</h1>
}
export default Homekopiuj
nie potrzebujemy import react from "react"
do konstrukcji pages używamy zwykłych funkcji a nie strzałkowych
uruchom aplikację i zobacz w przeglądarceskompilowana strona znajduje się w katalogu .next/server/pages/index.html
5. file based routing
do folderu pages dodajemy dwa pliki
index.js
kopiuj
login.jskopiuj
struktura aplikacji
{appDir}
|
|__ pages
|__ index.js
|__ login.jskopiuj
|
|__ pages
|__ index.js
|__ login.jskopiuj
routing będzie taki:
/
/loginkopiuj
6. struktura projektu oparta na folderach
zamiast plików js można utworzyć strukturę folderów, co przydaje się w większej aplikacji i tak jest przejrzyściej
{appDir}
|
|__ pages
|__ login
| |__ index.js
|__ register
|__ index.jskopiuj
routing
/login
/registerkopiuj
ogólnie podfoldery będą się zachowywać jak router
7. dynamic paths
zakładamy że strona
/news/index.js widoczna pod adresem /news
daje informację o wszystkich newsachnewsów może być wiele, ale strona jednego newsa powinna być jedna, więc
do określenia, że jest to dynamiczna ścieżka, nextJS potrzebuje nawiasów [] w nazwie pliku
/news/[newsid].jskopiuj
w takim razie
odczyt danych z adresu wygląda w tym pliku tak:
import { useRouter } from "next/router"
function DetailsPage() {
const router = useRouter()
console.log(router.query.newsid);
return <h1> page in folder news - dynamic page {router.query.newsid}</h1>
}
export default DetailsPage;kopiuj
8. standardowe linki a href - wysył requesta do serwera
dodajmy plik pages/test/index.js
w nim
function TestPage() {
return (
<>
<h1>page in /test folder</h1>
<ul>
<li><a href="/news/i-like-next-js">i like next.js - A HREF</a></li>
<li><a href="/news/i-like-react-js">i like react.js - A HREF</a></li>
<li><a href="/news/i-likeexpress-js">i like express.js - A HREF</a></li>
</ul>
</>
)
}
export default TestPagekopiuj
w powyższym wypadku tracimy SPA (single page application) czyli mamy przeładowanie strony i przy każdej stronie nowy request do serwera, automatycznie tracimy też ewentualny state w komponentach
9. linki SPA
pozwalaja na lepszy user-experience
zachowują state między stronami
wyszukiwarki indeksują zawartość strony
Link: renderuje odpowiedni komponent oraz zmienia url w pasku adresu
podmień w pliku pages/test/index.js
i zaobserwuj działanie przeglądarki
import Link from "next/link"kopiuj
<li><Link href="/news/i-like-next-js">i like next.js - A HREF</Link></li>
<li><Link href="/news/i-like-react-js">i like react.js - A HREF</Link></li>
<li><Link href="/news/i-likeexpress-js">i like express.js - A HREF</Link></li>kopiuj
10. Head
wróćmy do pliku pages/index.js
wykorzystując wbudowany komponent Head (jest takich wbudowanych kilka, patrz dokumentacja https://nextjs.org/docs/api-reference/next/head)
import Head from 'next/head'
import styles from '../styles/Home.module.css'
function Home() {
return (
<div className={styles.container}>
<Head>
<title>Home Page</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
<meta name='keywords' content='nextjs, head' />
</Head>
<h1 className="styles.title"> Home Page </h1>
</div>
)
}
export default Homekopiuj
plik /styles/Home.module.css
.container {
margin: 0;
padding: 0;
background: blueviolet
}kopiuj
jak widać dla każdego route’a możemy zmienić zawartość sekcji head za pomocą
wbudowanego komponentu <Head>
<Head>
<title>Home Page</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
<meta name='keywords' content='nextjs, head' />
</Head>kopiuj
11. state
a) strona About używajaca state, wykonana za pomocą komponentu klasowego
plik pages/about.js
import Head from 'next/head'
import React from 'react'
import styles from '../styles/About.module.css'
class About extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'hello world'
};
console.log(props)
}
render() {
return (
<div className={styles.container}>
<Head>
<title> About Page </title>
</Head>
<h1> About </h1>
<h2> {this.state.value} </h2>
</div>
);
}
}
export default Aboutkopiuj
b) wykonaj to samo komponentem funkcyjnym
Pobieranie danych z REST API różni się od klasycznego Reacta.
Next wstrzykuje sobie te dane jako props z zewnętrznej funkcji.
Potrzebne będzie użycie wbudowanej funkcji getStaticProps() - jest to funkcja zwracająca obiekt z polem props Taką funkcje należy umieścić pod kodem komponentu.
export const getStaticProps = async () => {
const res = await fetch(`https://jsonplaceholder.typicode.com/posts?_limit=4`);
const posts = await res.json();
return {
props: {
posts
}
}
}kopiuj
cała aplikacja pobierająca posty
import Head from 'next/head'
import Item from "../components/Item"
import React from 'react'
import styles from '../styles/Posts.module.css'
class Posts extends React.Component {
constructor(props) {
super(props);
console.log(props)
}
render() {
return (
<div>
{
this.props.posts.map((post) => {
return <Item post={post} />
})
}
</div>
)
}
}
export default Postskopiuj
plik /components/item.js
import React, { Component } from 'react';
export default class Item extends Component {
render() {
console.log(this.props);
return (
<div>
<h3>{this.props.post.id}</h3>
<h4>{this.props.post.title}</h4>
<h4>{this.props.post.body}</h4>
</div>
);
}
}kopiuj
zadanie: wykonaj całość z użyciem komponentów funkcyjnych
NextJS posiada gotowy mechanizm wystawienia klasycznego REST API
Wszystkim co musimy zrobić jest:
W katalogu pages/api tworzymy folder o nazwie tego co będziemy wystawiać a
nim index.js i [id].js
poniżej krok po kroku utworzenie REST API i korzystającej z niego aplikacji
a) dane wstaw w pliku /pages/api/avatars/avatars.json
[
{
"id": "aatrox",
"name": "Aatrox",
"title": "the Darkin Blade",
"icon": "http://ddragon.leagueoflegends.com/cdn/10.23.1/img/champion/Aatrox.png",
"description": "Once honored defenders of Shurima against the Void, Aatrox and his brethren would eventually become an even greater threat to Runeterra, and were defeated only by cunning mortal sorcery. But after centuries of imprisonment, Aatrox was the first to find..."
},
{
"id": "ahri",
"name": "Ahri",
"title": "the Nine-Tailed Fox",
"icon": "http://ddragon.leagueoflegends.com/cdn/10.23.1/img/champion/Ahri.png",
"description": "Innately connected to the latent power of Runeterra, Ahri is a vastaya who can reshape magic into orbs of raw energy. She revels in toying with her prey by manipulating their emotions before devouring their life essence. Despite her predatory nature..."
},
{
"id": "akali",
"name": "Akali",
"title": "the Rogue Assassin",
"icon": "http://ddragon.leagueoflegends.com/cdn/10.23.1/img/champion/Akali.png",
"description": "Abandoning the Kinkou Order and her title of the Fist of Shadow, Akali now strikes alone, ready to be the deadly weapon her people need. Though she holds onto all she learned from her master Shen, she has pledged to defend Ionia from its enemies, one..."
},
{
"id": "alistar",
"name": "Alistar",
"title": "the Minotaur",
"icon": "http://ddragon.leagueoflegends.com/cdn/10.23.1/img/champion/Alistar.png",
"description": "Always a mighty warrior with a fearsome reputation, Alistar seeks revenge for the death of his clan at the hands of the Noxian empire. Though he was enslaved and forced into the life of a gladiator, his unbreakable will was what kept him from truly..."
},
{
"id": "amumu",
"name": "Amumu",
"title": "the Sad Mummy",
"icon": "http://ddragon.leagueoflegends.com/cdn/10.23.1/img/champion/Amumu.png",
"description": "Legend claims that Amumu is a lonely and melancholy soul from ancient Shurima, roaming the world in search of a friend. Doomed by an ancient curse to remain alone forever, his touch is death, his affection ruin. Those who claim to have seen him describe..."
}
]
kopiuj
b) plik /pages/api/avatars/index.js
import avatars from "./avatars.json"
console.log(avatars);
export default function (req, res) {
res.status(200).json(avatars)
}kopiuj
route
http://localhost:3000/api/avatarskopiuj
wyszukanie jednego avatara:
c) plik /pages/api/avatars/[id].js
import avatars from "./avatars.json"
export default function (req, res) {
let id = req.query.id;
const result = avatars.filter(av => av.id == id)
if (result.length > 0)
res.status(200).json(result[0])
else
res.status(404).json({ message: 'brak avatara o takim id' })
}kopiuj
adres
d) użycie tych danych w aplikacji poza folderem api
plik /pages/av/index.js
import React from 'react'
import Avatar from "../../components/Avatar"
class Av extends React.Component {
constructor(props) {
super(props);
console.log(props)
}
render() {
return (
<div>
{
this.props.avs.map((data) => {
return <Avatar data={data} />
})
}
</div>
)
}
}
export default Av
export const getStaticProps = async () => {
const res = await fetch(`http://localhost:3000/api/avatars`);
const avs = await res.json();
return {
props: {
avs
}
}
}
kopiuj
plik /components/Avatar.js
import React, { Component } from 'react';
import styles from '../styles/Avatar.module.css'
export default class Avatar extends Component {
render() {
console.log(styles.av);
return (
<div className={styles.av}>
<h1>{this.props.data.id}</h1>
<h4>{this.props.data.name}</h4>
<h4>{this.props.data.title}</h4>
<img src={this.props.data.icon} />
</div >
);
}
}kopiuj
zadanie: wykonaj całość z użyciem komponentów funkcyjnych
14. pobieranie danych - różne sposoby
warto poczytać
https://nextjs.org/docs/basic-features/pages#static-generation-recommended
https://nextjs.org/docs/basic-features/data-fetching