First Journey with Next.js
Nextjs is a React framework.
HTML ——> DOM ——> UI
The HTML represents the initial page content, whereas the DOM represents the updated page content which was changed by the JavaScript code.
React: A declarative UI library
As a developer, you can tell React what you want to happen to the user interface, and React will figure out the steps of how to update the DOM on your behalf.
- component: User interfaces can be broken down into smaller building blocks called components.
function Header() {
return <h1>Develop. Preview. Ship. 🚀</h1>;
}
function HomePage() {
return (
<div>
{/* Nesting the Header component */}
<Header />
</div>
);
}
ReactDOM.render(<HomePage />, app);
- props: passing inforamtion to components
function Header(props) {
console.log(props) // { title: "React 💙" } - 可见props is an object
// return <h1>React 💙</h1>
// }
//对比
function Header({ title }) {
console.log(title) // "React 💙"
return <h1>{title}</h1>
}
function HomePage() {
return (
<div>
<Header title="React 💙" />
</div>
)
}
function HomePage() {
const names = ['Ada Lovelace', 'Grace Hopper', 'Margaret Hamilton'];
return (
<div>
<Header title="Develop. Preview. Ship. 🚀" />
<ul>
{names.map((name) => (
<li key={name}>{name}</li> // need key to uniquely identify items in an array
))}
</ul>
</div>
);
}
- hooks: add additional logic such as state to your components.
Nextjs - A simple Game
default三个文件:
App.js
The code in App.js
creates a component. In React, a component is a piece of reusable code that represents a part of a user interface.
export default function Square() {
return <button className="square">X</button>;
}
export
JavaScript keyword makes this function accessible outside of this file.
default
keyword tells other files using your code that it’s the main function in your file.
className="square"
is a button property or prop that tells CSS how to style the button
- React components need to return a single JSX element and not multiple adjacent JSX elements like two buttons. To fix this you can use fragments (
<>
and</>
) to wrap multiple adjacent JSX elements - How to reuse a component?
//define the component, the Square component can be passed a prop called value
function Square({value}){
return <button className="square">{value}</button>
}
//reuse it,实现一个Board中三个Square,且每个Square显示的value不一样
export default function Board() {
return (
<>
<div className="board-row">
<Square value="1" />
<Square value="2" />
<Square value="3" />
</div>
</>
);
}
- How to respond to a click?
function Square({ value }) {
function handleClick() { //定义如何handle click
console.log('clicked!');
}
return (
<button className="square" onClick={handleClick}>{value}</button>
);
}
- React provides a special function called
useState
that you can call from your component to let it “remember” things. By calling thisset
function from anonClick
handler, you’re telling React to re-render thatSquare
whenever its<button>
is clicked.
import { useState } from 'react';
function Square() {
// 使用 useState 声明一个状态变量 value,初始值为 null,setValue 是更新状态的函数
const [value, setValue] = useState(null);
function handleClick() {
setValue('X');
}
return (
<button className="square" onClick={handleClick}>{value}</button>
);
}
- JavaScript supports closures which means an inner function (e.g.
handleClick
) has access to variables and functions defined in a outer function (e.g.Board
). ThehandleClick
function can read thesquares
state and call thesetSquares
method because they are both defined inside of theBoard
function.
涉及到immutability principle: state should not be directly mutated but should be updated in an immutable (unchanged) way. When you need to update the state, you should not modify the existing state directly. Instead, you create a new copy of the state with the desired changes, and then you set the state to the new copy. This ensures that the original state remains unchanged.
React relies on comparing the previous state with the new state to determine which parts of the UI need to be updated.
export default function Board() {
const [squares, setSquares] = useState(Array(9).fill(null));
function handleClick() {
const nextSquares = squares.slice(); //相当于copy array
nextSquares[0] = "X";
setSquares(nextSquares);
}
//
return (
// ...
)
}
- two ways to declare a variable in javascript: const(不变) and let (变)
[...history, nextSquares]
creates a new array that contains all the items inhistory
, followed bynextSquares
. (You can read the...history
spread syntax as “enumerate all the items inhistory
”.)
export default function Game() {
//...
function handlePlay(nextSquares) {
setHistory([...history, nextSquares]);
setXIsNext(!xIsNext);
}
//...
}
- Transform one array into another, you can use the array
map
method:
[1, 2, 3].map((x) => x * 2) // [2, 4, 6]
styles.css
Index.js
The bridge between the component created in the App.js
file and the web browser.
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './styles.css';
import App from './App';
Lines 1-5 brings all the necessary pieces together:
- React
- React’s library to talk to web browsers (React DOM)
- the styles for your components
- the component you created in
App.js
How Next.js Works
From development process to production process:
- Compling: the process of taking code in one language(TypeScript) and outputting it in another language or another version of that language(JavaScript, which browser can understand).
- Minifying: removing unnecessary code formatting and comments without changing the code’s functionality.
- Bundling: the process of resolving the web of dependencies and merging (or ‘packaging’) the files (or modules) into optimized bundles for the browser, with the goal of reducing the number of requests for files when a user visits a web page. —— resolve application dependency graph and reduce the number of files
- Code splitting: the process of splitting the application’s bundle into smaller chunks required by each entry point. The goal is to improve the application's initial load time by only loading the code required to run that page. Each file inside your
pages/
directory will be automatically code split into its own JavaScript bundle during the build step.
Build time and Runtime
- Build time: prepare your application code for production. Files:
- HTML files for statically generated pages
- JavaScript code for rendering pages on the server
- JavaScript code for making pages interactive on the client
- CSS files
- Runtime: the period of time when your application runs in response to a user’s request, after your application has been built and deployed.
Rendering (渲染): convert the code in React into the HTML representation of UI
Three types of rendering methods are available in Next.js:
- Pre-Rendering: Server-Side Rendering and Static Site Generation —— the fetching of external data and transformation of React components into HTML happens before the result is sent to the client. Initial Load ——> Hydration
Initial Load: Pre-rendered HTML is displayed.
Hydration: When a page is loaded by the browser, its JavaScript code runs and makes the page becomes interactive.
- Server-Side Rendering: the HTML of the page is generated on a server for each request.
- Static Site Generation: the HTML is generated on the server once, at build time. The pre-rendered HTML is then reused on each request.
**Static Generation can be done with and without data.**
How to Static Generation **with data(generate HTML after fetching data from databse or some APIs)? —— getStaticProps
** [getStaticProps](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation)
only runs on the server-side.
export default function Home(props) { ... }
export async function getStaticProps() {
// Get external data from the file system, API, DB, etc.
const data = ...
// The value of the `props` key will be passed to the `Home` component
return {
props: ...
}
}
In development mode (when you run npm run dev
or yarn dev
), pages are pre-rendered on every request, no matter which pre-rendering method.
- Client-Side Rendering: the browser receives an empty HTML shell(空白页面) from the server along with the JavaScript instructions to construct the UI —— the initial rendering work happens on the user's device.
Key features of nextjs:
- server-side rendering
- file-based routing - define different pages with files and folders called “Page” instead of writing code
- fullstack capabilites - easy to add backend code and Api in code, no need seperate project
Pages in nextjs:
Pages are associated with a route based on their file name. For example, in development:
pages/index.js
is associated with the/
route.pages/posts/first-post.js
is associated with the/posts/first-post
route.
How to create pages?
Simply create a JS file under the [pages
directory](https://nextjs.org/docs/basic-features/pages), and the path to the file becomes the URL path.
How to link between pages? —- Link
import Link from 'next/link';
<h1 className="title">
Read <Link href="/posts/first-post">this page!</Link>
</h1>
The [Link](https://nextjs.org/docs/api-reference/next/link)
component enables client-side navigation (the page transition happens using JavaScript) between two pages in the same Next.js app. Whenever [Link](https://nextjs.org/docs/api-reference/next/link)
components appear in the browser’s viewport, Next.js automatically prefetches the code for the linked page in the background.
Assets, Metadata, and CSS
Next.js can serve static assets, like images, under the top-level [public
directory](https://nextjs.org/docs/basic-features/static-file-serving).
Images are lazy loaded by default. That means your page speed isn't penalized for images outside the viewport. Images load as they are scrolled into viewport.
<Head>
is used instead of the lowercase <head>
. <Head>
is a React Component that is built into Next.js. It allows you to modify the <head>
of a page. You can import the Head
component from the [next/head](https://nextjs.org/docs/api-reference/next/head)
module. 可用于改browser tab名字
To use CSS Modules, the CSS file name must end with .module.css
. This is what CSS Modules does: It automatically generates unique class names. As long as you use CSS Modules, you don’t have to worry about class name collisions.
Global CSS —— some CSS to be loaded by every page. The default export of _app.js
under pages directory is a top-level React component that wraps all the pages in your application. 只能在[pages/_app.js](https://nextjs.org/docs/advanced-features/custom-app)
中import global.css
,其他file都不可以.
Dynamic Routes —— the case where each page path depends on external data/posts/<id>
Use getStaticPaths
to fetch an array of product IDs and use getStaticProps
to fetch data for each product.
API Routes
API Routes let you create an API endpoint inside a Next.js app. You can do so by creating a function inside the pages/api
directory that has the following format:
// req = HTTP incoming message, res = HTTP server response
export default function handler(req, res) {
// ...
}
Deployment on Vercel
The workflow recommended DPS: Develop, Preview, and Ship.
- Develop: We’ve written code in Next.js and used the Next.js development server running to take advantage of its hot reloading feature.
- Preview: We’ve pushed changes to a branch on GitHub, and Vercel created a preview deployment that’s available via a URL. We can share this preview URL with others for feedback. In addition to doing code reviews, you can do deployment previews.
- Ship: We’ve merged the pull request to
main
to ship to production.