阅读 1469

Next.js 入门指南

作者:杨磊

一、概念介绍

  • SPA(Single Page Application): 单页面应用程序,是前后端分离时提出的一种解决方案。在一个应用或者站点中,只有一个完整的html页面,这个页面有一个容器root节点,可以把需要加载的代码片段插入到该容器中。

    • SPA的工作原理:采用的前端路由跳转子页面系统,通过改变页面的URL,在不重新请求页面的情况下,实现局部更新页面视图。
  • SEO(Search Engine Optimization ): 搜索引擎优化,利用搜索引擎的规则提高网站在有关搜索引擎内的自然排名。

    • SEO的工作原理:网页在被请求的时候,从服务器发出的内容可以被搜索引擎的爬虫爬到数据,这个时候从搜索引擎搜索的关键字包含在这些内容中,那么这个网址的信息就更容易显示在搜索结果中。
  • SSR(Server side rendering):服务端渲染把数据的初始请求放在了服务端,服务端收到请求后,把数据填充到模板形成完整的页面,由服务端把渲染的完整的页面返回给客户端。

  • CSR(Client side rendering):客户端渲染,即客户端发起HTML网页请求时,服务器不做任何处理,直接返回静态的HTML文件。客户端收到后,执行JavaScript,生成DOM,插入HTML页面,完成最终页面的绘制。

  • 同构渲染(Isomorphic render):前后端同构是指在前后端维护同一份代码。它是在SPA的基础上,利用服务端渲染(SSR)直出首屏,解除单页面应用(SPA)在首屏渲染上面临的窘境。明确地说,同构是将传统的纯服务端直出的首屏优势和SPA的站内体验优势结合起来,以取得最优解的解决方案。

二、服务端渲染与客户端渲染的优缺点

服务端渲染的优点:

  • 首屏渲染快
  • 利于SEO
  • 客户端资源占用较少
  • 服务端渲染的缺点:
  • 不利于前后端分离,开发效率低
  • 服务器端资源占用较多

客户端渲染优点:       

  • 网络传输数据量小,减少了服务器压力     
  • 前后端分离,开发效率高,易于维护  
  • 客户端渲染的缺点:     
  • 不利于SEO
  • 首屏渲染慢

三、Next 简介

Next.js 不单纯是 服务端渲染 框架,准确的说,应该是同构渲染框架。 Next.js 具有同类框架中最佳的“开发人员体验”和许多内置功能。列举其中一些如下:

  • 直观的、基于页面的路由系统(并支持动态路由)
  • 预渲染。支持在页面级设置静态生成 (SSG) 和服务器端渲染 (SSR)
  • 自动代码拆分,提升页面加载速度
  • 具有优化的预取功能的客户端路由
  • 内置 CSS 和 Sass 的支持,并支持任何 CSS-in-JS 库
  • 开发环境支持快速刷新
  • API routes 可使用 Serverless Functions 构建 API 端点
  • 完全可扩展

四、Next 核心概念

Next.js 具有两种形式的预渲染: 静态生成(Static Generation) 和 服务器端渲染(Server-side Rendering)。这两种方式的不同之处在于为 page(页面)生成 HTML 页面的 时机 。

静态生成(Static Generation)

静态生成就是项目构建时生成页面,并在每次页面请求(request)时被重用。下图描述了静态生成的概念:

image.png

什么时候使用静态生成

呈现页面所需的数据在用户请求之前的构建时可用。 数据不是用于特定用户的,并且可以被公开缓存。 常见的页面类型有:

  • 营销页面
  • 博客文章
  • 电子商务产品列表
  • 帮助和文档
  • 如何使用

Next.js 将在构建时使用 getStaticProps 所返回的包含数据来预渲染此页面

export async function getStaticProps(context) {
  return {
    props: {}, // will be passed to the page component as props
  }
}
复制代码

服务器端渲染(Server-side Rendering)

服务端渲染就是在每次页面请求(request)时,重新生成一个页面返回给客户端,下图描述了服务端渲染的概念:

image.png

什么时候使用服务端渲染

仅在页面预渲染的数据必须在请求时获取的时候,即每次请求的的页面内容都会不一样,就应使用。 在请求服务端渲染的页面时,Next 会根据 getServerSideProps 返回的数据来构建页面。

export async function getServerSideProps(context) {
  return {
    props: {
      // props for your component
    }
  }
}
复制代码

路由

Next.js 有一个基于页面概念的文件系统路由器。 当一个文件被添加到 pages 目录时,它将自动作为一个可用路由。 pages 目录中的文件命名方式可以用来定义大多数常见的路由模式。 以下是三种路由匹配的模式,通俗易懂:文件路径 → 对应路由

