如果你做过前端开发,应该知道单页面应用程序(SPA,Single Page Application)的概念。但是,随着网络应用程序变得越来越复杂,我们可能需要一种更为先进的技术,能够快速地渲染和显示页面内容,而不仅仅是等待客户端加载和执行 JavaScript。
这就是服务端渲染(SSR,Server-side Rendering)的解决方案。服务端渲染可以带来更好的性能、更好的SEO、更好的用户体验、更好的可访问性等等许多好处。Next.js是一个建立在服务端渲染概念之上的React框架。它提供了许多工具,可以快速而简单地开发出高性能的服务端渲染应用。
在本文中,我们将带你探索使用Next.js打造SSR应用程序的基础知识和技术。我们将学习如何配置环境、编写组件、定义路由、实现数据获取和懒加载等等。最后,我们将为你提供一个完整的示例代码,以便你更好地了解和运用Next.js。
环境和工具
在开始学习Next.js之前,我们需要做一些环境配置和工具安装。准备工作如下:
- 安装Node.js:Next.js需要在Node.js环境下运行。你可以从官方网站下载并安装Node.js最新版本。
- 创建项目:通过以下命令,在您的本地计算机上创建一个名为
nextjs-demo的项目:
npx create-next-app nextjs-demo
通过这个命令,Next.js将创建一个基本的Boilerplate示例,以便我们更轻松地开始构建我们的应用程序。 3. 运行应用程序:在项目目录下运行以下命令,打开应用程序的开发模式。
cd nextjs-demo npm run dev
组件
在React中,组件是构建用户界面的基本单元。组件可以是无状态组件(stateless)和有状态组件(stateful)。无状态组件接受一些属性(props),并可以渲染出一个UI元素;有状态组件保存一些状态数据,并在需要时修改这些数据。
在Next.js中,我们创建组件的方式基本上和在React中创建组件的方式相同。首先,我们将在/components目录下创建一个名为Header.js的文件,并添加以下代码:
// www.javascriptcn.com code example
const Header = () => {
return (
<header>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
</header>
);
};
export default Header;这个组件将包含我们应用程序的导航栏。我们使用无状态函数式组件,并返回一个包含导航栏的头部元素。
同样地,在/components目录下,我们创建一个名为Layout.js的文件,并添加以下代码:
// www.javascriptcn.com code example
import Header from "./Header";
const Layout = ({ children }) => {
return (
<>
<Header />
<main>
{ children }
</main>
</>
);
};
export default Layout;这个组件将充当我们应用程序的主要布局。它将包含应用程序的头部(header)和主要内容(main)部分。{ children }是一个特殊的变量,代表布局组件内部的子组件。
路由
在单页面应用程序中,路由是一个非常重要的概念。它允许用户在不刷新页面的情况下,能够浏览不同的内容页面。在Next.js中,如何定义路由呢?
首先,我们将为我们的应用程序创建一个简单的路由。我们将在/pages目录下创建三个名为index.js、about.js、和contact.js文件,并向它们中分别添加以下代码:
// www.javascriptcn.com code example
const Home = () => {
return (
<>
<h1>Home page</h1>
</>
);
};
export default Home;
const About = () => {
return (
<>
<h1>About page</h1>
</>
);
};
export default About;
const Contact = () => {
return (
<>
<h1>Contact page</h1>
</>
);
};
export default Contact;此时,我们已定义了三个基本的页面组件。但是,如何将它们与路由关联起来呢?在Next.js中,我们可以使用next/link和next/router这两个工具。
我们将在/components/Header.js中添加以下代码,以启用我们的导航栏:
// www.javascriptcn.com code example
import Link from "next/link";
const Header = () => {
return (
<header>
<nav>
<ul>
<li><Link href="/">Home</Link></li>
<li><Link href="/about">About</Link></li>
<li><Link href="/contact">Contact</Link></li>
</ul>
</nav>
</header>
);
};
export default Header;Link组件使得我们能够轻松地指定链接和目标路由。现在,我们需要使用路由器(next/router)来处理实际的页面路由转换。我们将在/pages目录下创建一个名为_app.js的文件,并添加以下代码:
// www.javascriptcn.com code example
import Layout from "../components/Layout.js";
import { useRouter } from "next/router";
export default function MyApp({ Component, pageProps }) {
const router = useRouter();
return (
<Layout>
{router.pathname === "/" ? (
<Component {...pageProps} />
) : (
<h1>{router.pathname}</h1>
)}
</Layout>
);
}此时,我们已经能够通过导航栏访问不同的页面,但是当我们访问未定义的路由时,我们只看到的是错误提示信息。因此我们需要添加自定义404页面:在/pages目录下创建一个名为404.js的文件,并添加以下代码:
// www.javascriptcn.com code example
const NotFoundPage = () => {
return (
<>
<h1>404 - Page Not Found</h1>
<p>The page you are looking for does not exist.</p>
</>
);
};
export default NotFoundPage;现在,当我们遇到未定义的路由时,我们的应用程序将返回我们刚刚创建的自定义404页面。
数据获取
在单页面应用程序中,数据通常通过客户端请求获得。然而,在服务端渲染中,为了获得最佳性能,我们需要尽可能多地在服务器端获取数据。这就意味着我们需要调整客户端和服务端之间的数据传递。
在Next.js中,我们可以使用getInitialProps将数据从伺服器传递到客户端。getInitialProps是Next.js中特有的用于服务器端渲染的静态方法,它允许我们在组件渲染之前获取数据且可将数据传递给组件作为props。
假设我们有一个/pages/posts/[id].js页面组件,用于渲染每个博客文章的详细信息。我们可以像这样定义数据获取功能:
// www.javascriptcn.com code example
import Layout from "../../components/Layout.js";
import { useRouter } from "next/router";
const Post = ({ title, body }) => {
const router = useRouter();
if (router.isFallback) {
return <div>Loading...</div>;
}
return (
<Layout>
<h1>{title}</h1>
<p>{body}</p>
</Layout>
);
};
Post.getInitialProps = async (context) => {
const { id } = context.query;
const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
const post = await res.json();
return {...post};
};
export default Post;在这个例子中,getInitialProps函数使用async/await从API获取博客文章的标题和内容,并把它们作为props返回到组件内部。我们可以使用这些props来更新UI,以便将数据展示给用户。如果getInitialProps方法返回的是一个promise,Next.js将自动将其解决并将结果传递给组件。
懒加载
懒加载(lazy loading)是一种技术,用于减少初始页面加载时间并节省带宽。它通过只在需要的时候加载页面组件和其他内容,而不是一开始就加载所有组件和内容,来实现这个目标。
在Next.js中,我们可以使用dynamic方法来实现懒加载。假设我们有一个模块文件,其中定义了一个名为MyComponent的React组件。我们可以如下所示地将其懒加载:
import dynamic from 'next/dynamic';
const MyComponent = dynamic(import('./MyComponent'));这个组件将在需要时(即在渲染过程中)进行加载,而不是在一开始就进行加载。以这种方式实现懒加载也非常适合与服务器端渲染一起使用。
示例代码
最后,我们提供以下完整的Next.js示例代码,以帮助你更好地学习和理解如何使用这个框架:
// www.javascriptcn.com code example
// /pages/index.js
import Layout from "../components/Layout.js";
const Home = () => {
return (
<Layout>
<h1>Home page</h1>
</Layout>
);
};
export default Home;
// /pages/about.js
import Layout from "../components/Layout.js";
const About = () => {
return (
<Layout>
<h1>About page</h1>
</Layout>
);
};
export default About;
// /pages/contact.js
import Layout from "../components/Layout.js";
const Contact = () => {
return (
<Layout>
<h1>Contact page</h1>
</Layout>
);
};
export default Contact;
// /pages/posts/[id].js
import Layout from "../../components/Layout.js";
import { useRouter } from "next/router";
const Post = ({ title, body }) => {
const router = useRouter();
if (router.isFallback) {
return <div>Loading...</div>;
}
return (
<Layout>
<h1>{title}</h1>
<p>{body}</p>
</Layout>
);
};
Post.getInitialProps = async (context) => {
const { id } = context.query;
const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
const post = await res.json();
return {...post};
};
export default Post;
// /components/Header.js
import Link from "next/link";
const Header = () => {
return (
<header>
<nav>
<ul>
<li>
<Link href="/">Home</Link>
</li>
<li>
<Link href="/about">About</Link>
</li>
<li>
<Link href="/contact">Contact</Link>
</li>
</ul>
</nav>
</header>
);
};
export default Header;
// /components/Layout.js
import Header from "./Header";
const Layout = ({ children }) => {
return (
<>
<Header />
<main>{children}</main>
</>
);
};
export default Layout;
// /pages/_app.js
import Layout from "../components/Layout.js";
import { useRouter } from "next/router";
export default function MyApp({ Component, pageProps }) {
const router = useRouter();
return (
<Layout>
{router.pathname === "/" ? (
<Component {...pageProps} />
) : (
<h1>{router.pathname}</h1>
)}
</Layout>
);
}
// /pages/404.js
const NotFoundPage = () => {
return (
<>
<h1>404 - Page Not Found</h1>
<p>The page you are looking for does not exist.</p>
</>
);
};
export default NotFoundPage;
// /pages/lazy.js
import dynamic from "next/dynamic";
const MyComponent = dynamic(import("../components/MyComponent"));
const LazyPage = () => (
<>
<h1>Lazy loaded page</h1>
<MyComponent />
</>
);
export default LazyPage;
// /components/MyComponent.js
const MyComponent = () => (
<div>This component was lazily loaded.</div>
);
export default MyComponent;我们希望这篇文章能够对你有所帮助,并使你更好地了解和使用Next.js来构建高性能的服务端渲染应用程序。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d0017ee46428fe9ec7501d