Next.js 14 新特性:引领 Web SSR 技术

开始

Next.js 14 推出了新的 App router ,结合 React 实现了 Server Component 和 Streaming 流式渲染,使用 turbopack 优化打包效率。从开发体验到项目性能,都更上一层楼。

外加它背后的 Vercel 公司 商业化的成功,相信 Next.js 会越做越好,有可能会成为 Web 开发的最主流框架,和解决方案(包括部署、运维)。

想要学习的话,推荐大家跟着 Next.js 官网的教程做一个小项目。我认真看了一下,这个教程的体验还是很好的,很多代码都帮你写好了,学起来非常方便快捷。

App router

App router 可以和 Pages router 并存(有点像 class 和 fn 组件),App router 可解锁 Server component 和 Streaming 等新特性,Next.js 14 推荐使用。

顶级路由是 app 目录,下级可创建自己的目录。每个目录下面创建 page.tsx 来定义页面内容。

tsx 复制代码
export default function Page() {
    return <h1>Hello, Dashboard Page!</h1>
}

动态路由可以使用 [xxx] 形式来定义,例如

tsx 复制代码
// app/blog/[slug]/page.tsx
export default function Page({ params }: { params: { slug: string } }) {
    return <div>My Post: {params.slug}</div>
}

如果只是想通过文件夹组织代码,而不想体现到路由中,可以使用 Route Group 路由分组,用 (xxx) 形式定义目录名。

想要定义服务端 API 接口,可使用 api/route.ts 目录定义。

其中可定义 GET POST 等函数,并通过 request 和 response 来处理逻辑。

ts 复制代码
export async function GET(request: Request) {
  const { searchParams } = new URL(request.url)
  const id = searchParams.get('id')
  const res = await fetch(`https://data.mongodb-api.com/product/${id}`, {
    headers: {
      'Content-Type': 'application/json',
      'API-Key': process.env.DATA_API_KEY!,
    },
  })
  const product = await res.json()
 
  return Response.json({ product })
}

Layout

路由的目录下不可能只有一个 page.tsx 文件,还可以添加 Layout.tsx 模板。

顶级路由的 Layout 是必须的,用于定义 <html><body> 标签。其他下级路由可自定义 Layout ,可多级嵌套。

tsx 复制代码
// page/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

路由目录下还可以定义 Template Loading Error Not-Found 等,功能非常强大。

Server Component

Next.js 默认所有的组件都是 Server Component 服务端组件,即服务端渲染 SSR 。它的好处主要有两点:安全和性能。

服务端处理数据会更加安全。服务端可缓存数据、直接渲染 HTML 、还可支持 Streaming 流式渲染,这些方式都会提升性能。

如果有些组件需要用到浏览器的 API ,则需要标记为客户端组件,使用 'use client'。例如 useEffect 就需要客户端组件,其他常见的还有 useState 、 DOM 事件、路由和 url 操作、表单校验等很多。当一个组件为客户端组件时,在其中引入的所有子组件都是客户端组件。

tsx 复制代码
'use client'

import { useEffect } from 'react'

export default function About() {
  useEffect(() => {
    console.log('done')
  }, [])

  return <p>About</p>
}

Server Component 是获取数据,并一次性渲染出结果(构建时,或者运行时),重点在于渲染。Client Component 是渲染完成以后,还有其他的交互和更新,重点在于交互。所以两者各有各的职责,要相互配合。

Streaming 流式渲染

当一个页面因为数据加载过慢时,会导致页面加载卡顿。

如果前端后端分离,自己开发前端,解决这个问题很简单,只需要加一个 loading 状态即可。

而在 Next.js 中也非常简单,只需要在路由目录加一个 loading.tsx ,Next.js 会自动实现 loading 状态。

tsx 复制代码
// app/dashboard/loading.tsx
export default function Loading() {
  // You can add any UI inside Loading, including a Skeleton.
  return <LoadingSkeleton />
}

这种是简单粗暴的 loading 整个页面。如果页面模块较多,有可能有些模块加载快,有些模块加载慢,最好的方式是一部分一部分的加载 ------ 这就是 Streaming 流式渲染。

Streaming 也不是新东西了,当年 facebook 在 PHP 时代就搞过这个,但后来没流行开来,说明通用性不强。

而 Next.js 结合 React 实现起来就非常简单,主要用到 <Suspense> 组件。Page 只是容器,具体的数据获取在各个子组件内部进行。

tsx 复制代码
// app/dashboard/page.tsx
import { Suspense } from 'react'
import { PostFeed, Weather } from './Components'
 
export default function Posts() {
  return (
    <section>
      <Suspense fallback={<p>Loading feed...</p>}>
        <PostFeed />
      </Suspense>
      <Suspense fallback={<p>Loading weather...</p>}>
        <Weather />
      </Suspense>
    </section>
  )
}

