lynn1286

V1

2023/01/07阅读:22主题:默认主题

Next13新功能整理

系统要求:

  • Node.js 14.6.0 或更新版本 (测试版本 Node.js v16.8)
  • 支持 Mac OS、Windows(包括 WSL)和 Linux

安装:

npx create-next-app@latest --typescript 
# or yarn create next-app --typescript 
# or pnpm create next-app --typescript

React 服务器组件

服务端组件

Next13 支持 React 服务器组件,服务器组件能够在服务器端执行和渲染 React 组件,以前会影响客户端上 JavaScript 包大小的大型依赖项现在可以完全保留在服务器上,从而提高性能。 当一个路由被加载时,Next.js和React runtime将被加载,它是可缓存的,而且大小可预测。这个运行时不会随着你的应用程序的增长而增加大小。此外,运行时是异步加载的,使你的HTML从服务器上被逐步增强到客户端上。 只有当客户端的交互性通过客户端组件在你的应用程序中使用时,才会添加额外的JavaScript。

个人理解: 渲染服务器组件实际上是一个 API 调用,以获取序列化的虚拟 DOM,然后在浏览器中实现它。 最重要的是,服务器组件用于呈现非交互式内容,因此没有事件处理程序、没有 React 勾子,也没有仅限浏览器的 API。 最显着的好处是可以自由访问服务器组件中的任何后端资源和机密。它更安全(数据不会泄漏)和更快(代码不会泄漏)。

客户端组件

客户端组件是在客户端渲染的。 客户端组件也可以在服务器上预渲染,并在客户端进行水合化。 要使用客户端组件,在app中创建一个文件,并在文件的顶部(在任何导入之前)添加 "use client" 。

'use client'

import { useEffect } from 'react'