索引路由

pages/index.js → /
pages/blog/index.js → /blog
复制代码

嵌套路由

pages/blog/first-post.js → /blog/first-post
pages/dashboard/settings/username.js → /dashboard/settings/username
复制代码

动态路由

pages/blog/[slug].js → /blog/:slug (/blog/hello-world)
pages/[username]/settings.js → /:username/settings (/foo/settings)
pages/post/[...all].js → /post/* (/post/2020/id/title)
复制代码

如果使用静态生成与动态路由相结合的方式,则我们还需要使用  getStaticPaths 方法,该方法返回的对象中,必须包含 fallback 与 paths:

  • paths 为项目构建时想要使用静态生成的页面对应的路由
  • fallback 是布尔值
    • 为 true 时,用户访问没有预渲染的页面时,不会跳转到 404 页面,而是会根据新路由尝试构建新页面,构建失败会报错
    • 为 false 时,用户访问没有预渲染的页面时,会直接跳转 404 页面
export async function getStaticPaths() {
  return {
    paths: [
      { params: { ... } } 
    ],
    fallback: true or false 
  };
}
复制代码

页面跳转

使用 next 的 Link 组件进行页面跳转

import Link from 'next/link'

function Home() {
  return (
    <ul>
      <li>
        <Link href="/post/abc">
          <a>Go to pages/post/[pid].js</a>
        </Link>
      </li>
      <li>
        <Link href="/post/abc?foo=bar">
          <a>Also goes to pages/post/[pid].js</a>
        </Link>
      </li>
      <li>
        <Link href="/post/abc/a-comment">
          <a>Go to pages/post/[pid]/[comment].js</a>
        </Link>
      </li>
    </ul>
  )
}

export default Home
复制代码

使用 useRouter 进行编程式导航

import { useRouter } from 'next/router'

function ReadMore() {
  const router = useRouter()

  return (
    <span onClick={() => router.push('/about')}>Click here to read more</span>
  )
}

export default ReadMore
复制代码

API 路由

Next.js 提供了构建 API 的解决方案,与上面路由的使用方法类似,在 “pages/api” 目录下添加的文件都会映射到 “/api/*” API。

在项目构建时,在 “pages/api” 下的文件将不会增加客户端包的体积,仅会增加服务端的包体积。

// pages/api/user.js  → /user 

export default function handler(req, res) {
  res.status(200).json({ data: 'hello API' })
}
复制代码

动态 API 路由 的使用方式与上面提到的 动态路由 类似

pages/api/post/[pid].js   →    /api/post/:pid (/api/post/1,/api/post/abc)
pages/api/post/[...slug].js   →   /api/post/*  (/api/post/1/2 /api/post/a/b/c)
复制代码

环境变量

Next.js 支持开发者在项目根目录下面创建三个文件分别存放不同的环境变量

  • .env(所有环境)
  • .env.development(开发环境)
  • .env.production(生产环境)

然后通过 process.env.变量名 获取

// 设置环境变量
DB_HOST=localhost
DB_USER=myuser
DB_PASS=mypassword
复制代码
// 获取环境变量
export async function getStaticProps() {
  const db = await myDB.connect({
    host: process.env.DB_HOST,
    username: process.env.DB_USER,
    password: process.env.DB_PASS,
  })
}
复制代码

默认情况下,环境变量是用于服务端 Node.js,如果开发者希望在浏览器上使用环境变量,则需要给环境变量添加  NEXT_PUBLIC_,表示将环境变量暴露给浏览器端:

NEXT_PUBLIC_HELLO=hello
复制代码

内置 CSS 支持

Next.js 允许开发者在 JavaScript 文件导入 SCSS/CSS 文件。

添加全局样式

要添加全局样式,就需要使用 自定义 App

开发者只需要在 pages 目录下,添加一个 _app.js 文件,然后在此文件中导入全局样式表即可。

import '../global.css'
// This default export is required in a new `pages/_app.js` file.
export default function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}
复制代码

添加组件级样式模块

开发者要使用样式模块,则样式文件命名必须为一下几种:

  • *.module.css
  • *.module.scss

CSS Modlue 通过自动创建唯一的类名来在本地范围内定义 CSS,这使开发者可以在不同文件中使用相同的 CSS 类名称,而不必担心冲突。

//  button.module.css
.error {
  color: white;
  background-color: red;
}

// Button.js
import styles from './button.module.css'

export function Button() {
  return (
    <button
      type="button"
      // Note how the "error" class is accessed as a property on the imported
      // `styles` object.
      className={styles.error}
    >
      Destroy
    </button>
  )
}
复制代码

五、参考文档

文章分类
前端
文章标签