$ ls ~yifei/notes/

Nextjs 教程

Posted on:

Last modified:

当我们要写一个稍微复杂的 React 应用的时候,就需要路由功能了,比较流行的是 react-router, 这是一个很好的库。但是当我们已经用到路由的时候,下一步就该考虑如何做服务端渲染了,所以 直接上 next.js 吧。

创建第一个 next 项目:

pnpm create next-app MY-APP --ts

// 添加一些常用的库
cd MY-APP
pnpm add tailwindcss postcss autoprefixer axios react-hook-form dayjs nprogress

核心概念--页面

Next 的核心概念是页面。按照约定,放在 /pages 文件夹中的每一个组件都是一个页面,每个组件 需要使用 export default 导出。

/pages/index.tsx 对应的自然是首页了。/pages/xxx/index.js 对应的是 /xxx 路径。

import type {NextPage} from 'next'

const HomePage: NextPage = () => {
  return <div>Welcome to Next.js!</div>
}

export default HomePage

pnpm next 就可以看到首页啦!

作为一个服务端渲染框架,Next.js 既可以预先编译生成静态文件,也可以每个请求实时编译。不过, 推荐的方式是预先编译

Next.js 完全按照文件的物理路径来确定路由,参数就体现在文件路径中。要实现 post/1,直接 定义 pages/post/[id].js 文件即可。

获取数据

在一个页面中,export 一个 async 函数 getStaticProps/getServerSideProps 就可以实现获取 服务端的数据。

  • getStaticProps 对应静态生成网页,也就是预先编译 html;
  • getServerSideProps 对应服务端渲染,也就是每个请求来的时候,服务端实时渲染出 html。

getStaticProps 还需要使用 getStaticPaths 函数配合生成所需路径,比如所有的文章的 id.

// pages/post/[id].js
export async function getStaticProps({params}) {

  const res = await axios.get(`http://example.com/api/post/${params.id}`)
  return {
    props: res.json  // 传递给页面组件的 props
  }
}

// 导出所有需要静态生成的页面的参数
export async function getStaticPaths() {
  return {
    paths: [
      { params: { ... } }
    ],
    fallback: true // 是否允许未生成的页面实时渲染
    // fallback 的选项有三个:
    // 1. false, 未生成的页面直接 404
    // 2. true, 客户端请求数据渲染
    // 3. 'blocking', 服务端渲染
  };
}

getStaticProps 获得的参数是:context, 其中比较重要的几个属性:

  • params 路径中的参数,比如 {id: xxx}
  • req/res 请求响应
  • query query_string

getStaticPaths 和 getStaticProps 会在 build 的时候运行,不过在开发阶段(next dev),每个 请求都会触发 getStaticPaths.

当使用服务端渲染的时候,每次从客户端路由到一个页面,会调用 getServerSideProps 来获取数据。

export async function getServerSideProps({params}) {  // 同样的 context 参数
  return {
    props: {}, // 传递给页面组件的 props
  }
}

在这个函数中,应该直接读取数据库或者外部 API. 除此之外,传统方式是在客户端获取数据,可以 使用 useSWR 库。

样式

Next.js 中必须在 pages/_app.js 中导入全局的 css,这个文件会应用到所有页面。除此之外, 不能再导入别的自定义全局 css 文件。

// 自定义样式
import '../styles.css'
// 第三方库的样式
import 'antd/dist/style.css'

export default function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

tailwind

首先,安装 tailwindcss

pnpm add tailwindcss postcss autoprefixer
pnpm tailwindcss init -p

编辑 tailwind.config.js

module.exports = {
  content: [
    "./src/**/*.{js,ts,jsx,tsx}",
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

确保 _app.js 中已经引入了 global.css,然后编辑 global.css

@tailwind base;
@tailwind components;
@tailwind utilities;

URL 参数和链接

router

URL 参数需要使用 router 来手动读取,还可以使用 router.push 在 JS 中跳转。

import {useRouter} from 'next/router'

function MyPage() {
  const router = useRouter()
  const path = router.pathname
  const {keyword} = router.query

  const query = {foo: "bar"}
  const onSubmit = () => {
    router.push("/ok")
    // or
    router.push({pathname: "/ok", query: {foo: "bar"}})
  }
}

默认情况下,跳转后还会回滚到页面顶部。如果要跳转到外部链接,直接使用 window.location 就好了。

router 的其他常用方法还有:

  • back(),返回
  • reload(), 刷新
  • replace(), 类似 push,但是不增加 history 条目

Link

Next.js 中的链接是这样的:

import Link from 'next/link'

// about
<Link href="/about"><a>About</a></Link>

// blog/hello
<Link href={{pathname: "/blog/[slug]", query: {slug: "hello"}}}>
    <a>{post.title}</a>
</Link>

// q?kw=hello
<Link href={{pathname: "q", query: {kw: "hello"}}}>
    <a>Search</a>
</Link>

静态文件

直接放到 /public 目录,就能在根路径访问了。

图片

Next.js 提供了 Image 组件,相对于裸 <img/> 标签做了不少优化。需要使用 import 显式导入。

import Image from 'next/image'

function Home() {
  return (
    <>
      <h1>My Homepage</h1>
      <Image
        src="/me.png"
        alt="Picture of the author"
        // width={500} automatically provided
        // height={500} automatically provided
        // blurDataURL="data:..." automatically provided
        // placeholder="blur" // Optional blur-up while loading
      />
      <p>Welcome to my homepage!</p>
    </>
  )
}

Header

Next.js 中提供了 next/head 包,省去了使用 react-helmet 修改 header 的逻辑。在 meta 标签 中最好使用 key,这样可以避免重复渲染多个标签。

不过,并不能在 head 中增加 css,而要使用 import 的方式。

import Head from 'next/head'

function IndexPage() {
  return (
    <div>
      <Head>
        <title>My page title</title>
        <meta property="og:title" content="My page title" key="title" />
      </Head>
      <Head>
        // 重复
        <meta property="og:title" content="My new title" key="title" />
      </Head>
      <p>Hello world!</p>
    </div>
  )
}

错误页面

Next.js 可以自定义 404 和 500 错误页面

部署

修改 next.config.js, 增加 output: standalone 选项。

module.exports = {
  // ... rest of the configuration.
  output: 'standalone',
}

然后根据官方 Dockerfile 修改:https://github.com/vercel/next.js/tree/canary/examples/with-docker 在这个 dockerfile 中已经包含了复制 .next/static, .next/public 的逻辑,无需手动添加。

参考

  1. https://haodong.io/render-client-side-only-component-in-next-js
  2. https://github.com/vercel/next.js/blob/canary/examples/progressive-render/pages/index.js
  3. Install tailwind CSS with Next.js
  4. https://nextjs.org/docs/advanced-features/output-file-tracing
WeChat Qr Code

© 2016-2022 Yifei Kong. Powered by ynotes

All contents are under the CC-BY-NC-SA license, if not otherwise specified.

Opinions expressed here are solely my own and do not express the views or opinions of my employer.

友情链接: MySQL 教程站