export default function Client({
  console.log(
    'Client page rendering: this should only be printed on the server during ssr, and client when routing'
  )

  useEffect(() => {
    console.log('Client component rendered')
  })

  return (
    <div>
      <h1>Client Page</h1>
      {/* Uncommenting this will result in an error complaining about inconsistent
            rendering between client and server, which is very true */}
      {/* <p>My secret env: {process.env.MY_SECRET_ENV}</p> */}

      <br></br>
      <p>Render log and useEffect log should be printed in browser console</p>
    </div>

  )
}

当页面首次加载时,它是由 SSR 渲染的;因此您应该在服务器控制台中看到第一个日志;在客户端路由期间,两条日志消息都将出现在浏览器控制台中。

Next.js 13 路由系统

增加 app 目录(测试版本)

目前还在测试阶段,需要在 next.config.js 中开启才能使用, 默认情况下app下的所有文件都属于服务器组件:

/** @type {import('next').NextConfig} */
const nextConfig = {
    experimental: {
        appDirtrue,
    },
}

module.exports = nextConfig
route Next 12 Next 13
/ pages/index.js app/page.js
/blog pages/blog.js app/blog/page.js
/blog/new pages/blog/new.js app/blog/new/page.js

另外,在服务器启动的时候 Next 会在 app 目录下检测是否有根布局组件,如果没有会帮自动创建。 Next.js 13引入了一个全新的 app 文件夹,其中有一个完全翻新的路由系统。 它包括许多改进,其中最好的礼物是新的布局机制。 只要它们的路由不发生冲突,app文件夹可以与旧的页面文件夹共存,这使你可以逐步采用。

新的文件夹结构

app 文件夹要求每个路由都是一个文件夹,使定义路由更加明确。 一个路由文件夹通常包含以下路由文件(有.js|.ts|.jsx|.tsx后缀)。

  • page - 为这个路由提供特定的用户界面。
  • layout - 为这个路由和所有后代路由提供布局UI。
  • loading - 当路由的服务器组件正在加载时,提供一个加载的用户界面
  • error - 为处理此路由内和下的错误提供UI(除非由子层的另一个错误路由处理)。

Layout and nesting

在以前的版本中:

// pages/index.js

export default function Page({
  return (
    /** Your content */
    <div>Your content</div>
  )
}

Page.getLayout = function getLayout(page{
  return (
    <Layout>
      <NestedLayout>{page}</NestedLayout>
    </Layout>

  )
}

// pages/_app.js
export default function MyApp({ Component, pageProps }{
  // 使用在页面级别定义的布局(如果可用)
  const getLayout = Component.getLayout || (page => page)
  return getLayout(<Component {...pageProps} />)
}

使用Next.js 13

新的路由系统使 "layout "成为一流的公民。 在app下的任何一级文件夹(即任何一级路由),都可以使用layout.tsx来明确定义一个容器组件。 这个容器组件将自动环绕该文件夹内和下的所有页面。 如果在不同级别有多个layout.tsx组件,就会自动构建一个包裹的层次结构。 layout 官方文档

错误处理

React有一个Error Boundaries的概念,用于以结构化的方式捕获错误。 Next.js的路由文件夹自然形成了一个组件层次结构。 为什么我们不发明一个公约来处理任何层次的路由的错误呢? 这就是error.tsx文件的作用。它只不过是在 中包装下级组件树的语法糖。 官方文档

Next.js 13 数据获取

在 Next.js 13 之前,页面级数据获取模式非常简单

  • 如果页面(大部分)是静态的,使用 getStaticProps 获取数据,以便在构建时(和ISR时)进行获取。
  • 如果页面是动态的,使用 getServerSideProps 在服务器端获取数据。
  • 对于依赖于用户交互的数据,在 useEffect 页面呈现后在钩子中在客户端进行抓取。

这已在 Next.js 13 中完全翻新(如果您选择实验性功能)。但在我们看到新东西之前,让我们反思一下旧世界的问题。

旧模式的问题

  • 看起来不自然
export default function Page({ data }{
  // Render data...
}

export async function getServerSideProps({
  const res = await fetch(`https://.../data`)
  const data = await res.json()
  return { props: { data } }
}

你必须严格按照约定来书写代码。

  • Prop drilling

引发props嵌套传递问题,层层传递 , 解决办法是使用 Context API 或者 组件组合模式 ,但这要么会阻碍组件的可重用性,要么需要仔细设计它们。

  • 不成功便成仁 (要么有要么无)

当使用 getServerSideProps 时,无论页面加载是由浏览器重载还是由客户端路由触发,新的页面内容都不会显示,直到数据获取完全完成(异步getServerSideProps函数解析)。如果页面同时包含 "快速数据 "和 "慢速数据",这可能是个问题。 例如,数据仪表板是一个典型的场景:一些卡片可以瞬间加载,而另一些则需要很多秒。

Next.js 13中修改了什么?

Next.js 13中引入的新数据获取模式放弃了之前你所熟悉的一切:

  • getStaticProps
  • getServerSideProps

甚至对于客户端的获取,也有一个新的使用钩子,有可能取代useEffect中的旧的获取方式。

异步服务器组件

在旧世界里,组件是同步的,你不能在组件的顶层等待。在Next.js 13中,所有组件默认为 "服务器",可以是异步的。最后,我们可以在React组件中使用我们熟悉的async/await语法。 这使得数据的获取变得更加容易和灵活。最重要的是,你可以将服务器端的获取逻辑分布到多个地方,并将它们与使用数据的组件搭配在一起。 示例:

// app/server-async-fetching/page.tsx
import { Suspense } from 'react'
import Quote from '../../components/server/quote'

export default function AsyncLoading({
  return (
    <>
      <h1>Server Component Async Fetching</h1>
      <div className="flex flex-col gap-4 w-full h-full">
        {/* @ts-ignore */}
        <Quote />
        {/* @ts-ignore */}
        <Quote slow={true} />
      </div>
    </>

  )
}

// lib/quote.ts
import sleep from 'sleep-promise'

export async function getQuote(delay = 0{
  if (delay) {
    await sleep(delay)
  }
  console.log('Getting quote')
  return (await fetch('https://api.quotable.io/random?tags=technology')).json()
}

// components/server/Quote.tsx
import { getQuote } from '../../lib/quote'
import os from 'os'

export default async function Quote({ slow }: { slow?: boolean }{
  const quote = await getQuote(slow ? 2000 : 0)
  return (
    <div className="container border border-blue-600 rounded p-4">
      <p>
        {slow ? 'Slow' : 'Fast'} component rendered on{' '}
        <span className="text-orange-600">${os.hostname()}</span>
      </p>
      <blockquote className='"text-xl italic font-semibold text-gray-900 p-4'>
        {quote.content}
      </blockquote>
    </div>

  )
}

// app/server-async-fetching/Loading.tsx
export default function Loading({
  return <p>Loading...</p>
}



以上代码,在数据没有请求回来之前,Loading 组件一直展示到客户端以及服务端组件都可以渲染为止,这也是典型的 不成功便成仁(all-or-nothing) 🥶 。 我们可以通过在组件周围添加 来通过一个小的修复来改进它。 Suspense 最初是由 React 添加的,用于支持代码拆分;现在它可用于为尚未解析的异步组件提供后备 UI,因此它们可以无阻塞地呈现:

import { Suspense } from 'react'
import Quote from '../../components/server/quote'

export default function AsyncLoading({
  return (
    <>
      <h1>Server Component Async Fetching</h1>
      <div className="flex flex-col gap-4 w-full h-full">
        <Suspense fallback={<p>Fast component loading...</p>}>
          {/* @ts-ignore */}
          <Quote />
        </Suspense>

        <Suspense fallback={<p>Slow component loading...</p>}>
          {/* @ts-ignore */}
          <Quote slow={true} />
        </Suspense>
      </div>
    </>

  )
}

现在好多了。😊 你可以看到,页面及其两个子组件的渲染是完全异步的。 React已经扩展了Suspense的功能,以支持任意的异步操作。它现在可以完美地与异步服务器组件协同工作。Suspense最酷的地方在于,"取消暂停 "一个组件不需要额外的API请求或WebSocket连接。相反,新的页面内容是通过向HTML文档追加虚拟DOM(用<script/>包裹)来流向浏览器的。这可以通过查看文档请求的时间来确认。

自动去除重复 Fetch

另一个有趣的事情你可能已经注意到了,尽管快速和慢速组件分别进行了API请求(使用fetch),但它们得到的内容是一样的。 这是由于来自 React 的另一个重要更新——获取调用(在服务器端)被自动删除重复数据: 如果你需要在一棵树上的多个组件中获取相同的数据(如当前用户),Next.js会自动将有相同输入的fetch请求缓存在一个临时缓存中。 这个功能有助于我们解决旧(nextjs12)的数据获取模式的另一个问题--Prop drilling。 有了自动提取重复数据,在一次渲染过程中提取相同的资源只产生一个HTTP请求,所以你可以自由地在你需要渲染的地方提取数据,而不用担心额外的费用问题。很酷,不是吗?🥳

客户端数据获取

在Next.js(和React)的前几个版本中,客户端的数据获取不在框架的关注范围内。你可以使用任何你想要的库,第三方工具如SWR和react-query都很好地解决了这个问题。 Next.js 13(应该更公平地说,最新的React)向前迈进了一步,提供了一个内置的使用钩子,作为从承诺中解包数据的通用API。它不像直接使用async/await那样理想(正如React所解释的那样),但它使客户端的获取感觉与服务器端足够接近。 再次通过一个例子来看看它是如何工作的(所有的组件都是客户端组件,因为它们被标记为 'use client' ):

// app/client-fetching/page.tsx
'use client'

import { useState, Suspense } from 'react'
import Quote from '../../components/client/quote'

export default function ClientFetching({
  // use a button to toggle loading of components to make sure they're loaded client-side
  const [show, setShow] = useState(false)

  return (
    <>
      <h1>Client Fetching</h1>
      <button className="btn" onClick={() => setShow(true)}>
        Show Components
      </button>

      {show && (
        <>
          <div className="flex flex-col gap-4 w-full h-full">
            <Suspense fallback={<p>Fast component loading...</p>}>
              <Quote />
            </Suspense>

            <Suspense fallback={<p>Slow component loading...</p>}>
              <Quote slow={true} />
            </Suspense>
          </div>
        </>

      )}
    </>
  )
}


/
/ components/client/Quote.tsx
'use client'

import { getQuote } from '../../lib/quote'
import { use } from 'react'

const quoteFetch = getQuote()
const quoteFetchSlow = getQuote(2000)

export default function Quote({ slow }: { slow?: boolean }{
  const quote = use(slow ? quoteFetchSlow : quoteFetch)
  return (
    <div className="box">
      <p>{slow ? 'Slow' : 'Fast'} component rendered</p>
      <blockquote>{quote.content}</blockquote>
    </div>

  )
}

它比我们习惯的更干净:在 useEffect 中进行抓取并将结果存储在状态变量中。 它也非常接近它在服务器组件中的外观。 但是这里的 fetch 调用不会在客户端进行重复数据删除。 我不知道这是设计使然还是有待修复。😅

Next.js 13 Turbopack

作为 "Webpack的继任者",一个名为Turbopack的新JavaScript捆绑器是Next.js 13版本的最后一个重要更新。最受欢迎的JavaScript构建工具之一,Webpack,具有难以置信的可定制性和强大的功能,但偶尔也会出现迟缓和繁琐的情况。 Webpack的开发者创建了Turbopack,它是用Rust构建的,并承诺比原来的Webpack快700倍(比Vite这个更现代的替代品快10倍)。 一个变化是,Rust可以创建插件而不是JavaScript。精通系统语言的JavaScript开发人员的数量明显少于JS开发人员的总人数。 使用 next dev --turbo 启动你的开发服务器,如果你建立一个新的 Next.js 13 应用程序,你可以测试新的 Turbopack 捆绑器。

其他升级

  • next/image

Next.js 13引入了一个强大的新图像组件,使您能够轻松地显示图像,而无需进行布局转移,并按需优化文件以提高性能。

  1. 减少了客户端的JavaScript
  2. 更容易设计和配置
  3. 更容易访问,默认需要alt标签
  4. 与网络平台保持一致
  5. 更快,因为原生的懒惰加载不需要水化

单独升级 image 组件,查看文档

  • next/font

Next.js 13引入了一个全新的字体系统:

  1. 自动优化你的字体,包括自定义字体
  2. 移除外部网络请求以提高隐私和性能
  3. 为任何字体文件提供内置的自动自我托管功能
  4. 使用CSS的大小调整属性自动实现布局零的转变

这个新的字体系统允许你方便地使用所有谷歌字体,并考虑到性能和隐私。CSS和字体文件在构建时被下载,并与你的其他静态资产一起自我托管。浏览器不会向谷歌发送任何请求。

import { Inter } from '@next/font/google';
const inter = Inter();
<html className={inter.className}>

还支持自定义字体,包括支持字体文件的自动自我托管、缓存和预加载。

import localFont from '@next/font/local';
const myFont = localFont({ src'./my-font.woff2' });
<html className={myFont.className}>
  • next/link

next/link 不再需要手动添加 <a> 作为子项。

这是在 v12.2 中作为实验性选项添加的,现在是默认选项。在 Next.js 13 中,<Link> 始终呈现一个 <a> 并允许您将 props 转发到底层标签。例如:

import Link from 'next/link'
// Next.js 12: `<a>`必须是嵌套的,否则会被排除。
<Link href="/about">
  <a>About</a>
</Link>

/
/ Next.js 13: `<Link>`总是显示为`<a>`。
<Link href="/
about">
  About
</Link>

查看更多

13.1 的更新

最近的版本发布了, 下面是对13版本的巩固以及问题的修复:

  1. 改进了可靠性和对 app 目录的支持:
    1. No Layout Divs: 以前,应用程序目录添加了额外的
      元素,以便在导航时将布局滚动到视图中。在13.1版本中,这些额外的元素不再被创建。滚动行为被保留了。issue
    2. TypeScript Plugin: 新的TypeScript插件,为页面和布局配置选项提供建议,将文档直接引入你的IDE,并围绕服务器和客户端组件提供有用的使用提示(例如防止在服务器组件中使用useState)Learn more.
    3. Reliability Improvements: 修补了许多错误,包括改进对CSS模块的支持,正确去除布局和页面的cache()和fetch(),内存泄漏,以及其他。
    4. Less Client-Side JavaScript: 与pages目录相比,app 目录现在包含的客户端JavaScript少了9.3kB。无论你在应用程序中添加1个还是1000个服务器组件,这一基线都不会增加。React运行时暂时略大,增加的原因是React服务器组件运行时,它处理Next.js以前处理的机械。
  2. 内置模块转换(稳定)
  3. Import resolution for smaller bundles
  4. A light Node.js runtime for the edge, now stable for API routes
  5. Turbopack 的改进
  6. Next.js advanced Middleware
  7. Other improvements

最后附上文章的github地址

分类:

前端

标签:

React.js

作者介绍

lynn1286
V1