4月25日,React官方宣布React19在NPM上推出,可以先行下载使用。
正好此博客也是NextJS搭建的,尝试一下React19带来哪些变化。
官方的建议是先安装稳定版本的React18.3,以在更新到19前发现一些潜藏的问题。检查无误后,就可以安装React-19(还处于BETA)使用新API。React-v19的升级指南
React在沉寂一长段时间后,听取社区的意见,改掉许多开发痛点以及优化框架。重磅推出了React19
重大的改变有以下几点:
<form>
标签以及配套的Hooks表单操作。use()
等等。ReactCompiler可以说是19里最让人激动的东西,它是一个新的编译器,用于帮程序员优化React代码。比如说先前的useMemo,useCallback等等这一系列的优化钩子,不说使用起来麻烦,使用不当甚至还会造成负优化。于是React推出了Compiler直接自动处理代码,避免了负优化的现象。
简单来说,Compiler做到的事情就是将组件中每个元素,每一个函数都进行缓存,只有当发生变化的时候才会重新缓存,不然就接着使用。
本文主要是说使用而非原理,具体可以查看这篇文章我已彻底拿捏 React Compiler
对于React-Compiler的启用,我们首先要对我们的项目做一个检测。
1npx react-compiler-healthcheck
2
该脚本主要用于检测 1、项目中有多少组件可以成功优化:越多越好2、是否使用严格模式,使用了优化成功率更高 3、是否使用了与 Compiler不兼容的三方库
这个框架的检测效果如下:对于不同的框架使用Compiler的方法不同,Next启用Compiler需要先下载Next-canary版以及babel-plugin-react-compiler
1npm install next@canary babel-plugin-react-compiler
然后在next.config.js
:
1// next.config.js
2/** @type {import('next').NextConfig} */
3const nextConfig = {
4 experimental: {
5 reactCompiler: true,
6 },
7};
8
9module.exports = nextConfig;
10
便可以启动compiler对项目进行优化。成功优化后,可以在React-dev-tool就会看到Memo星星。
值得一说的是Compiler还在测试中,存在不少问题,比如与i18n的客户端组件存在一些冲突等,所以有待观望
React19更新许多新的Hooks,包括use()
,useOptimistic()
,useFormStatus()
,绝大多是都是为了Action也就是<form>
标签所适配的。。
因为没用到表单,所以我只先使用use()
。
use用于获取资源的值,比如说Promise或者Context。和其他钩子不同,它可以在if语句中使用。
他的具体原理如下(取自官方文档):
当使用 Promise 调用时, use API 会集成 和 Suspense错误边界。当传递给的 use Promise 处于挂起状态时,组件调用 use将挂起。如果调用 use 的组件包装在 Suspense 边界中,则将显示回退。解析 Promise 后,Suspense 回退将替换为使用 use API返回的数据呈现的组件。如果传递给 use 的 Promise被拒绝,则将显示最近的错误边界的回退。
基础用法如下:
1const value = use(resource);
2
值得注意的是:
在此项目中的使用如下:
1"use client";
2import { Suspense } from "react";
3import { GhostPointer } from "./GhostPointer";
4import { MyTypeWrite } from "./TypeWrite";
5import { DailyWord } from "@/utils/getDailyWord";
6import ErrorBoundary from "./ErrorBoundary";
7
8export function Banner({
9 language,
10 isGetDailyWord,
11 wordsFetch,
12}: {
13 wordsFetch?: Promise<DailyWord>;
14 language: string;
15 isGetDailyWord: boolean;
16}) {
17 return (
18 <ErrorBoundary
19 fallback={
20 <GhostPointer>
21 <span
22 style={{
23 display: "flex",
24 lineHeight: "250px",
25 fontSize: "4rem",
26 justifyContent: "center",
27 color: "white",
28 }}
29 >
30 ⚠️Something went wrong
31 </span>
32 </GhostPointer>
33 }
34 >
35 <Suspense
36 fallback={
37 <GhostPointer>
38 <span
39 style={{
40 display: "flex",
41 lineHeight: "250px",
42 fontSize: "4rem",
43 justifyContent: "center",
44 color: "white",
45 }}
46 >
47 Loading...
48 </span>
49 </GhostPointer>
50 }
51 >
52 <GhostPointer>
53 <MyTypeWrite
54 language={language}
55 wordsFetch={wordsFetch}
56 isGetDailyWord={isGetDailyWord}
57 />
58 </GhostPointer>
59 </Suspense>
60 </ErrorBoundary>
61 );
62}
63
利用ErrorBoundary以及Suspense包裹目标组件,在解析中以及解析失败后有相对应的UI呈现。再从服务端传入wordsFetch函数再进行use解析。
1//layout
2import { Banner } from "../components/Banner";
3import { getDailyWord } from "@/utils/getDailyWord";
4
5export default async function FrontLayout({
6 children,
7 params: { language },
8}: {
9 children: React.ReactNode;
10 params: { language: string };
11}) {
12 const wordsFetch = getDailyWord();
13 return (
14 <div className="flex flex-col items-center">
15 <div className="w-[100vw]">
16 <Banner
17 language={language}
18 isGetDailyWord={true}
19 wordsFetch={wordsFetch}
20 ></Banner>
21 </div>
22 <section className="w-full">{children}</section>
23 </div>
24 );
25}
26//TypeWrite
27("use client");
28import { usePathname } from "next/navigation";
29import { ReactTyped } from "react-typed";
30import { getDailyWord } from "@/utils/getDailyWord";
31import { Suspense, use, useState } from "react";
32import { DailyWord } from "@/utils/getDailyWord";
33import { splitPathname } from "@/utils/dealPathname";
34import { useTranslation } from "@/app/i18n/client";
35export function MyTypeWrite({
36 language,
37 isGetDailyWord,
38 wordsFetch,
39}: {
40 language: string;
41 isGetDailyWord: boolean;
42 wordsFetch?: Promise<DailyWord>;
43}) {
44 let word;
45 const pathName = usePathname();
46 const title = splitPathname(pathName);
47 const { t } = useTranslation(language, "translations");
48 if (isGetDailyWord && wordsFetch) {
49 const words = use(wordsFetch);
50 word = language === "zh-CN" ? words.note : words.content;
51 }
52 return (
53 <ReactTyped
54 strings={!word ? [t(title)] : [word]}
55 typeSpeed={50}
56 style={{
57 display: "flex",
58 lineHeight: "250px",
59 fontSize: "4rem",
60 justifyContent: "center",
61 color: "white",
62 }}
63 />
64 );
65}
66
最终效果可见博客首页(代码存放于github)
React19的更新远不止于此,目前我只用上这两个方法。
还有关于乐观更新,表单操作等等的钩子尚未使用。
以及令人诟病的Ref转发也得到了优化。
报错提示更人性化等等等等。
在未来会慢慢投入使用,投入生产。