什么是NextJS?用Next开发更快网站的实践介绍
使用JavaScript和NextJS构建全栈应用程序的介绍

尽管现代基于Javascript的前端框架(如React)帮助我们开发强大的动态网站,但它们有一个主要的缺点:客户端必须等待所有Javascript代码加载,才能渲染页面并向用户显示内容。由于页面的加载速度会极大地影响其用户体验和搜索引擎排名,这已成为网站开发中的一个主要问题。
Next.js是一个基于React的框架,为这个问题提供了一个解决方案。它承诺使用一套易于使用的功能来提高网站的性能,包括预渲染。考虑到自该框架发布以来,开发者社区在很短的时间内就蜂拥而至,可以说,Next毫无保留地实现了这一承诺。
因此,我们决定在本教程中向你介绍Next.js,这个正在迅速成为开发者最喜爱的框架。让我们了解Next是如何在React的基础上提高性能和简化开发流程的。
Next.js的主要特点
与React这样的框架相比,Next.js最突出的特点是预渲染。如上所述,在客户端接收Javascript代码后渲染网页是一个缓慢的过程。Next通过向客户端发送每个页面的预渲染版本来解决这个问题。
它使用三种类型的预渲染。它们是
- 静态网站生成
- 服务器端渲染
- 递增的静态再生
静态网站生成最适合建立静态网页,而服务器端渲染则适合有动态内容的网页。Next的特点是,它允许你决定对每个网页分别使用哪种类型的渲染。
除了预渲染外,Next还提供了一套精心设计的功能,以简化开发过程,具体如下:
- 自动捆绑和代码拆分。与React不同的是,你必须手动设置Webpack来捆绑代码,而Next.js在引擎盖下使用Webpack自动捆绑代码。它还支持基于独立路由和动态导入的代码拆分。它减少了网页和动态组件的加载时间。 = 图像优化。Next原生支持图像的按需调整和优化。你可以将它与默认的懒惰加载选项搭配使用,以获得最佳性能。
- 快速刷新:当一个组件的代码在开发过程中发生变化时,可以立即重新渲染。
开始使用Next
要开始使用Next,你的设备上应该安装了Node.js和npm。一旦安装完成,你就可以继续构建一个新的Next应用程序。
使用create-next-app
Next提供了一个create-next-app ,类似于create-react-app ,可以快速设置项目的文件夹和环境。下面是你如何调用create-next-app 的操作。
npx create-next-app
一旦你按照提示添加应用程序名称,该命令将创建一个项目文件夹并安装必要的包(next、react和react-dom)。它还会设置package.json文件,内容如下。
{
"name": "first-next-app",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"next": "11.0.1",
"react": "17.0.2",
"react-dom": "17.0.2"
},
"devDependencies": {
"eslint": "7.30.0",
"eslint-config-next": "11.0.1"
}
}
手动设置Next应用程序
你也可以手动设置Next应用,不需要借助create-next-app。为此,首先创建一个项目目录,并用npm init 进行初始化。然后,使用以下npm命令安装必要的软件包。
npm install next, react, react-dom
用以下内容更新package.json的脚本部分。
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
},
- dev脚本在开发模式下运行应用程序。
- start脚本在生产模式下运行该应用程序。
- build脚本编译和构建应用程序。
在本教程中,我们将假设一个手动设置的项目,以帮助你了解创建Next应用程序的背景细节。
在Next应用中添加页面
在Next.js中,页面是一个存储在名为 "pages "的目录中的文件。每个页面都与Web应用程序中的一个路由相关联,并为该特定路由导出一个React组件。
例如,文件路径为pages/products/display.js 的页面应该存储/products/display 路由为访问者渲染的 React 组件。一个名称为pages/products/index.js 的页面与路由/products 关联。
因此,要开始向Web应用程序添加页面,你应该首先创建一个名为pages. 的目录,然后,你应该在该目录中添加一个index.js文件,与Web应用程序的/ 路由相关联。
在我们的第一个页面,我们可以导出一个简单的组件,向访问者显示 "Hello World",如下图所示。
const Home = () => (
<div>
<h1>Hello World!</h1>
</div>
);
export default Home;
现在,我们可以使用以下命令在开发模式下启动Next应用程序。
npm run dev
它在3000端口构建并启动我们的应用程序。你可以使用URL http://localhost:3000/ 访问它的主页。

