Skip to content

Commit a59e3df

Browse files
committed
feat: 测试
1 parent 3e31b14 commit a59e3df

File tree

9 files changed

+634
-1
lines changed

9 files changed

+634
-1
lines changed

markdown/index.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[
2+
{
3+
"path": "yyblog.md",
4+
"title": "yyblog",
5+
"tag":"技术/react"
6+
},
7+
{
8+
"path": "react19.md",
9+
"title": "react19",
10+
"tag":"随笔/生活"
11+
}
12+
]

markdown/react19.md

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
# React19
2+
3+
4月25日,React官方宣布React19在NPM上推出,可以先行下载使用。
4+
正好此博客也是NextJS搭建的,尝试一下React19带来哪些变化。
5+
6+
## 准备
7+
8+
官方的建议是先安装稳定版本的React18.3,以在更新到19前发现一些潜藏的问题。
9+
检查无误后,就可以安装React-19(还处于BETA)使用新API。
10+
[React-v19的升级指南](https://react.dev/blog/2024/04/25/react-19-upgrade-guide#typescript-changes)
11+
12+
## 变化
13+
14+
React在沉寂一长段时间后,听取社区的意见,改掉许多开发痛点以及优化框架。重磅推出了React19
15+
16+
**重大的改变有以下几点:**
17+
18+
- React-Compiler:帮助开发人员自动优化页面,减少甚至抛弃useMemo和useCallback。
19+
- Actions:新的`<form>`标签以及配套的Hooks表单操作。
20+
- New Hooks:新的增强操作钩子如`use()`等等。
21+
- Document Metadata:现在可以直接在单个组件里编写Meta数据。
22+
- Web components:React 代码现在将使我们能够合并 Web 组件。
23+
24+
### React-Compiler
25+
26+
ReactCompiler可以说是19里最让人激动的东西,它是一个新的编译器,用于帮程序员优化React代码。
27+
比如说先前的useMemo,useCallback等等这一系列的优化钩子,不说使用起来麻烦,使用不当甚至还会造成负优化。于是React推出了Compiler直接自动处理代码,避免了负优化的现象。
28+
29+
简单来说,Compiler做到的事情就是将组件中每个元素,每一个函数都进行缓存,只有当发生变化的时候才会重新缓存,不然就接着使用。
30+
本文主要是说使用而非原理,具体可以查看这篇文章[我已彻底拿捏 React Compiler](https://mp.weixin.qq.com/s/7XFn56O3ia5vHPqSaeo6GA)
31+
32+
对于React-Compiler的启用,我们首先要对我们的项目做一个检测。
33+
34+
```shell
35+
npx react-compiler-healthcheck
36+
```
37+
38+
> 该脚本主要用于检测
39+
> 1、项目中有多少组件可以成功优化:越多越好
40+
> 2、是否使用严格模式,使用了优化成功率更高
41+
> 3、是否使用了与 Compiler 不兼容的三方库
42+
43+
这个框架的检测效果如下:
44+
<img src="/imgs/React19-BETA/test.png" alt="react-server-components" />
45+
对于不同的框架使用Compiler的方法不同,Next启用Compiler需要先下载Next-canary版以及babel-plugin-react-compiler
46+
47+
```shell
48+
npm install next@canary babel-plugin-react-compiler
49+
```
50+
51+
然后在`next.config.js`:
52+
53+
```js
54+
// next.config.js
55+
/** @type {import('next').NextConfig} */
56+
const nextConfig = {
57+
experimental: {
58+
reactCompiler: true,
59+
},
60+
};
61+
62+
module.exports = nextConfig;
63+
```
64+
65+
便可以启动compiler对项目进行优化。
66+
成功优化后,可以在React-dev-tool就会看到Memo星星。
67+
<img src="/imgs/React19-BETA/compiler.png" alt="react-server-components"/>
68+
69+
> 值得一说的是Compiler还在测试中,存在不少问题,比如与i18n的客户端组件存在一些冲突等,所以有待观望
70+
71+
### New Hooks
72+
73+
React19更新许多新的Hooks,包括`use()`,`useOptimistic()`,`useFormStatus()`,绝大多是都是为了Action也就是`<form>`标签所适配的。
74+
75+
因为没用到表单,所以我只先使用`use()`
76+
77+
use用于获取资源的值,比如说Promise或者Context。和其他钩子不同,它可以在if语句中使用。
78+
79+
他的具体原理如下(取自官方文档):
80+
81+
> 当使用 Promise 调用时, use API 会集成 和 Suspense 错误边界。当传递给的 use Promise 处于挂起状态时,组件调用 use 将挂起。如果调用 use 的组件包装在 Suspense 边界中,则将显示回退。 解析 Promise 后,Suspense 回退将替换为使用 use API 返回的数据呈现的组件。如果传递给 use 的 Promise 被拒绝,则将显示最近的错误边界的回退。
82+
83+
基础用法如下:
84+
85+
```js
86+
const value = use(resource);
87+
```
88+
89+
**值得注意的是:**
90+
91+
- use 必须在 Component 或 Hook 中调用 API。
92+
- 首选在服务器组件中创建 Promise 并将其传递给客户端组件,而不是在客户端组件中创建 Promise。在客户端组件中创建的 Promise 会在每次渲染时重新创建。从服务器组件传递到客户端组件的 promise 在重新渲染时是稳定的。
93+
- 像useContext一样, use(context)总是在调用它的组件上方寻找最接近的上下文提供程序。它会向上搜索,并且不考虑要从中调用 use(context) 的组件中的上下文提供程序。
94+
- 将 Promise 从服务器组件传递到客户端组件时,其解析值必须可序列化才能在服务器和客户端之间传递。函数等数据类型不可序列化,并且不能是此类 Promise 的解析值。
95+
96+
在此项目中的使用如下:
97+
98+
```jsx
99+
"use client";
100+
import { Suspense } from "react";
101+
import { GhostPointer } from "./GhostPointer";
102+
import { MyTypeWrite } from "./TypeWrite";
103+
import { DailyWord } from "@/utils/getDailyWord";
104+
import ErrorBoundary from "./ErrorBoundary";
105+
106+
export function Banner({
107+
language,
108+
isGetDailyWord,
109+
wordsFetch,
110+
}: {
111+
wordsFetch?: Promise<DailyWord>;
112+
language: string;
113+
isGetDailyWord: boolean;
114+
}) {
115+
return (
116+
<ErrorBoundary
117+
fallback={
118+
<GhostPointer>
119+
<span
120+
style={{
121+
display: "flex",
122+
lineHeight: "250px",
123+
fontSize: "4rem",
124+
justifyContent: "center",
125+
color: "white",
126+
}}
127+
>
128+
⚠️Something went wrong
129+
</span>
130+
</GhostPointer>
131+
}
132+
>
133+
<Suspense
134+
fallback={
135+
<GhostPointer>
136+
<span
137+
style={{
138+
display: "flex",
139+
lineHeight: "250px",
140+
fontSize: "4rem",
141+
justifyContent: "center",
142+
color: "white",
143+
}}
144+
>
145+
Loading...
146+
</span>
147+
</GhostPointer>
148+
}
149+
>
150+
<GhostPointer>
151+
<MyTypeWrite
152+
language={language}
153+
wordsFetch={wordsFetch}
154+
isGetDailyWord={isGetDailyWord}
155+
/>
156+
</GhostPointer>
157+
</Suspense>
158+
</ErrorBoundary>
159+
);
160+
}
161+
```
162+
163+
利用ErrorBoundary以及Suspense包裹目标组件,在解析中以及解析失败后有相对应的UI呈现。
164+
再从服务端传入wordsFetch函数再进行use解析。
165+
166+
```tsx
167+
//layout
168+
import { Banner } from "../components/Banner";
169+
import { getDailyWord } from "@/utils/getDailyWord";
170+
171+
export default async function FrontLayout({
172+
children,
173+
params: { language },
174+
}: {
175+
children: React.ReactNode;
176+
params: { language: string };
177+
}) {
178+
const wordsFetch = getDailyWord();
179+
return (
180+
<div className="flex flex-col items-center">
181+
<div className="w-[100vw]">
182+
<Banner
183+
language={language}
184+
isGetDailyWord={true}
185+
wordsFetch={wordsFetch}
186+
></Banner>
187+
</div>
188+
<section className="w-full">{children}</section>
189+
</div>
190+
);
191+
}
192+
//TypeWrite
193+
("use client");
194+
import { usePathname } from "next/navigation";
195+
import { ReactTyped } from "react-typed";
196+
import { getDailyWord } from "@/utils/getDailyWord";
197+
import { Suspense, use, useState } from "react";
198+
import { DailyWord } from "@/utils/getDailyWord";
199+
import { splitPathname } from "@/utils/dealPathname";
200+
import { useTranslation } from "@/app/i18n/client";
201+
export function MyTypeWrite({
202+
language,
203+
isGetDailyWord,
204+
wordsFetch,
205+
}: {
206+
language: string;
207+
isGetDailyWord: boolean;
208+
wordsFetch?: Promise<DailyWord>;
209+
}) {
210+
let word;
211+
const pathName = usePathname();
212+
const title = splitPathname(pathName);
213+
const { t } = useTranslation(language, "translations");
214+
if (isGetDailyWord && wordsFetch) {
215+
const words = use(wordsFetch);
216+
word = language === "zh-CN" ? words.note : words.content;
217+
}
218+
return (
219+
<ReactTyped
220+
strings={!word ? [t(title)] : [word]}
221+
typeSpeed={50}
222+
style={{
223+
display: "flex",
224+
lineHeight: "250px",
225+
fontSize: "4rem",
226+
justifyContent: "center",
227+
color: "white",
228+
}}
229+
/>
230+
);
231+
}
232+
```
233+
234+
最终效果可见博客首页(代码存放于github)
235+
236+
### 其他
237+
238+
React19的更新远不止于此,目前我只用上这两个方法。
239+
240+
还有关于乐观更新,表单操作等等的钩子尚未使用。
241+
242+
以及令人诟病的Ref转发也得到了优化。
243+
244+
报错提示更人性化等等等等。
245+
246+
在未来会慢慢投入使用,投入生产。
247+
248+
[React19官方博客](https://react.dev/blog/2024/04/25/react-19)
249+
250+
[关于USE](https://react.dev/reference/react/use)
251+
252+
[关于Compiler](https://react.dev/learn/react-compiler#)

0 commit comments

Comments
 (0)