NextJS
бертка над React, что-то типо GatsbyJS в плане билдинга. На выходе получается не чистый Virtual DOM, а смесь react и статичного html.
- NextJS Docs - nextjs.org
- NextJS Templates - vercel.com/templates
- SSR нормальный, нет таких проблем как в CRA
- SEO frendly
- Встроенный роутинг
Install with TypeScript (old):
yarn create next-app --typescript
Install with TypeScript (new):
now ships with TypeScript by default.
npx create-next-app@latest project-name
Install 1 old (create-next-app)
npx create-next-app nextjs-blog --use-npm --example "https://github.com/vercel/next-learn/tree/master/basics/learn-starter"
cd nextjs-blog
npm run dev
Install 2 old (manual)
npm init -y
npm i --save react react-dom next
./pages/index.js
package.json
Pages
Static pages
// index.tsx
const Index = () => {
return (
<div>
<h1>Hello, Nextjs!!!</h1>
</div>
);
}
export default Index;
_app page
// _app.tsx - обертка
import '../styles/global-styles.css'
// This default export is required in a new `pages/_app.js` file.
export default function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
404 page
// 404.tsx
export default function Error() {
return (
<h1>404 - Page not found</h1>
)
}
Dynamic pages
pages/users/index.tsx
- users list static page
pages/users/[id].tsx
- users detail dynamic page
Link
import Link from 'next/Link';
export default function LinkCustom({ text, href }) {
return (
<Link href={`/${href}`}>
<a>{text}</a>
</Link>
);
}
Styling
Styling Inline styling
const Index = () => {
return (
<div>
<h1 className="title">Hello, Nextjs!!!</h1>
</div>
<style jsx>
{`
.title {
color: maroon;
background-color: white;
}
`}
</style>
);
}
export default Index;
Styling (_app.js) Если в pages создать файл _app.js, то он будет оберткой для всего приложения. Можно подключать к нему глобальные стили.
import '../styles/global-styles.css'
// This default export is required in a new `pages/_app.js` file.
export default function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
Sass npm i --save sass
import Link from 'next/Link';
import styles from '../styles/LinkCustom.module.scss';
export default function LinkCustom({ text, href }) {
return (
<Link href={`/${href}`}>
<a className={styles.LinkCustom}>{text}</a>
</Link>
);
}
Import module local styles
import Link from 'next/Link';
import styles from '../styles/LinkCustom.module.css';
export default function LinkCustom({ text, href }) {
return (
<Link href={`/${href}`}>
<a className={styles.LinkCustom}>{text}</a>
</Link>
);
}
Routing
Routing Static routing pages/users/index.jsx || pages/users.jsx
import Link from 'next/Link';
export default function staticRouting() {
return (
<>
<Link href="/">
<a>Homepage</a>
</Link>
<Link href="/users">
<a>Users</a>
</Link>
</>
)
}
Dynamic routing pages/users/index.jsx || pages/users/[id].jsx
import Link from 'next/Link';
export default function staticRouting() {
return (
<Link href={`/users/${user.id}`}>
<a>{user.name}</a>
</Link>
)
}
useRouter // users/[id].js
import { useRouter } from 'next/router';
export default function userId() {
// const router = useRouter();
const { query } = useRouter();
const { id } = query;
console.log('id', id);
return (
<div>
<h1>User detail</h1>
<div>{`User id: ${id}`}</div>
</div>
)
}
SEO
import Head from 'next/head';
<Head>
<meta keywords="keyword1, keyword2, keyword3" />
<title>NextJs - Test</title>
</Head>
MainContainer.js
import Head from 'next/head';
import LinkCustom from './LinkCustom';
export default function MainContainer({ children, keywords }) {
return (
<>
<Head>
<meta keywords={keywords} />
<title>NextJs - Test</title>
</Head>
<header>
<ul className="navbar">
<li>
<LinkCustom text="Index" href="" />
</li>
<li>
<LinkCustom text="Users" href="users" />
</li>
</ul>
</header>
<div>{children}</div>
{/* inline styling */}
<style jsx>
{`
.navbar {
display: flex;
list-style: none;
margin: 0;
padding: 0;
}
`}
</style>
</>
);
}
Use MainContainer
// index.js
import MainContainer from '../components/MainContainer';
const Index = () => {
return (
<MainContainer keywords='index page'>
<div>
<h1 style={{ color: 'maroon' }}>Hello, Nextjs!!!</h1>
</div>
</MainContainer>
)
}
export default Index;
Next SEO npm install --save next-seo
import { NextSeo } from 'next-seo';
const Page = () => (
<>
<NextSeo
title="Simple Usage Example"
description="A short description goes here."
/>
<p>Simple Usage</p>
</>
);
export default Page;
API
Запросы для статичных страниц - getStaticProps
Не нужен useState или useEffect, все можно сделать через getStaticProps
import Link from 'next/Link';
// static props
export async function getStaticProps(context) {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
const users = await response.json();
return {
props: {users}, // will be passed to the page component as props
}
}
export default function users({ users }) {
return (
<div>
<h1>Users</h1>
<ul>
{users.map(user => {
return (
<li key={user.id}>
<Link href={`/users/${user.id}`}>
<a>{user.name}</a>
</Link>
</li>
);
})}
</ul>
<h2>Routing</h2>
<ul>
<li><Link href='/'><a>Index</a></Link></li>
</ul>
</div>
)
}
Запросы для динамических страниц - getServerSideProps
При запросах через getStaticProps или getServerSideProps данные, полученные с сервера уже отрендериваются на клиенте.
// getServerSideProps - for dynamic pages
export async function getServerSideProps(context) {
const { params, query } = context; // params, query можно забирать прямо из контекста этой функции
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${params.id}`);
const userData = await response.json();
return {
props: {userData}, // will be passed to the page component as props
}
}
export default function userId({ userData }) {
console.log('userData', userData);
return (
<div>
<h1>User details:</h1>
<ul>
<li><span>Name:</span> <b>{userData.name}</b></li>
<li><span>Phone:</span> <b>{userData.phone}</b></li>
<li><span>Website:</span> <b>{userData.website}</b></li>
<li><span>Id:</span> <b>{userData.id}</b></li>
</ul>
</div>
)
}
Express
Config for express server
// @ts-ignore
const express = require('express');
const next = require('next');
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
app
.prepare()
.then(() => {
const server = express();
server.get('*', (req: any, res: any) => {
return handle(req, res);
});
server.listen(3000, (err: any) => {
if (err) throw err;
console.log('> Ready on http://localhost:3000');
});
})
.catch((ex: any) => {
console.error(ex.stack);
process.exit(1);
});