我们简单的hello world网络应用
按照类似的模式,你可以向应用程序添加更多的页面。
页面之间的链接
Next提供了一个名为 "Link "的React组件,以确保链接页面之间的最佳过渡。尽管可以使用普通的锚点标签来连接页面,但Next的链接组件通过防止客户端重新导入两个页面共有的捆绑物来加速操作。
为了演示链接功能,首先,我们需要在我们的网络应用中添加另一个页面。为此,我们将在pages目录下创建一个新的about.js文件,并在其中添加以下代码。
//about.js
const About = () => (
<div>
<h3>About Us</h3>
<p>We strive to provide the best services to you</p>
</div>
);
export default About;
一旦你保存了这些改动,Next会自动用新的组件重建应用程序。你可以使用URL http://localhost:3000/about 访问该页面。

简单的关于我们的页面
现在,我们应该在网站的主页上提供一个关于页面的链接。下面是我们如何实现这一目标。
import Link from "next/link";
const Home = () => (
<div>
<h1>Hello World!</h1>
<Link href="/about"><a>Learn more about us</a></Link>
</div>
);
export default Home;

链接的例子
动态链接
如果你想用动态路线来链接一个页面,怎么办?Next在动态页面名称和路由器的帮助下,使这项任务变得非常简单。
还记得Next是如何在网络应用中使用页面名称作为路由的吗?以同样的方式,Next允许我们创建与动态路由有关的动态名称的页面。
例如,考虑一个像/shows/[tvshow] 的路由。这个动态路由会根据电视节目改变URL(例如:/shows/game-of-thrones, shows/friends)。
接下来,我们可以为这个路由中的所有电视节目创建一个通用模板。我们称它们为动态页面。动态页面的名称遵循这样的格式:pages/shows/[tvshow].js 。
然后,我们可以使用Next路由器来提取动态URL中的信息,并渲染页面以适应每个电视节目。
让我们从创建一个名称为pages/shows/[tvshow].js 的页面开始实施。在这个文件中,我们应该创建一个路由器对象来检索URL参数。然后,我们可以用与该参数相关的信息返回组件。
import {useRouter} from "next/router";
const TVShow = () => {
const router = useRouter();
const tvshow = router.query.tvshow;
return (
<div>
<h3>{tvshow}</h3>
</div>
);
}
export default TVShow
现在我们已经设置好了动态页面内容,我们可以在网站的主页上提供不同电视节目的链接。
import Link from "next/link";
const Home = () => (
<div>
<h1>Hello World!</h1>
<p>Popular TV Shows</p>
<ul>
<li>
<Link href={`/shows/game-of-thrones`}><a>Game of Thrones</a></Link>
</li>
<li>
<Link href={`/shows/friends`}><a>Friends</a></Link>
</li>
<li>
<Link href={`/shows/westworld`}><a>Westworld</a></Link>
</li>
</ul>
<Link href="/about"><a>Learn more about us</a></Link>
</div>
);
export default Home;
现在,我们的主页显示了电视节目的列表,并有链接将它们连接到动态路线上。

