Перейти к основному содержимому

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)

  1. npx create-next-app nextjs-blog --use-npm --example "https://github.com/vercel/next-learn/tree/master/basics/learn-starter"
  2. cd nextjs-blog
  3. npm run dev

Install 2 old (manual)

  1. npm init -y
  2. npm i --save react react-dom next
  3. ./pages/index.js
  4. 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


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);
});