blog: React 18,新的严格模式.
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
0b3bdfc36a
commit
2f526f713c
74
data/blog/react-18-stricter-strict-mode.md
Normal file
74
data/blog/react-18-stricter-strict-mode.md
Normal file
@ -0,0 +1,74 @@
|
||||
---
|
||||
title: React 18,新的严格模式
|
||||
date: '2023-02-06'
|
||||
tags: ['React']
|
||||
draft: false
|
||||
summary: 好好的 useMemo、useEffect 居然执行了两次,我明明传入了依赖,为什么会执行两次呢?原来是 React 18 的破坏性改动!
|
||||
---
|
||||
|
||||
之前在开发模式时,一直记得 `useMemo` 在严格模式下不会二次执行, `useEffect` 在有传入 `deps` 时也不会二次执行。而今天我在排下面这段代码时,发现一个要命的事情!
|
||||
|
||||
```tsx
|
||||
const camera = useMemo(/* .. */, []);
|
||||
const renderer = props; // from parent
|
||||
|
||||
const controls = useMemo(
|
||||
() => new OrbitControls(camera, renderer.domElement),
|
||||
[camera, renderer],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// 同一个 controls 进入两次
|
||||
return () => {
|
||||
controls.dispose(); // 执行一次,controls 被释放
|
||||
};
|
||||
}, [controls]);
|
||||
```
|
||||
|
||||
`controls` 生成了两个,第一个似乎没用到了,第二个是后续要正常使用的对象,并且进入了 `useEffect` 中,而且进去了两次!
|
||||
这导致两个严重的问题就是,第一个 `controls` 没有被销毁,第二个 `controls` 被销毁了!
|
||||
第一个没销毁是内存泄漏,第二个被销毁了导致 `controls` 对象不可用了!
|
||||
|
||||
我一度以为是我对 useEffect 特性的记忆出现了偏差,后来我在官方文档翻了半天没啥收获,指到我看见了更新说明里的 [Stricter Strict Mode](https://github.com/facebook/react/blob/main/CHANGELOG.md#1820-june-14-2022:~:text=Stricter%20Strict%20Mode)。
|
||||
|
||||
## 新的严格模式
|
||||
|
||||
从更新日志可以看到,新的严格模式会自动卸载并再次重新挂载每个组件,这就解释了为什么 `useMemo` 和 `useEffect` 即使在有传入 `deps` 也会多执行一次。
|
||||
|
||||
这个特性是破坏性的,会影响之前版本的程序逻辑,所以 React 在[更新说明](https://github.com/facebook/react/blob/main/CHANGELOG.md#1820-june-14-2022:~:text=If%20this%20breaks%20your%20app%2C%20consider%20removing%20Strict%20Mode%20until%20you%20can%20fix%20the%20components%20to%20be%20resilient%20to%20remounting%20with%20existing%20state.)也建议如果旧的应用因为这个出现兼容性问题,建议先关掉 `strictMode`。
|
||||
|
||||
### 在 React 18 的测试代码
|
||||
|
||||
![React 18 Stricter Strict Mode.png](https://pan.ivanli.cc/api/v3/file/source/2753/React%2018%20Stricter%20Strict%20Mode.png?sign=ARQ8AVTh-NEaeJRypJlVokuUVhocPeaK8n7GRSDwqNw%3D%3A0)
|
||||
|
||||
代码:[Code Sandbox](https://codesandbox.io/p/sandbox/clever-cache-pm1oct?file=%2Fsrc%2FApp.tsx&selection=%5B%7B%22endColumn%22%3A20%2C%22endLineNumber%22%3A33%2C%22startColumn%22%3A20%2C%22startLineNumber%22%3A33%7D%5D)
|
||||
|
||||
红色是第一次渲染,绿色是第二次渲染。输出日志里淡些的是 React 18 在二次调用时输出的 log 的默认效果,在 React 17 中是被默认隐藏的。蓝色指向的是最终应用在界面上呈现的结果。
|
||||
|
||||
我在 V 站上也提出了我的[疑问](https://v2ex.com/t/913595),根据大佬们的回复,我总结了一下:
|
||||
|
||||
1. `useMemo(() => /* */, [])` 执行一此后,以新的严格模式的规则,进行了二次调用,第一次的值作废。
|
||||
2. `useEffect(() => /* */, [])`执行一此后,以新的严格模式的规则,调用了 `destructor` 后,进行了二次调用。
|
||||
|
||||
在第 2 点中,两次 useEffect 都是使用同一个值,是因为严格模式的二次调用按钩子分别执行两次,所以 useMemo 两次的调用都完毕后,得到的值再被 useEffect 执行两次。我调整了一下代码,将测试代码复制了一份在后面,可以看到 “useMemo” 和 “useMemo 2” 先执行了一次,又再执行了一次,然后再到 “useEffect“ 和 “useEffect 2":
|
||||
![加倍快乐](https://pan.ivanli.cc/api/v3/file/source/2754/React%2018%20Stricter%20Strict%20Mode%202.png?sign=iYz9KP9uMuccRCesjqoRPKejEoUOj4FZfnBPt8kCXnQ%3D%3A0)
|
||||
|
||||
## 结论
|
||||
|
||||
`useEffect` 应该独自管理副作用,要做到自己创建,自己销毁。
|
||||
|
||||
```tsx
|
||||
const camera = useMemo(/* .. */, []);
|
||||
const renderer = props; // from parent
|
||||
|
||||
const [controls, setControls] = useState<OrbitControls | null>(null);
|
||||
useEffect(() => {
|
||||
const controls = new OrbitControls(camera, renderer.domElement) // 自己的锅
|
||||
setControls(controls);
|
||||
return () => {
|
||||
controls.dispose(); // 自己背
|
||||
};
|
||||
}, [camera, renderer]);
|
||||
```
|
||||
|
||||
今天深究了一下这个问题,解决方案其实我也知道,但是之前的写法突然以我不理解的方式失效了,还是要较个劲,万一是 React 不规范呢?(狗头
|
35
data/blog/self-hosted-baas.md
Normal file
35
data/blog/self-hosted-baas.md
Normal file
@ -0,0 +1,35 @@
|
||||
---
|
||||
title: 自部署的 BaaS 服务对比
|
||||
date: '2023-01-24'
|
||||
tags: ['BaaS', 'Self-Hosted', 'appwrite', 'nhost', 'supabase']
|
||||
draft: false
|
||||
summary: supabase、nhost、appwrite 之间的对比,关注自部署方向。
|
||||
---
|
||||
|
||||
BaaS,后端即服务。
|
||||
最近关注 BaaS 自部署主要还是因为自己有一些简单的服务端开发需求,可能就一两个函数的事。
|
||||
如果专门去写一个后端,显得有些铺张了。而且创建一个项目是个麻烦事,我也不愿意从旧项目复制可能已经过时代码进来,所以就想到 FaaS。
|
||||
但是 FaaS 我搜罗了一圈,适合个人自部署的 FaaS 平台少之又少,最后找了个 Serverless 的函数项目 [Trusted CGI](https://trusted-cgi.reddec.net/)([Repo](https://github.com/reddec/trusted-cgi),Go Lang)。
|
||||
这个挺适合个人使用,但是无状态意味着我还得自己搞状态存储,还是不太符合我的需求,所以就看上了 BaaS。
|
||||
|
||||
## BaaS 简要介绍
|
||||
|
||||
目前 BaaS 大概是用户管理、授权、认证,加上数据库设计和存储、对象存储,再加上各类的 event hook 和 push,再加上 Serverless Functions 构成的。这个组件构成非常适合原型、Demo 以及轻量的应用开发。
|
||||
当然,如果后续有性能瓶颈,至少垂直扩展和 functions 层水平扩展是没有任何问题。至于持久层的水平扩展,也能像传统方案处理。
|
||||
|
||||
BaaS 最佳的应用场景就是各类 Apps 的服务端了。包括 Web Apps 在内,主要业务由 Apps 端处理的话,BaaS 就是绝佳的生产力工具。而且服务端的业务逻辑基本上都是写在 Serverless Functions 里,所以根本不需要考虑升级会暂停服务,因为它是无状态的,更新时是能做到零秒重载的。
|
||||
|
||||
更权威的定义可以看[这里](https://www.cloudflare.com/zh-cn/learning/serverless/glossary/backend-as-a-service-baas/)。
|
||||
|
||||
## 对比
|
||||
|
||||
适合生产的环境自然也就是各大平台自有的云 BaaS 平台了,在他们各自的云平台上,你可以享受到完整、轻松的开发体验。但是这是对于企业和专业用途的个人用户,而玩票性质的我就暂时用不上了,所以我的目标就是开源的、可自部署的 BaaS 服务。
|
||||
|
||||
我淘了好久,找到了三个不错的开源项目,分别是 supabase <img alt="Packagist Stars" class="inline" src="https://img.shields.io/packagist/stars/appwrite/appwrite?style=social"/>、appwrite <img alt="Packagist Stars" class="inline" src="https://img.shields.io/packagist/stars/nhost/nhost?style=social"/>、nhost <img alt="Packagist Stars" class="inline" src="https://img.shields.io/packagist/stars/appwrite/appwrite?style=social"/>。他们仨在 Github 上的 Stars 目前是从多到少的。
|
||||
接下来就以自部署的角度来对比下他们三个之间的差异。
|
||||
|
||||
TL; DR, 如果小项目多,推荐使用 appwrite;如果现阶段需要表的关联,建议使用 nhost;supabase 不适合自部署,他没有可以自部署的 serverless functions。
|
||||
|
||||
### Supabase
|
||||
|
||||
Github 上的星星老多了,可以说是目前最火的 BaaS 项目了。他对标的是 Firebase,以 Firebase 的开源版本自居。
|
Loading…
Reference in New Issue
Block a user