硬编码的节目
当我们点击其中一个链接时,就会渲染出[tvshow].js页面,其中有与每个电视节目相关的内容。
在下一节,让我们看看如何从远程API获取数据,以显示更多的电视节目数据。
获取数据
Next提供了三种不同的方法来从远程API获取数据。它们是:getStaticProps,getStaticPaths, 和getServerSideProps 。
让我们通过从TV Maze API获取电视节目数据来了解每个数据获取方法的不同之处。
getStaticProps
这个函数应该用在静态生成的页面上,因为它在构建时获取数据。当我们在与React组件相同的页面上声明一个异步getStaticProps函数时,它将接收检索到的数据作为属性。
让我们用这个方法从TV Maze API中获取最近的电视节目列表,以显示在主页上。
//index.js
export async function getStaticProps() {
const res = await fetch("https://api.tvmaze.com/shows?page=1");
const data = await res.json();
return { props: { data } }
}
主页组件现在可以使用返回的数据来显示电视节目的名称列表。下面是我们如何修改该组件以实现这一目的。
//index.js
import Link from "next/link";
const Home = ({data}) => (
<div>
<h1>Hello World!</h1>
<p>Popular TV Shows</p>
<ul>
{data.map(tvshow => {
return (
<li>
<Link href={`/shows/${tvshow.id}`}><a>{tvshow.name}</a></Link>
</li>
);
})}
</ul>
<Link href="/about"><a>Learn more about us</a></Link>
</div>
);
export async function getStaticProps() {
const res = await fetch("https://api.tvmaze.com/shows?page=1");
const data = await res.json();
return { props: { data } }
}
export default Home;
每个电视节目的动态链接现在使用节目ID而不是它的名字shows/[tvshow-id] 。而我们的主页现在看起来是这样的。

从节目API动态生成的链接
getStaticPaths
getStaticPaths在静态生成过程中也被使用,特别是在动态路由中。
在有动态路由的页面中,它们所显示的内容取决于访问该页面时使用的URL。例如,如果用户访问/shows/123,他们应该看到一个使用ID为123的电视节目的数据渲染的页面。由于Next在静态生成的构建时间内预先渲染了这些页面,它应该事先知道网站上所有可用的ID(路径)。我们可以使用getStaticPaths方法检索这个列表。
下面是我们将如何为[tvshow].js 页面实现ID检索。
export async function getStaticPaths() {
const res = await fetch("https://api.tvmaze.com/shows?page=1");
const data = await res.json();
//Get the available TV show IDs
const paths = data.map(tvshow => {
return { params: {tvshow: tvshow.id.toString()}}
});
//Pass the paths to prerender their content at build time
return {paths, fallback: false}
}
在这里,我们将fallback设置为false,这样应用程序就会对不在检索列表中的ID返回404错误。
在使用getStaticPaths方法时,我们还应该声明一个getStaticProps 方法,使Next能够检索到每个电视节目的数据。我们可以实现这个函数,如下图所示。
export async function getStaticProps({params}) {
//Params points to each TV show ID we retrieved in getStaticPaths
//Retrieve data for the TV show with the given ID
const res = await fetch(`https://api.tvmaze.com/shows/${params.tvshow}`);
const data = await res.json();
//Return the data as a prop to TVShow component
return {props: {data}}
}
然后我们可以设置TVShow组件,在网页上显示检索到的数据。
const TVShow = ({data}) => {
return (
<div>
<h3>{data.name}</h3>
<div>{data.summary.replace(/<\/?[^>]+(>|$)/g, "")}</div>
<p>Language: {data.language}</p>
<p>Status: {data.status}</p>
</div>
);
}
export async function getStaticPaths() {...}
export async function getStaticProps({params}) {...}
export default TVShow
这就是我们的电视节目页面如何显示这些结果。

