Skip to content

Commit 6c5d544

Browse files
author
zhangdong
committed
feat: 初始化
1 parent e1cfb0e commit 6c5d544

File tree

5 files changed

+189
-17
lines changed

5 files changed

+189
-17
lines changed

.vitepress/config.mts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ export default defineConfig({
137137
text: "state",
138138
link: "/frame/react/state",
139139
},
140+
{
141+
text: "状态管理",
142+
link: "/frame/react/manage",
143+
},
140144
],
141145
"/Web-Api/": [
142146
{

frame/react/imgs/a.jpg

196 KB
Loading

frame/react/manage.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
outline: deep
3+
layout: doc
4+
---
5+
## 声明式 UI 与命令式 UI 的比较

frame/react/start.md

Lines changed: 102 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,10 @@
22
outline: deep
33
layout: doc
44
---
5-
## react组件
6-
就是一个返回jsx的函数,在jsx中可以写类似html的东西
7-
jsx 看起来很像 htmlL,但它更严格一些,并且可以显示动态信息。要求所有标签明确的闭合
8-
```js
9-
function MyButton() {
10-
return (
11-
<button>I'm a button</button>
12-
);
13-
}
14-
```
15-
:::warning :warning:
16-
React 组件名称必须始终以大写字母开头,而 HTML 标签必须小写
17-
:::
5+
## jsx
6+
* 只能返回一个根节点
7+
* 标签必须闭合
8+
* 使用驼峰式命名法给大部分属性命名!除了aria-* 和 data-* 属性是以带 - 符号的 HTML 格式书写的
189

1910
:::tip DEEP DIVE
2011
:::details 为什么只能返回一个根节点
@@ -41,7 +32,44 @@ const MyComponent = (props) => {
4132
```
4233
这样是不行的
4334
:::
44-
## 展示数据
35+
## 组件
36+
就是一个返回jsx的函数,在jsx中可以写类似html的东西
37+
38+
jsx 看起来很像 htmlL,但它更严格一些,并且可以显示动态信息。
39+
```js
40+
function MyButton() {
41+
return (
42+
<button>I'm a button</button>
43+
);
44+
}
45+
```
46+
:::warning :warning:
47+
React 组件名称必须始终以大写字母开头,而 HTML 标签必须小写
48+
:::
49+
50+
组件可以渲染其他组件,但是 请不要嵌套他们的定义:
51+
```js
52+
export default function Gallery() {
53+
// 🔴 永远不要在组件中定义组件
54+
function Profile() {
55+
// ...
56+
}
57+
// ...
58+
}
59+
```
60+
上面这段代码 非常慢,并且会导致 bug 产生。因此,你应该在顶层定义每个组件:
61+
```js
62+
export default function Gallery() {
63+
// ...
64+
}
65+
66+
// ✅ 在顶层声明组件
67+
function Profile() {
68+
// ...
69+
}
70+
```
71+
当子组件需要使用父组件的数据时,你需要 通过 props 的形式进行传递,而不是嵌套定义。
72+
## 在 jsx 中通过大括号使用 js
4573
jsx里面可以放入html,如果需要重新跳回js,可以使用大括号包裹
4674
```js{3}
4775
return (
@@ -73,6 +101,28 @@ return (
73101
74102
* 作为JSX 标签内的文本`<h1>{name}'s To Do List</h1>`:可以,但`<{tag}>Gregorio Y. Zara's To Do List</{tag}>` 不会。
75103
* 作为紧跟在符号后面的属性=:`src={avatar}`将读取avatar变量,但`src="{avatar}"`会传递字符串"{avatar}"。
104+
## 使用 jsx 展开语法传递 props
105+
```js
106+
function Profile(props) {
107+
return (
108+
<div className="card">
109+
<Avatar {...props} />
110+
</div>
111+
);
112+
}
113+
```
114+
## 将 jsx 作为子组件传递(插槽)
115+
通过解构children
116+
```js
117+
function Card({ children }) {
118+
return (
119+
<div className="card">
120+
{children}
121+
</div>
122+
);
123+
}
124+
125+
```
76126
## 条件渲染
77127
```jsx
78128
<div>
@@ -89,6 +139,44 @@ return (
89139
{isLoggedIn && <AdminPanel />}
90140
</div>
91141
```
142+
## 将 jsx 赋值给变量
143+
```jsx{4-8}
144+
function Item({ name, isPacked }) {
145+
let itemContent = name;
146+
if (isPacked) {
147+
itemContent = (
148+
<del>
149+
{name + " ✔"}
150+
</del>
151+
);
152+
}
153+
return (
154+
<li className="item">
155+
{itemContent}
156+
</li>
157+
);
158+
}
159+
```
160+
## Fragment
161+
Fragment 语法的简写形式 <> </> 无法接受 key 值,所以你只能要么把生成的节点用一个 `<div>` 标签包裹起来,要么使用长一点但更明确的 `<Fragment>` 写法:
162+
```js
163+
import { Fragment } from 'react';
164+
165+
// ...
166+
167+
const listItems = people.map(person =>
168+
<Fragment key={person.id}>
169+
<h1>{person.name}</h1>
170+
<p>{person.bio}</p>
171+
</Fragment>
172+
);
173+
```
174+
这里的 Fragment 标签本身并不会出现在 DOM 上,这串代码最终会转换成 `<h1>、<p>、<h1>、<p>`…… 的列表。
175+
## 严格模式
176+
在严格模式下开发时,它将会调用每个组件函数两次
177+
178+
严格模式在生产环境下不生效,因此它不会降低应用程序的速度。如需引入严格模式,你可以用 `<React.StrictMode>` 包裹根组件
179+
92180
## 响应事件
93181
onClick={handleClick}末尾没有括号
94182
## 简单示例

frame/react/state.md

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,15 @@ useState 在调用时没有任何关于它引用的是哪个 state 变量的信
6969
Hooks ——以 use 开头的函数——只能在组件或自定义 Hook 的最顶层调用。 你不能在条件语句、循环语句或其他嵌套函数内调用 Hook。
7070
:::
7171

72-
再看这个例子
72+
再看这个例子,连续使用三次set
7373

7474
<iframe src="https://codesandbox.io/embed/g7f575?view=editor+%2B+preview"
7575
style="width:100%; height: 500px; border:0; border-radius: 4px; overflow:hidden;"
7676
title="一次性三次set"
7777
allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
7878
sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
7979
></iframe>
80+
8081
:::details :rocket: DEEP DIVE
8182

8283
设置 state 只会为下一次渲染变更 state 的值
@@ -98,8 +99,7 @@ Hooks ——以 use 开头的函数——只能在组件或自定义 Hook 的最
9899
尽管你调用了三次 setNumber(number + 1),但在 这次渲染的 事件处理函数中 number 会一直是 0,所以你会三次将 state 设置成 1。
99100
:::
100101

101-
再看这个
102-
102+
再看这个,使用定时器输出state
103103
<iframe src="https://codesandbox.io/embed/x9692v?view=editor+%2B+preview&module=%2Fsrc%2FApp.js"
104104
style="width:100%; height: 500px; border:0; border-radius: 4px; overflow:hidden;"
105105
title="随时间变化的state"
@@ -112,3 +112,78 @@ state 的值在渲染过程中永远不会改变
112112

113113
即使其事件处理程序的代码是异步的。在该渲染 中,即使在调用之后,onClick的值number仍然保持不变。当 React 通过调用您的组件“拍摄 UI 快照”时,其值已“固定”
114114
:::
115+
## 批处理
116+
`React会等到事件处理程序中的所有代码都运行完毕后才处理状态更新`
117+
118+
但是不会批处理多个有意的event
119+
120+
例如有多个点击事件,是不会进行批处理
121+
122+
test 点击按钮等待的数量加一,三秒后完成加一,等待减一,现在多次点击等待一会,等待变成-1
123+
<iframe src="https://codesandbox.io/embed/c8cxxs?view=editor+%2B+preview&module=%2Fsrc%2FApp.js&hidenavigation=1"
124+
style="width:100%; height: 500px; border:0; border-radius: 4px; overflow:hidden;"
125+
title="延时处理"
126+
allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
127+
sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
128+
></iframe>
129+
130+
:::details :thinking:
131+
132+
![alt text](./imgs/a.jpg)
133+
134+
改为传递更新函数,而不是将计数器设置为在点击期间确定的具体值
135+
136+
这可以确保你在增加或减少计数器时是根据其 `最新 的 state` 而不是`点击时的 state `来进行增减的。
137+
:::
138+
## useState也可以传人一个函数
139+
例如
140+
```js
141+
setNumber(n => n + 1)
142+
```
143+
这会根据队列中的前一个状态计算下一个状态
144+
145+
```js
146+
setNumber(n+1)
147+
setNumber(n+1)
148+
setNumber(n+1)
149+
150+
setNumber(n=>n+1)
151+
setNumber(n=>n+1)
152+
setNumber(n=>n+1)
153+
```
154+
155+
替换状态后更新状态
156+
157+
```js
158+
<button onClick={() => {
159+
setNumber(number + 5);
160+
setNumber(n => n + 1);
161+
}}></button>
162+
163+
```
164+
:::details :rocket:
165+
6
166+
167+
* setNumber(number + 5):number是0,所以setNumber(0 + 5)。React 将“替换为5”添加到其队列中。
168+
* setNumber(n => n + 1):n => n + 1是一个更新函数。React将该函数添加到其队列中。
169+
:::
170+
171+
## 将 state 视为只读的
172+
`把所有存放在 state 中的 JavaScript 对象都视为只读的`
173+
174+
如果state保存的是对象,不能直接对对象进行修改,而是拷贝一份进行修改
175+
176+
可以使用immer库简化
177+
178+
:::tip DEEP DIVE immer原理
179+
由 Immer 提供的 draft 是一种特殊类型的对象,被称为 Proxy,它会记录你用它所进行的操作。这就是你能够随心所欲地直接修改对象的原因所在!从原理上说,Immer 会弄清楚 draft 对象的哪些部分被改变了,并会依照你的修改创建出一个全新的对象。
180+
:::
181+
182+
:::tip :rocket: 为什么在 React 中不推荐直接修改 state?
183+
* 调试:如果你使用 console.log 并且不直接修改 state,你之前日志中的 state 的值就不会被新的 state 变化所影响。这样你就可以清楚地看到两次渲染之间 state 的值发生了什么变化
184+
* 优化:React 常见的 优化策略 依赖于如果之前的 props 或者 state 的值和下一次相同就跳过渲染。如果你从未直接修改 state ,那么你就可以很快看到 state 是否发生了变化。如果 prevObj === obj,那么你就可以肯定这个对象内部并没有发生改变。
185+
新功能:我们正在构建的 React 的新功能依赖于 state 被 像快照一样看待 的理念。如果你直接修改 state 的历史版本,可能会影响你使用这些新功能。
186+
* 需求变更:有些应用功能在不出现任何修改的情况下会更容易实现,比如实现撤销/恢复、展示修改历史,或是允许用户把表单重置成某个之前的值。这是因为你可以把 state 之前的拷贝保存到内存中,并适时对其进行再次使用。如果一开始就用了直接修改 state 的方式,那么后面要实现这样的功能就会变得非常困难。
187+
* 更简单的实现:React 并不依赖于 mutation ,所以你不需要对对象进行任何特殊操作。它不需要像很多“响应式”的解决方案一样去劫持对象的属性、总是用代理把对象包裹起来,或者在初始化时做其他工作。这也是为什么 React 允许你把任何对象存放在 state 中——不管对象有多大——而不会造成有任何额外的性能或正确性问题的原因。
188+
:::
189+

0 commit comments

Comments
 (0)