我录制了一个 gif 动图,可以看看效果:

获取数据和缓存

Server Component 可以直接在一个 async 函数中请求数据,也可以在服务端 API 或者 Server Action 中获取数据。

tsx 复制代码
async function getData() {
  const res = await fetch('https://api.example.com/...')
  if (!res.ok) {
    throw new Error('Failed to fetch data')
  }
  return res.json()
}

export default async function Page() {
  const data = await getData()
  return <main>xxx</main>
}

但是,Next.js 默认情况下会缓存获取的数据,如不想缓存,需要增加 revalidate 机制,如按时间,或不缓存。

ts 复制代码
fetch('https://...', { next: { revalidate: 3600 } })

fetch('https://...', { cache: 'no-store' })

获取第三方数据时,可使用 noStore() 做标记。

ts 复制代码
import { unstable_noStore as noStore } from 'next/cache';

export async function fetchRevenue() {
  // Add noStore() here to prevent the response from being cached.
  // This is equivalent to in fetch(..., {cache: 'no-store'}).
  noStore();
  
  // get and return db data
}

PS:Next.js 还有非常复杂的 缓存机制,从服务端和客户端、从路由和数据。例如在创建页提交表单以后,回到列表页,需要重新验证页面缓存。

ts 复制代码
revalidatePath('/dashboard/invoices');

Server Action

'use server' 标记一个 ts 文件(里面的所有函数),或者一个 ts 函数,则可把他们标记为 Server Action 即在服务端执行的代码,一般用户客户端提交数据。【注意】'use server' 不是标记 React 组件的,而是针对函数的。

Server Component 中,可以直接定义一个 async 函数中为 Server Action ,也可以引入一个文件。

tsx 复制代码
// Server Component
export default function Page() {
  // Server Action
  async function create() {
    'use server'
    // ...
  }

  return (
    // ...
  )
}

而 Client Component 只能引入一个 Server Action 文件。或者把 create 函数作为属性,传递给客户端组件。

tsx 复制代码
// app/actions.ts
'use server'
export async function create() {
  // ...
}

// app/ui/button.tsx
import { create } from '@/app/actions'
export function Button() {
  return (
    // ...
  )
}

Server Action 函数可用于 <form> 或者 DOM 事件、 useEffect 等任何地方,Next.js 为它做了很多工作,例如:

  • 服务端组件,可在页面 JS 未加载完成的情况下,进行 form 提交
  • 客户端组件,如页面 JS 未加载完成,form 提交会暂存一个队列,页面渲染完再自动执行提交
  • Server Action 中可使用 Next.js 数据缓存机制
  • 背后统一使用 POST 请求来保证兼容性和安全性

Next.js 使用 Server Action 做到了进一步的 Hybration "水合",即在开发体验上模糊前端和服务端的界限。你都不用单独开发服务端接口了,所有的都在一个一个 JS 方法中调用和传递。

这样做:第一,可以降低对研发人员的技术要求,只要懂 React 就能快速上手操作。第二,给 Next.js 极大的性能优化空间,一切都听它在背后指挥。

但这样做的坏处,是可能又会培养一批不懂 HTTP 协议的"Next 工程师",类似一些不懂 JS 语法的"Vue 工程师"一样。

Server Action 是否重回 PHP 时代?

Next.js 14 发布 Server Action 遭到很多吐槽,又重新回到 PHP 时代。但我不这么认为。

具体可以看我上一篇博客 《Nestjs 助力 Offer : 论服务端技能对于前端人员的重要性》 里面有详细写我的观点。

最后

分享一些双越老师 1v1 简历优化的案例的 B 站小视频,供找工作的同学参考。

相关推荐
Ciito4 分钟前
vue项目使用eslint+prettier管理项目格式化
前端·javascript·vue.js
fighting ~40 分钟前
react17安装html-react-parser运行报错记录
javascript·react.js·html
老码沉思录1 小时前
React Native 全栈开发实战班 - 列表与滚动视图
javascript·react native·react.js
abments1 小时前
JavaScript逆向爬虫教程-------基础篇之常用的编码与加密介绍(python和js实现)
javascript·爬虫·python
老码沉思录1 小时前
React Native 全栈开发实战班 - 状态管理入门(Context API)
javascript·react native·react.js
爱编程的鱼2 小时前
Node.js事件循环:解锁异步编程的奥秘
node.js
南暮思鸢2 小时前
Node.js is Web Scale
经验分享·web安全·网络安全·node.js·ctf题目·hackergame 2024
文军的烹饪实验室2 小时前
ValueError: Circular reference detected
开发语言·前端·javascript
程序员小杰@2 小时前
Playwright 快速入门:Playwright 是一个用于浏览器自动化测试的 Node.js 库
node.js
Martin -Tang3 小时前
vite和webpack的区别
前端·webpack·node.js·vite