节目信息页面
getServerSideProps
如果你使用getServerSideProps 方法来获取数据,Next会检索数据并为每个客户端请求生成HTML内容。换句话说,这个方法是用在需要服务器端渲染的页面上。例如,如果你想检索的数据不断变化,你应该使用这个函数来获取最新的结果。
下面是当客户端请求查看一个特定的电视节目页面时,我们可以用这个方法检索数据。
export async function getServerSideProps(context) {
//context contains the URL user requests
const res = await fetch(`https://api.tvmaze.com/shows/${context.params.tvshow}`);
const data = await res.json();
return { props: { data } }
}
而后,TVShow组件可以显示所请求的电视节目的数据。
const TVShow = ({data}) => {
return (
<div>
<h3>{data.name}</h3>
<div>{data.summary.replace(/<\/?[^>]+(>|$)/g, "")}</div>
<p>Language: {data.language}</p>
<p>Status: {data.status}</p>
</div>
);
}
export async function getServerSideProps(context) {...}
export default TVShow
它返回的输出与之前的实现类似,有getStaticPaths 和getStaticProps 。这里唯一的区别是,应用程序在构建过程中没有为每个可用的电视节目预先渲染页面。相反,它只在用户发送请求查看某个电视节目时才检索数据并渲染页面。
添加CSS
Next提供了几种向应用程序组件添加CSS的方法。让我们在本节中讨论那些允许添加我们自己的样式表的方法。
全局样式表
你可以用这种方法添加网站上所有组件共有的CSS属性。在看到如何添加这种类型的样式表之前,让我们创建一个名为 "样式 "的新子目录来存储应用程序中的所有CSS文件。然后,在这个目录中,我们可以创建一个名为 "globals.css "的文件,其中包含以下全局样式。
//globals.css
html, body {
padding: 0;
margin: 0;
font-family: Segoe UI, Roboto;
}
a {
color: inherit;
}
现在,我们应该将全局样式表通知Next应用程序。为此,我们使用_app.js 文件。
_app.js文件允许开发者覆盖Next为初始化应用程序定义的默认配置。即使这个文件存储在pages目录中,它也不会转换为Web应用程序中的路由。
我们通过在_app.js 内导入全局样式表,将其引入Next。
//_app.js
import '../styles/globals.css'
//This code section should be in the _app.js by default
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
现在,当应用程序重新加载时,这些全局样式将应用于每个网页。
组件级的CSS
我们可以用第二种方法为应用程序中的不同组件定义CSS。一个组件级的CSS文件,也存储在样式目录中,应该遵循[组件名].模块.css的名称格式。然后,我们可以通过简单地导入文件并引用正确的类名,将这些样式应用于相关的组件。
下面是一个CSS文件的例子,它应该为我们的应用程序中的主页组件提供样式。
import Link from "next/link";
import styles from "../styles/Home.module.css";
const Home = ({data}) => (
<div className={styles.container}>
<h1>Hello World!</h1>
<p>Popular TV Shows</p>
<ul>
{data.map(tvshow => {
return (
<li className={styles.item}>
<Link href={`/shows/${tvshow.id}`}><a>{tvshow.name}</a></Link>
</li>
);
})}
</ul>
<Link href="/about"><a>Learn more about us</a></Link>
</div>
);
样式化后的主页:

样式化的JSX
我们可以使用styled-jsx块在组件级JSX内部添加CSS。在这种方法中,我们使用一个特殊的标签{``},并在引号内添加CSS代码,就像我们在普通的CSS文件中一样。
让我们使用一个styled-jsx块来修改我们的主页。
import Link from "next/link"; import styles from "./styles/Home.module.css";
const Home = ({data}) => (
<div className={styles.container}>
<h1>Hello World!</h1>
<p>Popular TV Shows</p>
<ul>
{data.map(tvshow => {
return (
<li className={styles.item}>
<Link href={`/shows/${tvshow.id}`}><a>{tvshow.name}</a></Link>
</li>
);
})}
</ul>
<Link href="/about"><a>Learn more about us</a></Link>
<style jsx>{`
a {
text-decoration: none;
}
ul {
list-style: none;
}
`}</style>
</div>
);
修改后的主页:

绝对是更好看的主页
总结
就这样,我们结束了对Next.js的介绍教程。我希望这篇文章能说服你将Next.js加入到你的网络开发工具库中。即使你以前没有使用React的经验,Next也能通过一套直观的功能和快速的性能让你轻松找到自己的立足点。考虑到Next在五年前才第一次被介绍给世界,我们肯定能够看到它在未来几年内成长为一个更好的工具。