-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
216 lines (112 loc) · 752 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>ayozooZ</title>
<subtitle>ayozooZ的blog</subtitle>
<link href="https://ayozoo.github.io/atom.xml" rel="self"/>
<link href="https://ayozoo.github.io/"/>
<updated>2024-03-12T13:58:56.017Z</updated>
<id>https://ayozoo.github.io/</id>
<author>
<name>ayozooZ</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>什么是ssr</title>
<link href="https://ayozoo.github.io/2024/03/12/%20%E4%BB%80%E4%B9%88%E6%98%AFssr/"/>
<id>https://ayozoo.github.io/2024/03/12/%20%E4%BB%80%E4%B9%88%E6%98%AFssr/</id>
<published>2024-03-12T13:30:03.000Z</published>
<updated>2024-03-12T13:58:56.017Z</updated>
<content type="html"><![CDATA[<h1 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h1><p>服务器端渲染(Server-Side Rendering,SSR)指的是在服务器上完成网页渲染并将将其发送给客户端的过程。</p><h1 id="为什么需要SSR"><a href="#为什么需要SSR" class="headerlink" title="为什么需要SSR?"></a>为什么需要SSR?</h1><p>SSR 发送给客户端的是包含了完整内容的网页,这样用户可以先看到网页的内容,而不需要等待“网页加载>执行JS>加载数据>渲染到网页”,从而提升用户体验。另一方面,因为网页内已经包含了具体内容,对搜索引擎也更加友好。</p><h1 id="SSR的优势"><a href="#SSR的优势" class="headerlink" title="SSR的优势"></a>SSR的优势</h1><p>SSR 能改善用户体验。<br>与传统服务器语言的对比很久之前,我们用 PHP 之类的语言输出 HTML,但是我们并不称其为”服务器端渲染”,因为现在的SSR 有更多的优化:</p><ul><li>语言同构化:开发难度降低。</li><li>数据传递与状态管理:虽然还是 JSON,但框架尽量帮我们做好了。</li><li>渲染函数由边缘计算负责:更快的速度、</li><li>页面切换时不会重新加载-</li></ul><h1 id="与传统服务器语言的对比"><a href="#与传统服务器语言的对比" class="headerlink" title="与传统服务器语言的对比"></a>与传统服务器语言的对比</h1><p>很久之前,我们用PHP之类的语言输出HTML,但是我们并不称其为”服务器端渲染”,因为现在的服务器端渲染有更多的优:</p><ul><li>语言同构化:开发难度降低。</li><li>数据传递与状态管理:虽然还是 JSON,但框架尽量帮我们做好了。</li><li>渲染函数由边缘计算负责:更快的速度、</li><li>页面切换时不会重新加载。</li></ul><h1 id="SSR的一般构成"><a href="#SSR的一般构成" class="headerlink" title="SSR的一般构成"></a>SSR的一般构成</h1><p>当谈论服务器端渲染(SSR)时,一般构成包掊以下几个关键部分:</p><ul><li>服务器端应用程序:这是运行在服务器上的应用程序,负责接收客户端请求,执行渲染过程,并返回渲染后的页面给客户端。</li><li>路由:服务器端应用程序需要能够根据客户端请求的不同路径,调用对应的渲染逻辑和数据获取方法。</li><li>模板引擎:用于将页面模板和数据结合,生成最终的 HTML 内容。我们目前会使用的模版引擎都基于某款 MVVM 框架。</li><li>数据获取:在渲染页面之前,服务器端应用程序通常需要获取页面所需的数据,这可能涉及到从<br>数据库、API或其他来源获取数据。</li><li>状态管理:在 SSR 应用中,需要考虑如何管理客户端和服务器端的状态同步,以避免出现不一致的情况。</li><li>客户端交互:尽管整个页面的初始渲染是在服务器端完成的,但在客户端加载后仍可能需要进行交互,如使用 JavaScript 添加动态内容或处理用户操作。</li></ul><h1 id="Nuxt3-的SSR-组件"><a href="#Nuxt3-的SSR-组件" class="headerlink" title="Nuxt3 的SSR 组件"></a>Nuxt3 的SSR 组件</h1><p>对于大部分应用而言,<strong>服务器端渲染=加载数据+渲染HTML</strong>。所以理解 Nuxt3 里复杂加载数据的组件非常重要。</p><ul><li>与异步组件<suspense></suspense></li><li>和<code>useAsyncData</code> <code>useLazyAsyncData</code></li><li><code>useFetch</code>和<code>useLazyFetchi</code></li><li>用 <code>process.client</code> 和 <code><client-only></code>来处理仅限浏览器内部使用的功能</li><li>用 <code>process.server </code>来处理仅限服务器使用的功能5</li><li>https:/nuxt.com/docs/getting-started/data-fetching#serializing-data-from-api-routes</li></ul><h1 id="Nuxt3-的渲染规则与缓存处理"><a href="#Nuxt3-的渲染规则与缓存处理" class="headerlink" title="Nuxt3 的渲染规则与缓存处理"></a>Nuxt3 的渲染规则与缓存处理</h1><p>Nuxt3 提供三种不同的渲染模式:</p><ul><li>SSR:默认。即在服务器端渲染之后再发给客户端</li><li>ISR:部署后,渲染之后即保留缓存至下次渲染(渐进式渲染)</li><li>SWR:保留缓存,并在指定时间后校验缓存</li><li>prerender:部署时生成静态页面<br><a href="https://nuxt.com/docs/guide/concepts/rendering">https://nuxt.com/docs/guide/concepts/rendering</a>!</li></ul><h1 id="如何鉴别用户身份"><a href="#如何鉴别用户身份" class="headerlink" title="如何鉴别用户身份"></a>如何鉴别用户身份</h1><p>在 vue,或者说传统 SPA 里,所有请求都是<strong>后</strong>请求,即完成网页加载、JS 执行完毕后,再发起请求。这些请求,可以认为完全由开发者控制,即你知道什么时候该请求,然后发起请求。一般来说所有的请求都是<strong>数据交互</strong>类。</p><p>在 Nuxt 里,因为 SSR 的存在,<em>所以请求至少可以分成两类:页面渲染类,数据交互类。</em>前者会影响到 HTML 的内容。在网络环境里,存在大量缓存节点,假如跟用户相关的敏感数据渲染成HTML,缓存到 CDN 当中,会是非常大的安全问题。所以 Nuxt 在 SSR 及其内部发起请求时,不会携带 cookie;在用户主动发起的请求里,才会携带 cookie。官方文档 useRequestHeaders</p><p>如果要使用 localstorage 保存用户数据,则势必会存在两次渲染,请处理好loading 状态。</p>]]></content>
<summary type="html"><h1 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h1><p>服务器端渲染(Server-Side Rendering,SSR)指的是在服务器上完成网页渲染并将将其发送给客户端的过程。</p>
<h1 </summary>
<category term="SSR" scheme="https://ayozoo.github.io/categories/SSR/"/>
<category term="SSR" scheme="https://ayozoo.github.io/tags/SSR/"/>
</entry>
<entry>
<title>nuxt-尝鲜</title>
<link href="https://ayozoo.github.io/2024/03/12/%20nuxt-%E5%B0%9D%E9%B2%9C/"/>
<id>https://ayozoo.github.io/2024/03/12/%20nuxt-%E5%B0%9D%E9%B2%9C/</id>
<published>2024-03-12T13:15:33.000Z</published>
<updated>2024-03-12T13:37:46.213Z</updated>
<content type="html"><![CDATA[<h1 id="runtimeConfig与app-confg"><a href="#runtimeConfig与app-confg" class="headerlink" title="runtimeConfig与app.confg"></a>runtimeConfig与app.confg</h1><p>回看<code>app.config.ts</code>与<code>nuxt.config.ts</code>,俩者都是向应用程序的其余部分公开变量</p><p> runtimeConfig:使用环境变量构建后需要指定的私有或公共令牌。<br> app.config:在构建时确定的公共令牌、网站配置(如主题变体、标题和任何不敏感的项目配置)。</p><p> 以下有使用准则,请参考以下标准:</p><h2 id="特征比较"><a href="#特征比较" class="headerlink" title="特征比较"></a>特征比较</h2><table><thead><tr><th>特征</th><th>runtimeConfig</th><th>app.config</th></tr></thead><tbody><tr><td>客户端</td><td>水合</td><td>捆绑</td></tr><tr><td>环境变量</td><td>✅是的</td><td>不</td></tr><tr><td>反应性</td><td>✅是的</td><td>✅是的</td></tr><tr><td>类型支持</td><td>✅部分</td><td>✅是的</td></tr><tr><td>每个请求的配置</td><td>✅不</td><td>✅是的</td></tr><tr><td>热模块更换</td><td>✅不</td><td>✅是的</td></tr><tr><td>非原始 JS 类型</td><td>✅不</td><td>✅是的</td></tr></tbody></table><h1 id="样式"><a href="#样式" class="headerlink" title="样式"></a>样式</h1><h2 id="本地样式表"><a href="#本地样式表" class="headerlink" title="本地样式表"></a>本地样式表</h2><p> 本地样式表存在<code>assets/</code>目录</p><h2 id="组件内导入"><a href="#组件内导入" class="headerlink" title="组件内导入"></a>组件内导入</h2><p> 通过<code>@import</code>导入</p> <figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><script></span><br><span class="line">// Use a static import for server-side compatibility</span><br><span class="line">import '~/assets/css/first.css'</span><br><span class="line"></span><br><span class="line">// Caution: Dynamic imports are not server-side compatible</span><br><span class="line">import('~/assets/css/first.css')</span><br><span class="line"></script></span><br><span class="line"></span><br><span class="line"><style></span><br><span class="line">@import url("~/assets/css/second.css");</span><br><span class="line"></style></span><br></pre></td></tr></tbody></table></figure><h2 id="通过npm分发下载"><a href="#通过npm分发下载" class="headerlink" title="通过npm分发下载"></a>通过npm分发下载</h2><p>终端下载</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install animate.css</span><br></pre></td></tr></tbody></table></figure><p>页面、组件、布局引用</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><script></span><br><span class="line">import 'animate.css'</span><br><span class="line"></script></span><br><span class="line"></span><br><span class="line"><style></span><br><span class="line">@import url("animate.css");</span><br><span class="line"></style></span><br></pre></td></tr></tbody></table></figure><p>下载的包也可以在Nuxt配置中<code>CSS</code>属性中作为字符串引用</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title function_">defineNuxtConfig</span>({</span><br><span class="line"> <span class="attr">css</span>: [<span class="string">'animate.css'</span>]</span><br><span class="line">})</span><br></pre></td></tr></tbody></table></figure><h2 id="外部样式表"><a href="#外部样式表" class="headerlink" title="外部样式表"></a>外部样式表</h2><p>可以通过<code>Nuxt.config.ts</code>文件的head添加link元素,实例如下:</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title function_">defineNuxtConfig</span>({</span><br><span class="line"> <span class="attr">app</span>: {</span><br><span class="line"> <span class="attr">head</span>: {</span><br><span class="line"> <span class="attr">link</span>: [{ <span class="attr">rel</span>: <span class="string">'stylesheet'</span>, <span class="attr">href</span>: <span class="string">'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css'</span> }]</span><br><span class="line"> }</span><br><span class="line">}})</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h2 id="动态添加样式表"><a href="#动态添加样式表" class="headerlink" title="动态添加样式表"></a>动态添加样式表</h2><p>使用<code>useHead</code>可组合项进行动态设置head的值</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">useHead</span>({</span><br><span class="line"> <span class="attr">link</span>: [{ <span class="attr">rel</span>: <span class="string">'stylesheet'</span>, <span class="attr">href</span>: <span class="string">'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css'</span> }]</span><br><span class="line">})</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h2 id="使用Nitro插件修改渲染的头部"><a href="#使用Nitro插件修改渲染的头部" class="headerlink" title="使用Nitro插件修改渲染的头部"></a>使用Nitro插件修改渲染的头部</h2><p>这个属于更高级别的控制,可以使用钩子拦截渲染的html,以编程的方式修改头部。<br>创建插件如下:</p><figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title function_">defineNitroPlugin</span>(<span class="function">(<span class="params">nitro</span>) =></span> {</span><br><span class="line"> nitro.<span class="property">hooks</span>.<span class="title function_">hook</span>(<span class="string">'render:html'</span>, <span class="function">(<span class="params">html</span>) =></span> {</span><br><span class="line"> html.<span class="property">head</span>.<span class="title function_">push</span>(<span class="string">'<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">'</span>)</span><br><span class="line"> })</span><br><span class="line">})</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>外部样式表是阻止呈现的资源:必须在浏览器呈现页面之前加载和处理它们。包含不必要的大样式的网页需要更长的时间来呈现。</p><h2 id="使用预处理器"><a href="#使用预处理器" class="headerlink" title="使用预处理器"></a>使用预处理器</h2><ul><li>首先确保安装<code>sass</code>或者<code>less</code></li><li>知悉编写样式表的目录,通过预处理器的语法将原文件导入需要使用的地方<figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><style lang="scss"></span><br><span class="line"> @use "~/assets/scss/main.scss";</span><br><span class="line"></style></span><br></pre></td></tr></tbody></table></figure></li><li>或者在<code>nuxt.config.ts</code>中配置预处理器<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title function_">defineNuxtConfig</span>({</span><br><span class="line"><span class="attr">css</span>: [<span class="string">'~/assets/scss/main.scss'</span>]</span><br><span class="line">})</span><br></pre></td></tr></tbody></table></figure></li></ul><h2 id="单文件组件动态样式"><a href="#单文件组件动态样式" class="headerlink" title="单文件组件动态样式"></a>单文件组件动态样式</h2><ul><li>通过<code>ref</code>或者<code>reactive</code> 变量动态依赖设置</li><li>通过<code>computed</code>计算属性动态设置</li><li>通过三目运算以数组的形式设置</li><li>通过动态字符串</li></ul><h2 id="动态v-bind"><a href="#动态v-bind" class="headerlink" title="动态v-bind"></a>动态v-bind</h2><p><code>v-bind</code>函数在样式块中可以引用JavaScript变量和表达式。这种绑定是动态的,当依赖值变化时,样式也会更新。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><script setup lang="ts"></span><br><span class="line">const color = ref("red")</span><br><span class="line"></script></span><br><span class="line"></span><br><span class="line"><template></span><br><span class="line"> <div class="text">hello</div></span><br><span class="line"></template></span><br><span class="line"></span><br><span class="line"><style></span><br><span class="line">.text {</span><br><span class="line"> color: v-bind(color);</span><br><span class="line">}</span><br><span class="line"></style></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h2 id="作用域组件"><a href="#作用域组件" class="headerlink" title="作用域组件"></a>作用域组件</h2><p>通过在<code>style</code>标签声明<code>scoped</code>属性,声明后的样式将仅应用于当前组件。</p><h2 id="css模块"><a href="#css模块" class="headerlink" title="css模块"></a>css模块</h2><p>可以使用css模块替换为<code>module</code>属性。使用注入的变量访问。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><template></span><br><span class="line"> <p :class="$style.red">This should be red</p></span><br><span class="line"></template></span><br><span class="line"></span><br><span class="line"><style module></span><br><span class="line">.red {</span><br><span class="line"> color: red;</span><br><span class="line">}</span><br><span class="line"></style></span><br></pre></td></tr></tbody></table></figure><h2 id="预处理器"><a href="#预处理器" class="headerlink" title="预处理器"></a>预处理器</h2><p>SFC样式快支持预处理器语法,Vite内置了对 .scss、.sass、.less、.styl 和 .stylus 文件的支持,无需配置。通过下载他们,直接在SFC中使用lang属性提供</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><style lang="scss||sass||less||stylus||stylus"></span><br></pre></td></tr></tbody></table></figure><h1 id="路由"><a href="#路由" class="headerlink" title="路由"></a>路由</h1><p>Nuxt文件路由为<code>pages/</code>目录下的文件提供路由。<br>Nuxt核心功能是将页面路由映射到文件。每个Vue文件都位于<code>pages/</code>目录中,<strong>文件名</strong>将映射到URL。</p><h2 id="页面"><a href="#页面" class="headerlink" title="页面"></a>页面</h2><p>Nuxt路由基于vue-router,从<code>pages</code>目录,基于文件名。<br>文件系统通过使用命名约定来创建动态路由和嵌套路由,如:文件目录为<br>| pages/<br>—| about.vue<br>—| index.vue<br>—| posts.vue<br>—-| [id].vue<br>映射出来路由文件为:</p><figure class="highlight json"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"routes"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"path"</span><span class="punctuation">:</span> <span class="string">"/about"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"component"</span><span class="punctuation">:</span> <span class="string">"pages/about.vue"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"path"</span><span class="punctuation">:</span> <span class="string">"/"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"component"</span><span class="punctuation">:</span> <span class="string">"pages/index.vue"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"path"</span><span class="punctuation">:</span> <span class="string">"/posts/:id"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"component"</span><span class="punctuation">:</span> <span class="string">"pages/posts/[id].vue"</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure><h2 id="导航"><a href="#导航" class="headerlink" title="导航"></a>导航</h2><p>**<code><NuxtLink></code>**组件,类似于vue-router中<router-link :to="xxx"><router-link>,通过该组件链接之间页面,他呈现的是一个标签,属性设置为页面路由。<br><code><NuxtLink></code>进入客户端的视口,Nuxt会自动提前加载链接页面组件和页面,从而快速地呈现页面。</router-link></router-link></p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><template></span><br><span class="line"> <header></span><br><span class="line"> <nav></span><br><span class="line"> <ul></span><br><span class="line"> <li><NuxtLink to="/about">About</NuxtLink></li></span><br><span class="line"> <li><NuxtLink to="/posts/1">Post 1</NuxtLink></li></span><br><span class="line"> <li><NuxtLink to="/posts/2">Post 2</NuxtLink></li></span><br><span class="line"> </ul></span><br><span class="line"> </nav></span><br><span class="line"> </header></span><br><span class="line"></template></span><br></pre></td></tr></tbody></table></figure><h2 id="路由参数"><a href="#路由参数" class="headerlink" title="路由参数"></a>路由参数</h2><p>通过<code>useRoute()</code>api在Vue组件的块或者方法中使用,以便于访问当前路由的详细信息。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><script setup lang="ts"></span><br><span class="line">const route = useRoute()</span><br><span class="line"></span><br><span class="line">// When accessing /posts/1, route.params.id will be 1</span><br><span class="line">console.log(route.params.id)</span><br><span class="line"></script></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h2 id="路由中间件"><a href="#路由中间件" class="headerlink" title="路由中间件"></a>路由中间件</h2><p>Nuxt提供了可以自定义的路由中间件,<strong>用于导航到特定路由之前提取要运行的代码</strong>。</p><p>可以使用<code>middleware</code>属性在<code>nuxt.config.ts</code>中配置,或者在<code>pages/</code>目录下创建中间件文件,文件名必须为<code>middleware.js</code>或者<code>middleware.ts</code>,文件中必须导出一个函数,函数参数为<code>context</code>,返回一个Promise,当函数执行完成时,返回的Promise会作为路由执行结果。</p><p>路由中间件有三种:</p><ul><li>匿名(或内联)路由中间件,直接在使用它们的页面中定义。</li><li>命名路由中间件,放置在middleware/目录,当在页面上使用时,将通过异步导入自动加载。(注意:路由中间件名称被规范化为 kebab-case,因此变为 。someMiddlewaresome-middleware</li><li>全局路由中间件,放置在middleware/目录(带后缀),并将在每次路线更改时自动运行。.global</li></ul><h2 id="路由验证"><a href="#路由验证" class="headerlink" title="路由验证"></a>路由验证</h2><p>Nuxt通过<code>validate</code>属性在路由中配置,**通过<code>**definePageMeta**()</code>**。<br>当路由匹配时,验证函数将被调用,如果返回false,路由将不会被激活,路由将被重定向到其他页面。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><script setup lang="ts"></span><br><span class="line">definePageMeta({</span><br><span class="line"> validate: async (route) => {</span><br><span class="line"> // Check if the id is made up of digits</span><br><span class="line"> return typeof route.params.id === 'string' && /^\d+$/.test(route.params.id)</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></script></span><br></pre></td></tr></tbody></table></figure><h1 id="SEO和Meta"><a href="#SEO和Meta" class="headerlink" title="SEO和Meta"></a>SEO和Meta</h1><h2 id="默认设置"><a href="#默认设置" class="headerlink" title="默认设置"></a>默认设置</h2><p>使用强大的头部配置、可组合项和组件可以改善Nuxt应用的SEO。<br>Nuxt提供了一些默认的SEO配置,可以在<code>nuxt.config.ts</code>中配置。</p><figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title function_">defineNuxtConfig</span>({</span><br><span class="line"> <span class="attr">app</span>: {</span><br><span class="line"> <span class="attr">head</span>: {</span><br><span class="line"> <span class="attr">charset</span>: <span class="string">'utf-8'</span>,</span><br><span class="line"> <span class="attr">viewport</span>: <span class="string">'width=device-width, initial-scale=1'</span>,</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></tbody></table></figure><p><code>app.head</code>可以通过<code>nuxt.config.ts</code>配置文件中配置,允许我们为整个应用程序自定义头部。</p><h2 id="useHead"><a href="#useHead" class="headerlink" title="useHead"></a>useHead</h2><p><code>useHead()</code>可组合功能可以在组件中使用,我们可以方便的通过编程和响应式方法管理应用头部标签。跟其他可组合项一样,只能与组件和生命周期钩子一起使用。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><script setup lang="ts"></span><br><span class="line">useHead({</span><br><span class="line"> title: 'My App',</span><br><span class="line"> meta: [</span><br><span class="line"> { name: 'description', content: 'My amazing site.' }</span><br><span class="line"> ],</span><br><span class="line"> bodyAttrs: {</span><br><span class="line"> class: 'test'</span><br><span class="line"> },</span><br><span class="line"> script: [ { innerHTML: 'console.log(\'Hello world\')' } ]</span><br><span class="line">})</span><br><span class="line"></script></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h2 id="useSeoMeta"><a href="#useSeoMeta" class="headerlink" title="useSeoMeta"></a>useSeoMeta</h2><p><code>useSeoMeta()</code>是<code>useHead()</code>的别名,它允许我们使用与<code>useHead()</code>相同的配置,但是它将自动处理<code>title</code>和<code>meta</code>属性,这使得配置SEO更简单。<br>它的主要作用是可以将站点的SEO元标记标为具备ts支持的平面对象,便于更好的配置。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><script setup lang="ts"></span><br><span class="line">useSeoMeta({</span><br><span class="line"> title: 'My Amazing Site',</span><br><span class="line"> ogTitle: 'My Amazing Site',</span><br><span class="line"> description: 'This is my amazing site, let me tell you all about it.',</span><br><span class="line"> ogDescription: 'This is my amazing site, let me tell you all about it.',</span><br><span class="line"> ogImage: 'https://example.com/image.png',</span><br><span class="line"> twitterCard: 'summary_large_image',</span><br><span class="line">})</span><br><span class="line"></script></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h2 id="组件中"><a href="#组件中" class="headerlink" title="组件中"></a>组件中</h2><p>Nuxt提供 <code><Title>, <Base>, <NoScript>, <Style>, <Meta>, <Link>, <Body>, <Html> 和<Head></code>,便于直接于组件间模板元数据交互。</p><p><strong>注意,这些组件名称在模板中必须将他们大写。</strong>。</p>并且可以接受嵌套的元标记,但这不会影响哪里嵌套的元标记在最终的 HTML 中呈现。<figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><script setup lang="ts"></span><br><span class="line">const title = ref('Hello World')</span><br><span class="line"></script></span><br><span class="line"></span><br><span class="line"><template></span><br><span class="line"> <div></span><br><span class="line"> <Head></span><br><span class="line"> <Title>{{ title }}</Title></span><br><span class="line"> <Meta name="description" :content="title" /></span><br><span class="line"> <Style type="text/css" children="body { background-color: green; }" ></Style></span><br><span class="line"> </Head></span><br><span class="line"></span><br><span class="line"> <h1>{{ title }}</h1></span><br><span class="line"> </div></span><br><span class="line"></template>```</span><br><span class="line"></span><br><span class="line">## 用于`useHead()`,`app.head`和组件传值类型</span><br><span class="line">```ts</span><br><span class="line">interface MetaObject {</span><br><span class="line"> title?: string</span><br><span class="line"> titleTemplate?: string | ((title?: string) => string)</span><br><span class="line"> templateParams?: Record<string, string | Record<string, string>></span><br><span class="line"> base?: Base</span><br><span class="line"> link?: Link[]</span><br><span class="line"> meta?: Meta[]</span><br><span class="line"> style?: Style[]</span><br><span class="line"> script?: Script[]</span><br><span class="line"> noscript?: Noscript[];</span><br><span class="line"> htmlAttrs?: HtmlAttributes;</span><br><span class="line"> bodyAttrs?: BodyAttributes;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h2 id="特征"><a href="#特征" class="headerlink" title="特征"></a>特征</h2><h3 id="反应"><a href="#反应" class="headerlink" title="反应"></a>反应</h3><p>可以通过计算值或响应式变量或对象来动态更新元标记,所有的属性都支持反应式。<br>建议使用<code>getter()</code>而不是<code>computed()</code>,因为<code>computed()</code>将创建一个新对象,这将导致不必要的重新渲染。<br>以下是三种更改元标记的三种写法。</p><ul><li>使用<code>useHead()</code><br><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><script setup lang="ts"></span><br><span class="line">const description = ref('My amazing site.')</span><br><span class="line">useHead({meta: [{ name: 'description', content: description }], })</span><br><span class="line"></script> ```</span><br><span class="line">+ 使用SeoMeta</span><br><span class="line"> ```vue</span><br><span class="line"><script setup lang="ts"></span><br><span class="line">const description = ref('My amazing site.')</span><br><span class="line">useSeoMeta({</span><br><span class="line"> description</span><br><span class="line">})</span><br><span class="line"></script> ```</span><br><span class="line">+ 组件</span><br><span class="line"> ```vue</span><br><span class="line"><script setup lang="ts"></span><br><span class="line">const description = ref('My amazing site.')</span><br><span class="line"></script></span><br><span class="line"></span><br><span class="line"><template></span><br><span class="line"> <div></span><br><span class="line"> <Meta name="description" :content="description" /></span><br><span class="line"> </div></span><br><span class="line"></template>```</span><br><span class="line"></span><br><span class="line">### 标题模板</span><br><span class="line">可以通过`titleTemplate`~属性自定义动态标题模板,该属性可以是字符串或函数。</span><br><span class="line">这样可以将网站的名称添加到每个页面的标题中。</span><br><span class="line"></span><br><span class="line">如果需要使用某个函数完全控制标题,应该在`nuxt.config.ts`中进行设置。</span><br><span class="line">```vue</span><br><span class="line"><script setup lang="ts"></span><br><span class="line">useHead({</span><br><span class="line"> title:'sdf'</span><br><span class="line"> titleTemplate: (titleChunk) => {</span><br><span class="line"> return titleChunk ? `${titleChunk} - Site Title` : 'Site Title';</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></script></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure></li></ul><h3 id="Body标签"><a href="#Body标签" class="headerlink" title="Body标签"></a>Body标签</h3><p>可以使用标签的选项将他们附加到标签的末尾</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><script setup lang=<span class="string">"ts"</span>></span><br><span class="line"><span class="title function_">useHead</span>({</span><br><span class="line"> <span class="attr">script</span>: [</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">src</span>: <span class="string">'https://third-party-script.com'</span>,</span><br><span class="line"> <span class="comment">// valid options are: 'head' | 'bodyClose' | 'bodyOpen'</span></span><br><span class="line"> <span class="attr">tagPosition</span>: <span class="string">'bodyClose'</span> <span class="comment">// 标签闭合的末尾</span></span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line">})</span><br><span class="line"></script></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h2 id="动态标题"><a href="#动态标题" class="headerlink" title="动态标题"></a>动态标题</h2><p>以下示例,设置带有占位符的字符串,这样就可以灵活的为Nuxt应用程序设置每个路由动态设置页面标题:<code>titleTemplate %s || function</code></p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"> <!-- 字符串匹配 --></span><br><span class="line"><script setup lang="ts"></span><br><span class="line">useHead({</span><br><span class="line"> // as a string,</span><br><span class="line"> // where `%s` is replaced with the title</span><br><span class="line"> titleTemplate: '%s - Site Title',</span><br><span class="line">})</span><br><span class="line"></script></span><br><span class="line"></span><br><span class="line"><!-- 函数匹配 --></span><br><span class="line"><script setup lang="ts"></span><br><span class="line">useHead({</span><br><span class="line"> // or as a function</span><br><span class="line"> titleTemplate: (productCategory) => {</span><br><span class="line"> return productCategory</span><br><span class="line"> ? `${productCategory} - Site Title`</span><br><span class="line"> : 'Site Title'</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></script></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>nuxt.config也用于设置页面标题的替代方法,但是不允许页面标题是是动态的。因此,一般建议在文件中使用添加一个动态标题,然后将其应用月Nuxt应用程序的所有路由。</p><h2 id="外部CSS"><a href="#外部CSS" class="headerlink" title="外部CSS"></a>外部CSS</h2><p>以下是可以使用<code>useHead</code>和<code>link</code>可组合或使用组件</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><!-- 使用头 --></span><br><span class="line"><script setup lang="ts"></span><br><span class="line">useHead({</span><br><span class="line"> link: [</span><br><span class="line"> {</span><br><span class="line"> rel: 'preconnect',</span><br><span class="line"> href: 'https://fonts.googleapis.com'</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> rel: 'stylesheet',</span><br><span class="line"> href: 'https://fonts.googleapis.com/css2?family=Roboto&display=swap',</span><br><span class="line"> crossorigin: ''</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line">})</span><br><span class="line"></script></span><br><span class="line"></span><br><span class="line"><!-- 在组件中 --></span><br><span class="line"><template></span><br><span class="line"> <div></span><br><span class="line"> <Link rel="preconnect" href="https://fonts.googleapis.com" /></span><br><span class="line"> <Link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" crossorigin="" /></span><br><span class="line"> </div></span><br><span class="line"></template></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h1 id="过渡"><a href="#过渡" class="headerlink" title="过渡"></a>过渡</h1><h2 id="页面过渡"><a href="#页面过渡" class="headerlink" title="页面过渡"></a>页面过渡</h2><ul><li>全部生效<br>可以在Nuxt.config.ts中配置页面过渡,这个设置会将过渡应用于所有页面。如下所示:<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title function_">defineNuxtConfig</span>({</span><br><span class="line"> <span class="attr">app</span>: {</span><br><span class="line"> <span class="attr">pageTransition</span>: { <span class="attr">name</span>: <span class="string">'page'</span>, <span class="attr">mode</span>: <span class="string">'out-in'</span> }</span><br><span class="line"> },</span><br><span class="line">})</span><br></pre></td></tr></tbody></table></figure></li><li>页面之间<br>只需要在页面之间添加过渡,则在<code>app.vue</code>设置过渡效果</li><li>页面设置不同的<br>将需要设置的页面通过<code>definePageMeta</code>键入<code>pageTransition</code>属性,例如:<figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><script setup lang="ts"></span><br><span class="line">definePageMeta({</span><br><span class="line"> pageTransition: {</span><br><span class="line"> name: 'rotate'</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></script>```</span><br><span class="line">## 布局过渡</span><br><span class="line">通过`defingNuxtConfig`配置启用`layout`布局过渡</span><br><span class="line">```js</span><br><span class="line">export default defineNuxtConfig({</span><br><span class="line"> app: {</span><br><span class="line"> layoutTransition: { name: 'layout', mode: 'out-in' }</span><br><span class="line"> },</span><br><span class="line">})</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure>也可以通过<code>definePageMeta</code>属性在页面中设置<code>layoutTransition</code>布局过渡<figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><script setup lang="ts"></span><br><span class="line">definePageMeta({</span><br><span class="line"> layout: 'orange',</span><br><span class="line"> layoutTransition: {</span><br><span class="line"> name: 'slide-in'</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></script></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure></li></ul><h2 id="全局设置"><a href="#全局设置" class="headerlink" title="全局设置"></a>全局设置</h2><p>可以使用<code>nuxt.confg</code>全局自定义默认这些过渡名称<br>密钥都接受<code>pageTransition</code>、<code>layoutTransition</code>和<code>transitionProps</code>可作为序列化值,其中都可以传递自定义CSS转换的、和其他有效的过渡道具。</p><figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title function_">defineNuxtConfig</span>({</span><br><span class="line"> <span class="attr">app</span>: {</span><br><span class="line"> <span class="attr">pageTransition</span>: {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'fade'</span>,</span><br><span class="line"> <span class="attr">mode</span>: <span class="string">'out-in'</span> <span class="comment">// default</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">layoutTransition</span>: {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'slide'</span>,</span><br><span class="line"> <span class="attr">mode</span>: <span class="string">'out-in'</span> <span class="comment">// default</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>覆盖全局过渡属性,为单个nuxt页面设置不同的过渡属性,可以使用<code>definePageMeta</code></p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><script setup lang="ts"></span><br><span class="line">definePageMeta({</span><br><span class="line"> pageTransition: {</span><br><span class="line"> name: 'bounce',</span><br><span class="line"> mode: 'out-in' // default</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></script></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h2 id="禁用过渡"><a href="#禁用过渡" class="headerlink" title="禁用过渡"></a>禁用过渡</h2><p><code>pageTransition</code>和<code>layoutTransition</code>属性可以设置为<code>false</code>,以禁用页面和布局过渡。<br>单个页面中</p><figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><script setup lang=<span class="string">"ts"</span>></span><br><span class="line"><span class="title function_">definePageMeta</span>({</span><br><span class="line"> <span class="attr">pageTransition</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">layoutTransition</span>: <span class="literal">false</span></span><br><span class="line">})</span><br><span class="line"></script></span><br></pre></td></tr></tbody></table></figure><p>全局设置</p><figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title function_">defineNuxtConfig</span>({</span><br><span class="line"> <span class="attr">app</span>: {</span><br><span class="line"> <span class="attr">pageTransition</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">layoutTransition</span>: <span class="literal">false</span></span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h2 id="通过JavaScript钩子精准控制过渡"><a href="#通过JavaScript钩子精准控制过渡" class="headerlink" title="通过JavaScript钩子精准控制过渡"></a>通过JavaScript钩子精准控制过渡</h2><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><script setup lang="ts"></span><br><span class="line">definePageMeta({</span><br><span class="line"> pageTransition: {</span><br><span class="line"> name: 'custom-flip',</span><br><span class="line"> mode: 'out-in',</span><br><span class="line"> onBeforeEnter: (el) => {</span><br><span class="line"> console.log('Before enter...')</span><br><span class="line"> },</span><br><span class="line"> onEnter: (el, done) => {},</span><br><span class="line"> onAfterEnter: (el) => {}</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></script></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h2 id="动态过渡"><a href="#动态过渡" class="headerlink" title="动态过渡"></a>动态过渡</h2><p>如果需要通过条件控制过渡,可以使用内联中间件分配不同的过渡名。<code>to.meta.pageTransition</code></p><figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><script setup lang=<span class="string">"ts"</span>></span><br><span class="line"><span class="title function_">definePageMeta</span>({</span><br><span class="line"> <span class="attr">pageTransition</span>: {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'slide-right'</span>,</span><br><span class="line"> <span class="attr">mode</span>: <span class="string">'out-in'</span></span><br><span class="line"> },</span><br><span class="line"><span class="comment">// 中间件</span></span><br><span class="line"> middleware (to, <span class="keyword">from</span>) {</span><br><span class="line"> <span class="keyword">if</span> (to.<span class="property">meta</span>.<span class="property">pageTransition</span> && <span class="keyword">typeof</span> to.<span class="property">meta</span>.<span class="property">pageTransition</span> !== <span class="string">'boolean'</span>)</span><br><span class="line"> to.<span class="property">meta</span>.<span class="property">pageTransition</span>.<span class="property">name</span> = +to.<span class="property">params</span>.<span class="property">id</span> > +<span class="keyword">from</span>.<span class="property">params</span>.<span class="property">id</span> ? <span class="string">'slide-left'</span> : <span class="string">'slide-right'</span></span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></script></span><br><span class="line"></span><br><span class="line"><span class="language-xml"><span class="tag"><<span class="name">template</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">h1</span>></span>#{{ $route.params.id }}<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"><span class="language-xml"><span class="tag"></<span class="name">template</span>></span></span></span><br><span class="line"></span><br><span class="line"><span class="language-xml"><span class="tag"><<span class="name">style</span>></span><span class="language-css"></span></span></span><br><span class="line"><span class="language-css"><span class="language-xml"><span class="selector-class">.slide-left-enter-active</span>,</span></span></span><br><span class="line"><span class="language-css"><span class="language-xml"><span class="selector-class">.slide-left-leave-active</span>,</span></span></span><br><span class="line"><span class="language-css"><span class="language-xml"><span class="selector-class">.slide-right-enter-active</span>,</span></span></span><br><span class="line"><span class="language-css"><span class="language-xml"><span class="selector-class">.slide-right-leave-active</span> {</span></span></span><br><span class="line"><span class="language-css"><span class="language-xml"> <span class="attribute">transition</span>: all <span class="number">0.2s</span>;</span></span></span><br><span class="line"><span class="language-css"><span class="language-xml">}</span></span></span><br><span class="line"><span class="language-css"><span class="language-xml"><span class="selector-class">.slide-left-enter-from</span> {</span></span></span><br><span class="line"><span class="language-css"><span class="language-xml"> <span class="attribute">opacity</span>: <span class="number">0</span>;</span></span></span><br><span class="line"><span class="language-css"><span class="language-xml"> <span class="attribute">transform</span>: <span class="built_in">translate</span>(<span class="number">50px</span>, <span class="number">0</span>);</span></span></span><br><span class="line"><span class="language-css"><span class="language-xml">}</span></span></span><br><span class="line"><span class="language-css"><span class="language-xml"><span class="selector-class">.slide-left-leave-to</span> {</span></span></span><br><span class="line"><span class="language-css"><span class="language-xml"> <span class="attribute">opacity</span>: <span class="number">0</span>;</span></span></span><br><span class="line"><span class="language-css"><span class="language-xml"> <span class="attribute">transform</span>: <span class="built_in">translate</span>(-<span class="number">50px</span>, <span class="number">0</span>);</span></span></span><br><span class="line"><span class="language-css"><span class="language-xml">}</span></span></span><br><span class="line"><span class="language-css"><span class="language-xml"><span class="selector-class">.slide-right-enter-from</span> {</span></span></span><br><span class="line"><span class="language-css"><span class="language-xml"> <span class="attribute">opacity</span>: <span class="number">0</span>;</span></span></span><br><span class="line"><span class="language-css"><span class="language-xml"> <span class="attribute">transform</span>: <span class="built_in">translate</span>(-<span class="number">50px</span>, <span class="number">0</span>);</span></span></span><br><span class="line"><span class="language-css"><span class="language-xml">}</span></span></span><br><span class="line"><span class="language-css"><span class="language-xml"><span class="selector-class">.slide-right-leave-to</span> {</span></span></span><br><span class="line"><span class="language-css"><span class="language-xml"> <span class="attribute">opacity</span>: <span class="number">0</span>;</span></span></span><br><span class="line"><span class="language-css"><span class="language-xml"> <span class="attribute">transform</span>: <span class="built_in">translate</span>(<span class="number">50px</span>, <span class="number">0</span>);</span></span></span><br><span class="line"><span class="language-css"><span class="language-xml">}</span></span></span><br><span class="line"><span class="language-css"><span class="language-xml"></span><span class="tag"></<span class="name">style</span>></span></span></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h2 id="使用NuxtPage过渡"><a href="#使用NuxtPage过渡" class="headerlink" title="使用NuxtPage过渡"></a>使用NuxtPage过渡</h2><p>在<code>app.vue</code>中使用时,<code>transitionProps</code>可以作为组件props传递以激活全局转换</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><template></span><br><span class="line"> <div></span><br><span class="line"> <NuxtLayout></span><br><span class="line"> <NuxtPage :transition="{</span><br><span class="line"> name: 'bounce',</span><br><span class="line"> mode: 'out-in'</span><br><span class="line"> }" /></span><br><span class="line"> </NuxtLayout></span><br><span class="line"> </div></span><br><span class="line"></template></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h1 id="数据获取"><a href="#数据获取" class="headerlink" title="数据获取"></a>数据获取</h1><p>Nuxt提供了可组合项来处理应用程序中的数据获取。<br>Nuxt提供俩个组合项:</p><ul><li><code>useFetch</code>:组件设置函数中处理数据获取的最直接方法。</li><li><code>useAsyncData</code>:结合使用,提供了更细颗粒度的控制<br>Nuxt提供内置库:</li><li><code>$fetch</code>:非常适合根据用户交互发出网络请求。</li></ul><h2 id="useFetch"><a href="#useFetch" class="headerlink" title="useFetch"></a>useFetch</h2><p><code>useFetch</code>该组合项是执行数据提取的最直接方法。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><script setup lang="ts"></span><br><span class="line">const { data: count } = await useFetch('/api/count')</span><br><span class="line"></script></span><br><span class="line"></span><br><span class="line"><template></span><br><span class="line"> <p>Page visits: {{ count }}</p></span><br><span class="line"></template></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h2 id="fetch"><a href="#fetch" class="headerlink" title="$fetch"></a>$fetch</h2><p>Nuxt内置了<code>$fetch</code>包含该库,用于页面数据异步加载时候获取数据。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><script setup lang="ts"></span><br><span class="line">async function addTodo() {</span><br><span class="line"> const todo = await $fetch('/api/todos', {</span><br><span class="line"> method: 'POST',</span><br><span class="line"> body: {</span><br><span class="line"> // My todo data</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line">}</span><br><span class="line"></script></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h2 id="useAsyncData"><a href="#useAsyncData" class="headerlink" title="useAsyncData"></a>useAsyncData</h2><p><code>useAsyncData</code>是<code>useFetch</code>的更细颗粒度的控制。负责包装异步逻辑,并解析后返回结果。</p><p>当CMS或第三方提供自己的API时,可以使用<code>useAsyncData</code>。例如:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><script setup lang="ts"></span><br><span class="line">const { data, error } = await useAsyncData('users', () => myGetFunction('users'))</span><br><span class="line"></span><br><span class="line">// This is also possible:</span><br><span class="line">const { data, error } = await useAsyncData(() => myGetFunction('users'))</span><br><span class="line"></script></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>第一个参数<code>useAsyncData</code>是<code>key</code>,用于缓存数据。这个key可以通过直接传递查询函数来忽略,key会自动生成。因为自动生成键只考虑调用的文件和行,为了避免产生不必要的行为因此,可以传递一个自定义的key。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><script setup lang="ts"></span><br><span class="line">const { id } = useRoute().params</span><br><span class="line"></span><br><span class="line">const { data, error } = await useAsyncData(`user:${id}`, () => {</span><br><span class="line"> return myGetFunction('users', { id })</span><br><span class="line">})</span><br><span class="line"></script></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>可组合项是个可以等待多个完成,然后检索每个结果的好方法。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><script setup lang="ts"></span><br><span class="line">const { data: discounts, pending } = await useAsyncData('cart-discount', async () => {</span><br><span class="line"> const [coupons, offers] = await Promise.all([</span><br><span class="line"> $fetch('/cart/coupons'),</span><br><span class="line"> $fetch('/cart/offers')</span><br><span class="line"> ])</span><br><span class="line"></span><br><span class="line"> return { coupons, offers }</span><br><span class="line">})</span><br><span class="line">// discounts.value.coupons</span><br><span class="line">// discounts.value.offers</span><br><span class="line"></script></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h2 id="返回值"><a href="#返回值" class="headerlink" title="返回值"></a>返回值</h2><p><code>useFetch</code>具有下面列出相同的返回值。</p><ul><li><code>data</code>:传入的异步函数结果</li><li><code>peding</code>:布尔值,指示这个异步函数是否执行完毕</li><li><code>refresh/execute</code>:可用于刷新函数返回的数据的函数</li><li><code>error</code>:数据获取失败返回的错误信息</li><li><code>status</code>:返回数据请求状态的字符串</li></ul>]]></content>
<summary type="html"><h1 id="runtimeConfig与app-confg"><a href="#runtimeConfig与app-confg" class="headerlink" title="runtimeConfig与app.confg"></a>runtimeConfig与app</summary>
<category term="SSR" scheme="https://ayozoo.github.io/categories/SSR/"/>
<category term="SSR" scheme="https://ayozoo.github.io/tags/SSR/"/>
</entry>
<entry>
<title>React-状态管理</title>
<link href="https://ayozoo.github.io/2023/11/20/%20React-%E7%8A%B6%E6%80%81%E7%AE%A1%E7%90%86/"/>
<id>https://ayozoo.github.io/2023/11/20/%20React-%E7%8A%B6%E6%80%81%E7%AE%A1%E7%90%86/</id>
<published>2023-11-20T15:03:43.000Z</published>
<updated>2023-11-21T15:09:00.757Z</updated>
<content type="html"><![CDATA[<h1 id="react之状态管理"><a href="#react之状态管理" class="headerlink" title="react之状态管理"></a>react之状态管理</h1><p>随着应用的不断加大,我们应该更有意识的的去关注我们的应用状态如何去组织,以及数据如何在组件中流动。我们应该去避免冗余或重复的状态,这样可以避免一些缺陷的产生。这时候,如何组织好状态,如何保持状态更新逻辑的可维护性,以及如何跨组件共享状态就变得尤为重要。</p><h2 id="使用状态响应输入"><a href="#使用状态响应输入" class="headerlink" title="使用状态响应输入"></a>使用状态响应输入</h2><p>在使用React时,我们不需要直接代码层面去修改UI。我们可以直接通过组件的不同状态去展现的UI,然后根据用户输入触发状态修改。</p><h3 id="声明式UI与命令式UI的比较"><a href="#声明式UI与命令式UI的比较" class="headerlink" title="声明式UI与命令式UI的比较"></a>声明式UI与命令式UI的比较</h3><p>当设计UI交互时,会思考UI如何根据用户的操作而<strong>响应变化</strong>。想象一个允许用户提交一个答案的表单:</p><ul><li>当表单输入的时候,“提交”按钮会变成<strong>可用状态</strong></li><li>点击“提交”后,表单和提交按钮都会随之变成<strong>不可用状态</strong></li><li>如果网络请求成功,表单会随之<strong>隐藏</strong>,同时会出现“提交成功”提示</li><li>如果网络请求失败,会出现错误提示信息,表单又变为可用状态</li></ul><p><strong>命令式编程</strong>中,我们需要根据具体的场景去设计如何实现交互。我们必须根据可能会发生的事情去写一些明确的命令去操作UI。也就是我们需要“命令”每个元素(操作dom),告诉计算机应该<strong>如何</strong>去更新UI的编程方式被称为<strong>命令式编程</strong>。</p><p>对于独立系统来说,命令式控制用户界面的效果也不错,但是如果要实现更为复杂的系统时,代码的组织就会指数级难度增长。</p><p>而React就是为了解决这样的问题而诞生。</p><p>在React中,不比直接去操作UI(不需要去直接操作dom)。相反,我们<strong>只需要声明我们想要显示的内容。</strong>React就会通过计算如何去更新UI。</p><h3 id="声明式考虑UI"><a href="#声明式考虑UI" class="headerlink" title="声明式考虑UI"></a>声明式考虑UI</h3><p>通过上面的思考方式,我们来看看React时如何去实现这个UI。</p><ol><li><strong>定位</strong>组件中不同的视图状态</li><li><strong>确定</strong>是什么触发这些<code>state</code>的改变</li><li><strong>表示</strong>内存中的<code>state</code>(需要使用<code>useState</code>)</li><li><strong>删除</strong>任何不必要的<code>state</code>变量</li><li><strong>连接</strong>事件处理函数去设置<code>state</code></li></ol><h4 id="步骤1-定位组件中不同的视图状态"><a href="#步骤1-定位组件中不同的视图状态" class="headerlink" title="步骤1:定位组件中不同的视图状态"></a>步骤1:定位组件中不同的视图状态</h4><p>在React中,不同的可视化UI界面中用户所有看到的都是不同的“状态”。</p><ul><li><strong>无数据</strong>:表单有一个不可用状态的“提交”按钮。</li><li><strong>输入中</strong>:表单有一个可用状态的“提交”按钮。</li><li><strong>提交中</strong>:表单完全处于不可用状态,加载动画出现。</li><li><strong>成功时</strong>:显示“成功”的消息而非表单。</li><li><strong>错误时</strong>:与输入状态类似,但会多错误的消息。</li></ul><h4 id="步骤2-确定是什么触发了这些状态的改变"><a href="#步骤2-确定是什么触发了这些状态的改变" class="headerlink" title="步骤2:确定是什么触发了这些状态的改变"></a>步骤2:确定是什么触发了这些状态的改变</h4><p>触发<code>state</code>的更新来响应俩种输入:</p><ul><li><strong>人为</strong>输入。比如点击按钮、在表单中输入内容,或导航到链接。</li><li><strong>计算机</strong>输入。比如网络请求得到反馈、定时器被触发,或加载一张图片。</li></ul><p>以上两种情况中,<strong>你必须设置 <a href="https://react.docschina.org/learn/state-a-components-memory#anatomy-of-usestate">state 变量</a> 去更新 UI</strong>。对于正在开发中的表单来说,你需要改变 state 以响应几个不同的输入:</p><ul><li><strong>改变输入框中的文本时</strong>(人为)应该根据输入框的内容是否是<strong>空值</strong>,从而决定将表单的状态从空值状态切换到<strong>输入中</strong>或切换回原状态。</li><li><strong>点击提交按钮时</strong>(人为)应该将表单的状态切换到<strong>提交中</strong>的状态。</li><li><strong>网络请求成功后</strong>(计算机)应该将表单的状态切换到<strong>成功</strong>的状态。</li><li><strong>网络请求失败后</strong>(计算机)应该将表单的状态切换到<strong>失败</strong>的状态,与此同时,显示错误信息。</li></ul><h4 id="步骤3:通过useState表示内存中的state"><a href="#步骤3:通过useState表示内存中的state" class="headerlink" title="步骤3:通过useState表示内存中的state"></a>步骤3:通过<code>useState</code>表示内存中的<code>state</code></h4><p>接下来,我们会需要在内存中通过 <a href="https://react.docschina.org/reference/react/useState"><code>useState</code></a> 表示组件中的视图状态。<code>state</code>的每个部分都是“处在变化中的”,并且需要<strong>让“变化大的部分”尽可能的少</strong>。</p><ol><li>先从<strong>绝对必须存在</strong>的状态开始。</li><li>接下来,创建一个状态变量去代表想要显示的可时状态</li><li>在很难想出最好的办法时,就从添加足够多的<code>state</code>开始,<strong>确保</strong>所有可能的视图状态都包含。</li></ol><p>最初的想法或许不是最好的,必要时,**重构<code>state</code>**也是步骤中的一部分。</p><h4 id="步骤4-删除任何不必要的state变量"><a href="#步骤4-删除任何不必要的state变量" class="headerlink" title="步骤4:删除任何不必要的state变量"></a>步骤4:删除任何不必要的state变量</h4><p>我们想要避免<code>state</code>内容中的重复,从而只需要关注必要的部分。这就需要我们花费时间去重构我们的<code>state</code>结构,这样会让我们的组件更容易被理解,从而减少重复避免歧义。<strong>主要的目的是防止出现在内存中的state不代表任何我们让用户看到的有效UI的情况。</strong></p><p>在创建<code>state</code>变量的时候,我们应该反问自己以下这些问题:</p><ul><li><strong>这个<code>state</code>是否会导致矛盾?</strong>例如,<code>isTyping</code> 与 <code>isSubmitting</code> 的状态不能同时为 <code>true</code>。矛盾的产生通常说明了这个 state 没有足够的约束条件。两个布尔值有四种可能的组合,但是只有三种对应有效的状态。为了将“不可能”的状态移除,可以将 <code>'typing'</code>、<code>'submitting'</code> 以及 <code>'success'</code> 这三个中的其中一个与 <code>status</code> 结合。</li><li><strong>相同的信息是否已经在另一个 state 变量中存在</strong>?另一个矛盾:<code>isEmpty</code> 和 <code>isTyping</code> 不能同时为 <code>true</code>。通过使它们成为独立的 state 变量,可能会导致它们不同步并导致 bug。幸运的是,可以移除 <code>isEmpty</code> 转而用 <code>message.length === 0</code>。</li><li><strong>是否可以通过另一个 state 变量的相反值得到相同的信息</strong>?<code>isError</code> 是多余的,因为你可以检查 <code>error !== null</code>。</li></ul><p>正是因为在不破坏功能的情况下删除其中任何一个状态变量,才可以确定这些都是必要的。</p><h4 id="步骤5-连接事件处理函数以设置state"><a href="#步骤5-连接事件处理函数以设置state" class="headerlink" title="步骤5:连接事件处理函数以设置state"></a>步骤5:连接事件处理函数以设置state</h4><p>最后,创建事件处理函数去设置<code>state</code>变量。</p><h2 id="选择状态结构"><a href="#选择状态结构" class="headerlink" title="选择状态结构"></a>选择状态结构</h2><p>良好的状态组织,可以区分易于修改和调试的组件和频繁出问题的组件。<strong>最重要的原则是,状态不应该包含冗余或重复的信息。</strong></p><h3 id="构建state的原则"><a href="#构建state的原则" class="headerlink" title="构建state的原则"></a>构建state的原则</h3><p>当编写一个存有<code>state</code>的组件时,需要我们去选择使用多少个<code>state</code>变量以及他们都是什么数据格式。以下是构建<code>state</code>的原则来指导哦们做出更好的决策:</p><ol><li>**合并关联的<code>state</code>**。如果存在同时更新俩个或者多个的<code>state</code>变量,就该考虑将他们合并为一个单独的<code>state</code>变量。</li><li>**避免互相矛盾的<code>state</code>**。当<code>state</code>结构存在多个互相矛盾或者“不一致”的<code>state</code>时,就应该避免这个情况。</li><li>**避免冗余的<code>state</code>**。如果在渲染期间从组件的<code>props</code>或现有的<code>state</code>变量中计算一些信息,那么这些信息不因该放在该组件的<code>state</code>中。</li><li>**避免重复的<code>state</code>**。当统一数据在多个<code>state</code>变量之间或在多个嵌套对象总重复时,会很难保持同步。因此应尽量减少重复。</li><li>**避免深层嵌套的<code>state</code>**。深度分层的<code>state</code>更新起来会不方便,最好时构建扁平化的<code>state</code>。</li></ol><p>这些原则背后的目标是<strong>使<code>state</code>易于更新而不引入错误</strong>。从<code>state</code>中删除冗余和重复数据有助于所有部分保持同步。这类似于数据库工程师想要 <a href="https://docs.microsoft.com/zh-CN/office/troubleshoot/access/database-normalization-description">“规范化”数据库结构</a>,以减少出现错误的机会。用爱因斯坦的话说,<strong>“让你的状态尽可能简单,但不要过于简单。”</strong></p><h3 id="合并关联的state"><a href="#合并关联的state" class="headerlink" title="合并关联的state"></a>合并关联的state</h3><p>有时候我们可能不确定使用单个<code>state</code>变量还是多个<code>state</code>变量。</p><p>例如下面的存在x、y</p><p>是这样做?</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">const [x, setX] = useState(0);</span><br><span class="line">const [y, setY] = useState(0);</span><br></pre></td></tr></tbody></table></figure><p>还是这样做?</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">const [position, setPosition] = useState({ x: 0, y: 0 });</span><br></pre></td></tr></tbody></table></figure><p>从技术实现上来说,可以使用任何一种方法。但是,<strong>如果俩个<code>state</code>变量总是一起变化,则将它们统一成state变量可能会更好</strong>。这样我们就不会忘记让它们始终保持同步。</p><p>另一种情况是,我们将数据整合到一个对象或一个数组中时,不知道需要多少个<code>state</code>片段。例如,用户可以自定义字段的表单,这样就很有帮助。</p><p><strong>注意</strong>:如果 state 变量是一个对象时,请记住,<a href="https://react.docschina.org/learn/updating-objects-in-state">不能只更新其中的一个字段</a> 而不显式复制其他字段。例如,在上面的例子中,不能写成 <code>setPosition({ x: 100 })</code>,因为它根本就没有 <code>y</code> 属性! 相反,如果你想要仅设置 <code>x</code>,则可执行 <code>setPosition({ ...position, x: 100 })</code>,或将它们分成两个 state 变量,并执行 <code>setX(100)</code>。</p><h3 id="避免矛盾的state"><a href="#避免矛盾的state" class="headerlink" title="避免矛盾的state"></a>避免矛盾的state</h3><p>看以下例子代码:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line"></span><br><span class="line">export default function FeedbackForm() {</span><br><span class="line"> const [text, setText] = useState('');</span><br><span class="line"> // 俩个状态去控制 很容易去忘记更新</span><br><span class="line"> const [isSending, setIsSending] = useState(false);</span><br><span class="line"> const [isSent, setIsSent] = useState(false);</span><br><span class="line"></span><br><span class="line"> async function handleSubmit(e) {</span><br><span class="line"> e.preventDefault();</span><br><span class="line"> setIsSending(true);</span><br><span class="line"> await sendMessage(text);</span><br><span class="line"> setIsSending(false);</span><br><span class="line"> setIsSent(true);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if (isSent) {</span><br><span class="line"> return <h1>Thanks for feedback!</h1></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <form onSubmit={handleSubmit}></span><br><span class="line"> <p>How was your stay at The Prancing Pony?</p></span><br><span class="line"> <textarea</span><br><span class="line"> disabled={isSending}</span><br><span class="line"> value={text}</span><br><span class="line"> onChange={e => setText(e.target.value)}</span><br><span class="line"> /></span><br><span class="line"> <br /></span><br><span class="line"> <button</span><br><span class="line"> disabled={isSending}</span><br><span class="line"> type="submit"</span><br><span class="line"> ></span><br><span class="line"> Send</span><br><span class="line"> </button></span><br><span class="line"> {isSending && <p>Sending...</p>}</span><br><span class="line"> </form></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// 假装发送一条消息。</span><br><span class="line">function sendMessage(text) {</span><br><span class="line"> return new Promise(resolve => {</span><br><span class="line"> setTimeout(resolve, 2000);</span><br><span class="line"> });</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>观察这段代码,其实是有效的,但是会存在一些<code>state</code>“极难处理”。如果忘记 <code>setIsSent</code> 和 <code>setIsSending</code>,则可能会出现 <code>isSending</code> 和 <code>isSent</code> 同时为 <code>true</code> 的情况。组件越复杂,就很难理解发生了什么。</p><p><strong>因为 <code>isSending</code> 和 <code>isSent</code> 不应同时为 <code>true</code>,所以最好用一个 <code>status</code> 变量来代替它们,这个 state 变量可以采取三种有效状态其中之一</strong>:<code>'typing'</code> (初始), <code>'sending'</code>, 和 <code>'sent'</code>:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line"></span><br><span class="line">export default function FeedbackForm() {</span><br><span class="line"> const [text, setText] = useState('');</span><br><span class="line"> // 将state合并为三个状态 动态修改去做条件判断</span><br><span class="line"> const [status, setStatus] = useState('typing');</span><br><span class="line"></span><br><span class="line"> async function handleSubmit(e) {</span><br><span class="line"> e.preventDefault();</span><br><span class="line"> setStatus('sending');</span><br><span class="line"> await sendMessage(text);</span><br><span class="line"> setStatus('sent');</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //它们不是 state 变量,所以不必担心它们彼此失去同步。</span><br><span class="line"> const isSending = status === 'sending';</span><br><span class="line"> const isSent = status === 'sent';</span><br><span class="line"></span><br><span class="line"> if (isSent) {</span><br><span class="line"> return <h1>Thanks for feedback!</h1></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <form onSubmit={handleSubmit}></span><br><span class="line"> <p>How was your stay at The Prancing Pony?</p></span><br><span class="line"> <textarea</span><br><span class="line"> disabled={isSending}</span><br><span class="line"> value={text}</span><br><span class="line"> onChange={e => setText(e.target.value)}</span><br><span class="line"> /></span><br><span class="line"> <br /></span><br><span class="line"> <button</span><br><span class="line"> disabled={isSending}</span><br><span class="line"> type="submit"</span><br><span class="line"> ></span><br><span class="line"> Send</span><br><span class="line"> </button></span><br><span class="line"> {isSending && <p>Sending...</p>}</span><br><span class="line"> </form></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// 假装发送一条消息。</span><br><span class="line">function sendMessage(text) {</span><br><span class="line"> return new Promise(resolve => {</span><br><span class="line"> setTimeout(resolve, 2000);</span><br><span class="line"> });</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h3 id="避免冗余的state"><a href="#避免冗余的state" class="headerlink" title="避免冗余的state"></a>避免冗余的state</h3><p>如果我们在渲染期间从组件的<code>props</code>或现有的<code>state</code>变量中计算出一些信息,则不应该把这些信息放到该组件的<code>state</code>中。</p><p>仔细看看以下代码。他可以允许,仔细观察就能发现其中的冗余之处。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line"></span><br><span class="line">export default function Form() {</span><br><span class="line"> const [firstName, setFirstName] = useState('');</span><br><span class="line"> const [lastName, setLastName] = useState('');</span><br><span class="line"> // 没错就是这里 这里fullName其实可以通过firstName 和 lastName计算得出</span><br><span class="line"> // const [fullName, setFullName] = useState('');</span><br><span class="line"> // 就可以替换成以下这段代码</span><br><span class="line"> // 这样就不需要每次在依赖的state中去重新修改状态</span><br><span class="line">const fullName = firstName + ''+lastName</span><br><span class="line"> </span><br><span class="line"> function handleFirstNameChange(e) {</span><br><span class="line"> setFirstName(e.target.value);</span><br><span class="line"> // setFullName(e.target.value + ' ' + lastName);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> function handleLastNameChange(e) {</span><br><span class="line"> setLastName(e.target.value);</span><br><span class="line"> //setFullName(firstName + ' ' + e.target.value);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <h2>Let’s check you in</h2></span><br><span class="line"> <label></span><br><span class="line"> First name:{' '}</span><br><span class="line"> <input</span><br><span class="line"> value={firstName}</span><br><span class="line"> onChange={handleFirstNameChange}</span><br><span class="line"> /></span><br><span class="line"> </label></span><br><span class="line"> <label></span><br><span class="line"> Last name:{' '}</span><br><span class="line"> <input</span><br><span class="line"> value={lastName}</span><br><span class="line"> onChange={handleLastNameChange}</span><br><span class="line"> /></span><br><span class="line"> </label></span><br><span class="line"> <p></span><br><span class="line"> Your ticket will be issued to: <b>{fullName}</b></span><br><span class="line"> </p></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>为什么能用常量去做呢?这是因为事件处理程序不需要任何操作去更新它,当我们调用<code>setFirstName</code> 或 <code>setLastName</code> 时,你会触发一次重新渲染,然后下一个 <code>fullName</code> 将从新数据中计算出来。</p><h5 id="深入探讨-不要在-state-中镜像-props"><a href="#深入探讨-不要在-state-中镜像-props" class="headerlink" title="深入探讨:不要在 state 中镜像 props"></a>深入探讨:不要在 state 中镜像 props</h5><p>看看以下代码:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">function Message({ messageColor }) {</span><br><span class="line"> const [color, setColor] = useState(messageColor);</span><br></pre></td></tr></tbody></table></figure><p>这里,一个 <code>color</code> state 变量被初始化为 <code>messageColor</code> 的 prop 值。这段代码的问题在于,<strong>如果父组件稍后传递不同的 <code>messageColor</code> 值(例如,将其从 <code>'blue'</code> 更改为 <code>'red'</code>),则 <code>color</code></strong> state 变量<strong>将不会更新!</strong> state 仅在第一次渲染期间初始化。</p><p>这就是为什么在 state 变量中,“镜像”一些 prop 属性会导致混淆的原因。相反,你要在代码中直接使用 <code>messageColor</code> 属性。如果想给它起一个更短的名称,请使用常量:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">function Message({ messageColor }) {</span><br><span class="line"></span><br><span class="line"> const color = messageColor;</span><br></pre></td></tr></tbody></table></figure><p>这种写法就不会与从父组件传递的属性失去同步。</p><p>只有当你 <strong>想要</strong> 忽略特定 props 属性的所有更新时,将 props “镜像”到 state 才有意义。按照惯例,prop 名称以 <code>initial</code> 或 <code>default</code> 开头,以阐明该 prop 的新值将被忽略:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">function Message({ initialColor }) {</span><br><span class="line"> // 这个 `color` state 变量用于保存 `initialColor` 的 **初始值**。</span><br><span class="line"> // 对于 `initialColor` 属性的进一步更改将被忽略。</span><br><span class="line"> const [color, setColor] = useState(initialColor);</span><br></pre></td></tr></tbody></table></figure><h3 id="避免重复的state"><a href="#避免重复的state" class="headerlink" title="避免重复的state"></a>避免重复的state</h3><p>下面这是一个可以选择的菜单列表:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line"></span><br><span class="line">const initialItems = [</span><br><span class="line"> { title: 'pretzels', id: 0 },</span><br><span class="line"> { title: 'crispy seaweed', id: 1 },</span><br><span class="line"> { title: 'granola bar', id: 2 },</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line">export default function Menu() {</span><br><span class="line"> const [items, setItems] = useState(initialItems);</span><br><span class="line"> const [selectedItem, setSelectedItem] = useState(</span><br><span class="line"> items[0]</span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <h2>What's your travel snack?</h2></span><br><span class="line"> <ul></span><br><span class="line"> {items.map(item => (</span><br><span class="line"> <li key={item.id}></span><br><span class="line"> {item.title}</span><br><span class="line"> {' '}</span><br><span class="line"> <button onClick={() => {</span><br><span class="line"> setSelectedItem(item);</span><br><span class="line"> }}>Choose</button></span><br><span class="line"> </li></span><br><span class="line"> ))}</span><br><span class="line"> </ul></span><br><span class="line"> <p>You picked {selectedItem.title}.</p></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p> 这里的所选元素作为对象存储在<code>selectedItem</code> state变量中。然而,**<code>selecteItem</code>的内容与<code>items</code>列表中的某一项是同一个对象**,这意味着关于该项本身的信息在俩个地方产生了重复。</p><p>我们在将我们的组件进行修改,让每个项目都可以编辑,看看会出现什么问题?</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line"></span><br><span class="line">const initialItems = [</span><br><span class="line"> { title: 'pretzels', id: 0 },</span><br><span class="line"> { title: 'crispy seaweed', id: 1 },</span><br><span class="line"> { title: 'granola bar', id: 2 },</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line">export default function Menu() {</span><br><span class="line"> const [items, setItems] = useState(initialItems);</span><br><span class="line"> const [selectedItem, setSelectedItem] = useState(</span><br><span class="line"> items[0]</span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> function handleItemChange(id, e) {</span><br><span class="line"> setItems(items.map(item => {</span><br><span class="line"> if (item.id === id) {</span><br><span class="line"> return {</span><br><span class="line"> ...item,</span><br><span class="line"> title: e.target.value,</span><br><span class="line"> };</span><br><span class="line"> } else {</span><br><span class="line"> return item;</span><br><span class="line"> }</span><br><span class="line"> }));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <h2>What's your travel snack?</h2> </span><br><span class="line"> <ul></span><br><span class="line"> {items.map((item, index) => (</span><br><span class="line"> <li key={item.id}></span><br><span class="line"> <input</span><br><span class="line"> value={item.title}</span><br><span class="line"> onChange={e => {</span><br><span class="line"> handleItemChange(item.id, e)</span><br><span class="line"> }}</span><br><span class="line"> /></span><br><span class="line"> {' '}</span><br><span class="line"> <button onClick={() => {</span><br><span class="line"> setSelectedItem(item);</span><br><span class="line"> }}>Choose</button></span><br><span class="line"> </li></span><br><span class="line"> ))}</span><br><span class="line"> </ul></span><br><span class="line"> <p>You picked {selectedItem.title}.</p></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>注意,当我们选择“choose”按钮时,<strong>然后编辑</strong>它,<strong>输入会更新</strong>,<strong>但是底部的标签不会反应编辑的内容</strong>。这是因为我们存在了重复的<code>state</code>,并且忘记去更新了<code>selectedItem</code>。</p><p>我们也可以选择去更新<code>selectedItem</code>,但更简单的办法是去消除重复项。在下面对代码中,我们就可以将<code>selectedId</code>保存在state中,而不是在<code>selectedItem</code>对象中(它创建了一个与<code>items</code>内重复的对象),然后通过<code>items</code>数组去过滤出具有该ID的项,以此来获取<code>selectedItem</code>:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line"></span><br><span class="line">const initialItems = [</span><br><span class="line"> { title: 'pretzels', id: 0 },</span><br><span class="line"> { title: 'crispy seaweed', id: 1 },</span><br><span class="line"> { title: 'granola bar', id: 2 },</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line">export default function Menu() {</span><br><span class="line"> const [items, setItems] = useState(initialItems);</span><br><span class="line"> const [selectedId, setSelectedId] = useState(0);</span><br><span class="line"></span><br><span class="line"> const selectedItem = items.find(item =></span><br><span class="line"> item.id === selectedId</span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> function handleItemChange(id, e) {</span><br><span class="line"> setItems(items.map(item => {</span><br><span class="line"> if (item.id === id) {</span><br><span class="line"> return {</span><br><span class="line"> ...item,</span><br><span class="line"> title: e.target.value,</span><br><span class="line"> };</span><br><span class="line"> } else {</span><br><span class="line"> return item;</span><br><span class="line"> }</span><br><span class="line"> }));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <h2>What's your travel snack?</h2></span><br><span class="line"> <ul></span><br><span class="line"> {items.map((item, index) => (</span><br><span class="line"> <li key={item.id}></span><br><span class="line"> <input</span><br><span class="line"> value={item.title}</span><br><span class="line"> onChange={e => {</span><br><span class="line"> handleItemChange(item.id, e)</span><br><span class="line"> }}</span><br><span class="line"> /></span><br><span class="line"> {' '}</span><br><span class="line"> <button onClick={() => {</span><br><span class="line"> setSelectedId(item.id);</span><br><span class="line"> }}>Choose</button></span><br><span class="line"> </li></span><br><span class="line"> ))}</span><br><span class="line"> </ul></span><br><span class="line"> <p>You picked {selectedItem.title}.</p></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>在或者,我们可以将所选择的索引保存在<code>state</code>中。</p><p>state 过去常常是这样赋值的:</p><ul><li><code>items = [{ id: 0, title: 'pretzels'}, ...]</code></li><li><code>selectedItem = {id: 0, title: 'pretzels'}</code></li></ul><p>改了之后是这样的:</p><ul><li><code>items = [{ id: 0, title: 'pretzels'}, ...]</code></li><li><code>selectedId = 0</code></li></ul><p>这样,就不存在重复的<code>state</code>,只保留了必要的<code>state</code>。这样的话,当我们编辑<code>selected</code>元素,下面的标签就会立即更新。这是因为<code>setItem</code>会触发重新渲染,而<code>item.find(...)</code>会找到带有更新文本的元素,我们不需要在state中保存<strong>选定的元素</strong>,因为<strong>只有选定的ID</strong>是必要的。其余的都可以在渲染期间计算得出。</p><h3 id="避免深度的嵌套state"><a href="#避免深度的嵌套state" class="headerlink" title="避免深度的嵌套state"></a>避免深度的嵌套state</h3><p>想象一下,一个由行星、大陆和国家组成的旅行计划。你可能会尝试使用嵌套对象和数组来构建它的 state,就像下面这个例子:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br></pre></td><td class="code"><pre><span class="line">// app.js</span><br><span class="line">import { useState } from 'react';</span><br><span class="line">import { initialTravelPlan } from './places.js';</span><br><span class="line"></span><br><span class="line">// 递归展示</span><br><span class="line">function PlaceTree({ place }) {</span><br><span class="line"> const childPlaces = place.childPlaces;</span><br><span class="line"> return (</span><br><span class="line"> <li></span><br><span class="line"> {place.title}</span><br><span class="line"> {childPlaces.length > 0 && (</span><br><span class="line"> <ol></span><br><span class="line"> {childPlaces.map(place => (</span><br><span class="line"> <PlaceTree key={place.id} place={place} /></span><br><span class="line"> ))}</span><br><span class="line"> </ol></span><br><span class="line"> )}</span><br><span class="line"> </li></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">export default function TravelPlan() {</span><br><span class="line"> const [plan, setPlan] = useState(initialTravelPlan);</span><br><span class="line"> const planets = plan.childPlaces;</span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <h2>Places to visit</h2></span><br><span class="line"> <ol></span><br><span class="line"> {planets.map(place => (</span><br><span class="line"> <PlaceTree key={place.id} place={place} /></span><br><span class="line"> ))}</span><br><span class="line"> </ol></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line">// place.js</span><br><span class="line">export const initialTravelPlan = {</span><br><span class="line"> id: 0,</span><br><span class="line"> title: '(Root)',</span><br><span class="line"> childPlaces: [{</span><br><span class="line"> id: 1,</span><br><span class="line"> title: 'Earth',</span><br><span class="line"> childPlaces: [{</span><br><span class="line"> id: 2,</span><br><span class="line"> title: 'Africa',</span><br><span class="line"> childPlaces: [{</span><br><span class="line"> id: 3,</span><br><span class="line"> title: 'Botswana',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }, {</span><br><span class="line"> id: 4,</span><br><span class="line"> title: 'Egypt',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }, {</span><br><span class="line"> id: 5,</span><br><span class="line"> title: 'Kenya',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }, {</span><br><span class="line"> id: 6,</span><br><span class="line"> title: 'Madagascar',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }, {</span><br><span class="line"> id: 7,</span><br><span class="line"> title: 'Morocco',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }, {</span><br><span class="line"> id: 8,</span><br><span class="line"> title: 'Nigeria',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }, {</span><br><span class="line"> id: 9,</span><br><span class="line"> title: 'South Africa',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }]</span><br><span class="line"> }, {</span><br><span class="line"> id: 10,</span><br><span class="line"> title: 'Americas',</span><br><span class="line"> childPlaces: [{</span><br><span class="line"> id: 11,</span><br><span class="line"> title: 'Argentina',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }, {</span><br><span class="line"> id: 12,</span><br><span class="line"> title: 'Brazil',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }, {</span><br><span class="line"> id: 13,</span><br><span class="line"> title: 'Barbados',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }, {</span><br><span class="line"> id: 14,</span><br><span class="line"> title: 'Canada',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }, {</span><br><span class="line"> id: 15,</span><br><span class="line"> title: 'Jamaica',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }, {</span><br><span class="line"> id: 16,</span><br><span class="line"> title: 'Mexico',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }, {</span><br><span class="line"> id: 17,</span><br><span class="line"> title: 'Trinidad and Tobago',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }, {</span><br><span class="line"> id: 18,</span><br><span class="line"> title: 'Venezuela',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }]</span><br><span class="line"> }, {</span><br><span class="line"> id: 19,</span><br><span class="line"> title: 'Asia',</span><br><span class="line"> childPlaces: [{</span><br><span class="line"> id: 20,</span><br><span class="line"> title: 'China',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }, {</span><br><span class="line"> id: 21,</span><br><span class="line"> title: 'India',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }, {</span><br><span class="line"> id: 22,</span><br><span class="line"> title: 'Singapore',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }, {</span><br><span class="line"> id: 23,</span><br><span class="line"> title: 'South Korea',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }, {</span><br><span class="line"> id: 24,</span><br><span class="line"> title: 'Thailand',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }, {</span><br><span class="line"> id: 25,</span><br><span class="line"> title: 'Vietnam',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }]</span><br><span class="line"> }, {</span><br><span class="line"> id: 26,</span><br><span class="line"> title: 'Europe',</span><br><span class="line"> childPlaces: [{</span><br><span class="line"> id: 27,</span><br><span class="line"> title: 'Croatia',</span><br><span class="line"> childPlaces: [],</span><br><span class="line"> }, {</span><br><span class="line"> id: 28,</span><br><span class="line"> title: 'France',</span><br><span class="line"> childPlaces: [],</span><br><span class="line"> }, {</span><br><span class="line"> id: 29,</span><br><span class="line"> title: 'Germany',</span><br><span class="line"> childPlaces: [],</span><br><span class="line"> }, {</span><br><span class="line"> id: 30,</span><br><span class="line"> title: 'Italy',</span><br><span class="line"> childPlaces: [],</span><br><span class="line"> }, {</span><br><span class="line"> id: 31,</span><br><span class="line"> title: 'Portugal',</span><br><span class="line"> childPlaces: [],</span><br><span class="line"> }, {</span><br><span class="line"> id: 32,</span><br><span class="line"> title: 'Spain',</span><br><span class="line"> childPlaces: [],</span><br><span class="line"> }, {</span><br><span class="line"> id: 33,</span><br><span class="line"> title: 'Turkey',</span><br><span class="line"> childPlaces: [],</span><br><span class="line"> }]</span><br><span class="line"> }, {</span><br><span class="line"> id: 34,</span><br><span class="line"> title: 'Oceania',</span><br><span class="line"> childPlaces: [{</span><br><span class="line"> id: 35,</span><br><span class="line"> title: 'Australia',</span><br><span class="line"> childPlaces: [],</span><br><span class="line"> }, {</span><br><span class="line"> id: 36,</span><br><span class="line"> title: 'Bora Bora (French Polynesia)',</span><br><span class="line"> childPlaces: [],</span><br><span class="line"> }, {</span><br><span class="line"> id: 37,</span><br><span class="line"> title: 'Easter Island (Chile)',</span><br><span class="line"> childPlaces: [],</span><br><span class="line"> }, {</span><br><span class="line"> id: 38,</span><br><span class="line"> title: 'Fiji',</span><br><span class="line"> childPlaces: [],</span><br><span class="line"> }, {</span><br><span class="line"> id: 39,</span><br><span class="line"> title: 'Hawaii (the USA)',</span><br><span class="line"> childPlaces: [],</span><br><span class="line"> }, {</span><br><span class="line"> id: 40,</span><br><span class="line"> title: 'New Zealand',</span><br><span class="line"> childPlaces: [],</span><br><span class="line"> }, {</span><br><span class="line"> id: 41,</span><br><span class="line"> title: 'Vanuatu',</span><br><span class="line"> childPlaces: [],</span><br><span class="line"> }]</span><br><span class="line"> }]</span><br><span class="line"> }, {</span><br><span class="line"> id: 42,</span><br><span class="line"> title: 'Moon',</span><br><span class="line"> childPlaces: [{</span><br><span class="line"> id: 43,</span><br><span class="line"> title: 'Rheita',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }, {</span><br><span class="line"> id: 44,</span><br><span class="line"> title: 'Piccolomini',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }, {</span><br><span class="line"> id: 45,</span><br><span class="line"> title: 'Tycho',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }]</span><br><span class="line"> }, {</span><br><span class="line"> id: 46,</span><br><span class="line"> title: 'Mars',</span><br><span class="line"> childPlaces: [{</span><br><span class="line"> id: 47,</span><br><span class="line"> title: 'Corn Town',</span><br><span class="line"> childPlaces: []</span><br><span class="line"> }, {</span><br><span class="line"> id: 48,</span><br><span class="line"> title: 'Green Hill',</span><br><span class="line"> childPlaces: [] </span><br><span class="line"> }]</span><br><span class="line"> }]</span><br><span class="line">};</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>这时候如果我们想添加一个按钮来删除已经去过的地方。该如何做呢?<a href="https://react.docschina.org/learn/updating-objects-in-state#updating-a-nested-object">更新嵌套的 state</a> 需要从更改部分一直向上复制对象。删除一个深度嵌套的地点将涉及复制其整个父级地点链。这样的代码可能非常冗长。</p><p><strong>如果 state 嵌套太深,难以轻松更新,可以考虑将其“扁平化”。</strong> 这里可以通过一个方法来重构上面这个数据:不同树状结构,每个节点的<code>place</code>都是一个<strong>包含其子节点</strong>的数组,我们可以让每个节点的<code>place</code><strong>作为数组保存其子节点的ID</strong>。然后存储一个节点ID与相应节点的映射关系。</p><p>看看下面重组的数据:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br></pre></td><td class="code"><pre><span class="line">// app.js</span><br><span class="line">import { useState } from 'react';</span><br><span class="line">import { initialTravelPlan } from './places.js';</span><br><span class="line"></span><br><span class="line">// 先找到该节点的 孩子id 再去递归id去寻找</span><br><span class="line">function PlaceTree({ id, placesById }) {</span><br><span class="line"> const place = placesById[id];</span><br><span class="line"> const childIds = place.childIds;</span><br><span class="line"> return (</span><br><span class="line"> <li></span><br><span class="line"> {place.title}</span><br><span class="line"> {childIds.length > 0 && (</span><br><span class="line"> <ol></span><br><span class="line"> {childIds.map(childId => (</span><br><span class="line"> <PlaceTree</span><br><span class="line"> key={childId}</span><br><span class="line"> id={childId}</span><br><span class="line"> placesById={placesById}</span><br><span class="line"> /></span><br><span class="line"> ))}</span><br><span class="line"> </ol></span><br><span class="line"> )}</span><br><span class="line"> </li></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">export default function TravelPlan() {</span><br><span class="line"> const [plan, setPlan] = useState(initialTravelPlan);</span><br><span class="line"> const root = plan[0];</span><br><span class="line"> const planetIds = root.childIds;</span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <h2>Places to visit</h2></span><br><span class="line"> <ol></span><br><span class="line"> {planetIds.map(id => (</span><br><span class="line"> <PlaceTree</span><br><span class="line"> key={id}</span><br><span class="line"> id={id}</span><br><span class="line"> placesById={plan}</span><br><span class="line"> /></span><br><span class="line"> ))}</span><br><span class="line"> </ol></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line">// places.js</span><br><span class="line">export const initialTravelPlan = {</span><br><span class="line"> 0: {</span><br><span class="line"> id: 0,</span><br><span class="line"> title: '(Root)',</span><br><span class="line"> childIds: [1, 42, 46],</span><br><span class="line"> },</span><br><span class="line"> 1: {</span><br><span class="line"> id: 1,</span><br><span class="line"> title: 'Earth',</span><br><span class="line"> childIds: [2, 10, 19, 26, 34]</span><br><span class="line"> },</span><br><span class="line"> 2: {</span><br><span class="line"> id: 2,</span><br><span class="line"> title: 'Africa',</span><br><span class="line"> childIds: [3, 4, 5, 6 , 7, 8, 9]</span><br><span class="line"> }, </span><br><span class="line"> 3: {</span><br><span class="line"> id: 3,</span><br><span class="line"> title: 'Botswana',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 4: {</span><br><span class="line"> id: 4,</span><br><span class="line"> title: 'Egypt',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 5: {</span><br><span class="line"> id: 5,</span><br><span class="line"> title: 'Kenya',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 6: {</span><br><span class="line"> id: 6,</span><br><span class="line"> title: 'Madagascar',</span><br><span class="line"> childIds: []</span><br><span class="line"> }, </span><br><span class="line"> 7: {</span><br><span class="line"> id: 7,</span><br><span class="line"> title: 'Morocco',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 8: {</span><br><span class="line"> id: 8,</span><br><span class="line"> title: 'Nigeria',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 9: {</span><br><span class="line"> id: 9,</span><br><span class="line"> title: 'South Africa',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 10: {</span><br><span class="line"> id: 10,</span><br><span class="line"> title: 'Americas',</span><br><span class="line"> childIds: [11, 12, 13, 14, 15, 16, 17, 18], </span><br><span class="line"> },</span><br><span class="line"> 11: {</span><br><span class="line"> id: 11,</span><br><span class="line"> title: 'Argentina',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 12: {</span><br><span class="line"> id: 12,</span><br><span class="line"> title: 'Brazil',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 13: {</span><br><span class="line"> id: 13,</span><br><span class="line"> title: 'Barbados',</span><br><span class="line"> childIds: []</span><br><span class="line"> }, </span><br><span class="line"> 14: {</span><br><span class="line"> id: 14,</span><br><span class="line"> title: 'Canada',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 15: {</span><br><span class="line"> id: 15,</span><br><span class="line"> title: 'Jamaica',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 16: {</span><br><span class="line"> id: 16,</span><br><span class="line"> title: 'Mexico',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 17: {</span><br><span class="line"> id: 17,</span><br><span class="line"> title: 'Trinidad and Tobago',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 18: {</span><br><span class="line"> id: 18,</span><br><span class="line"> title: 'Venezuela',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 19: {</span><br><span class="line"> id: 19,</span><br><span class="line"> title: 'Asia',</span><br><span class="line"> childIds: [20, 21, 22, 23, 24, 25], </span><br><span class="line"> },</span><br><span class="line"> 20: {</span><br><span class="line"> id: 20,</span><br><span class="line"> title: 'China',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 21: {</span><br><span class="line"> id: 21,</span><br><span class="line"> title: 'India',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 22: {</span><br><span class="line"> id: 22,</span><br><span class="line"> title: 'Singapore',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 23: {</span><br><span class="line"> id: 23,</span><br><span class="line"> title: 'South Korea',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 24: {</span><br><span class="line"> id: 24,</span><br><span class="line"> title: 'Thailand',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 25: {</span><br><span class="line"> id: 25,</span><br><span class="line"> title: 'Vietnam',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 26: {</span><br><span class="line"> id: 26,</span><br><span class="line"> title: 'Europe',</span><br><span class="line"> childIds: [27, 28, 29, 30, 31, 32, 33], </span><br><span class="line"> },</span><br><span class="line"> 27: {</span><br><span class="line"> id: 27,</span><br><span class="line"> title: 'Croatia',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 28: {</span><br><span class="line"> id: 28,</span><br><span class="line"> title: 'France',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 29: {</span><br><span class="line"> id: 29,</span><br><span class="line"> title: 'Germany',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 30: {</span><br><span class="line"> id: 30,</span><br><span class="line"> title: 'Italy',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 31: {</span><br><span class="line"> id: 31,</span><br><span class="line"> title: 'Portugal',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 32: {</span><br><span class="line"> id: 32,</span><br><span class="line"> title: 'Spain',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 33: {</span><br><span class="line"> id: 33,</span><br><span class="line"> title: 'Turkey',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 34: {</span><br><span class="line"> id: 34,</span><br><span class="line"> title: 'Oceania',</span><br><span class="line"> childIds: [35, 36, 37, 38, 39, 40, 41], </span><br><span class="line"> },</span><br><span class="line"> 35: {</span><br><span class="line"> id: 35,</span><br><span class="line"> title: 'Australia',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 36: {</span><br><span class="line"> id: 36,</span><br><span class="line"> title: 'Bora Bora (French Polynesia)',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 37: {</span><br><span class="line"> id: 37,</span><br><span class="line"> title: 'Easter Island (Chile)',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 38: {</span><br><span class="line"> id: 38,</span><br><span class="line"> title: 'Fiji',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 39: {</span><br><span class="line"> id: 40,</span><br><span class="line"> title: 'Hawaii (the USA)',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 40: {</span><br><span class="line"> id: 40,</span><br><span class="line"> title: 'New Zealand',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 41: {</span><br><span class="line"> id: 41,</span><br><span class="line"> title: 'Vanuatu',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 42: {</span><br><span class="line"> id: 42,</span><br><span class="line"> title: 'Moon',</span><br><span class="line"> childIds: [43, 44, 45]</span><br><span class="line"> },</span><br><span class="line"> 43: {</span><br><span class="line"> id: 43,</span><br><span class="line"> title: 'Rheita',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 44: {</span><br><span class="line"> id: 44,</span><br><span class="line"> title: 'Piccolomini',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 45: {</span><br><span class="line"> id: 45,</span><br><span class="line"> title: 'Tycho',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 46: {</span><br><span class="line"> id: 46,</span><br><span class="line"> title: 'Mars',</span><br><span class="line"> childIds: [47, 48]</span><br><span class="line"> },</span><br><span class="line"> 47: {</span><br><span class="line"> id: 47,</span><br><span class="line"> title: 'Corn Town',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 48: {</span><br><span class="line"> id: 48,</span><br><span class="line"> title: 'Green Hill',</span><br><span class="line"> childIds: []</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>此刻,<strong>state 已经“扁平化”(也称为“规范化”),更新嵌套项会变得更加容易。</strong></p><p>现在删除一个地点的话,只需要更新两个 state 级别:</p><ul><li>其 <strong>父级</strong> 地点的更新版本应该从其 <code>childIds</code> 数组中排除已删除的 ID。</li><li>其根级“表”对象的更新版本应包括父级地点的更新版本。</li></ul><p>看以下代码处理的实例:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br></pre></td><td class="code"><pre><span class="line">// app.js</span><br><span class="line">import { useState } from 'react';</span><br><span class="line">import { initialTravelPlan } from './places.js';</span><br><span class="line"></span><br><span class="line">export default function TravelPlan() {</span><br><span class="line"> const [plan, setPlan] = useState(initialTravelPlan);</span><br><span class="line"></span><br><span class="line"> function handleComplete(parentId, childId) {</span><br><span class="line"> const parent = plan[parentId];</span><br><span class="line"> // 创建一个其父级地点的新版本</span><br><span class="line"> // 但不包括子级 ID。</span><br><span class="line"> const nextParent = {</span><br><span class="line"> ...parent,</span><br><span class="line"> childIds: parent.childIds</span><br><span class="line"> .filter(id => id !== childId)</span><br><span class="line"> };</span><br><span class="line"> // 更新根 state 对象...</span><br><span class="line"> setPlan({</span><br><span class="line"> ...plan,</span><br><span class="line"> // ...以便它拥有更新的父级。</span><br><span class="line"> [parentId]: nextParent</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> const root = plan[0];</span><br><span class="line"> const planetIds = root.childIds;</span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <h2>Places to visit</h2></span><br><span class="line"> <ol></span><br><span class="line"> {planetIds.map(id => (</span><br><span class="line"> <PlaceTree</span><br><span class="line"> key={id}</span><br><span class="line"> id={id}</span><br><span class="line"> parentId={0}</span><br><span class="line"> placesById={plan}</span><br><span class="line"> onComplete={handleComplete}</span><br><span class="line"> /></span><br><span class="line"> ))}</span><br><span class="line"> </ol></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">function PlaceTree({ id, parentId, placesById, onComplete }) {</span><br><span class="line"> const place = placesById[id];</span><br><span class="line"> const childIds = place.childIds;</span><br><span class="line"> return (</span><br><span class="line"> <li></span><br><span class="line"> {place.title}</span><br><span class="line"> <button onClick={() => {</span><br><span class="line"> onComplete(parentId, id);</span><br><span class="line"> }}></span><br><span class="line"> Complete</span><br><span class="line"> </button></span><br><span class="line"> {childIds.length > 0 &&</span><br><span class="line"> <ol></span><br><span class="line"> {childIds.map(childId => (</span><br><span class="line"> <PlaceTree</span><br><span class="line"> key={childId}</span><br><span class="line"> id={childId}</span><br><span class="line"> parentId={id}</span><br><span class="line"> placesById={placesById}</span><br><span class="line"> onComplete={onComplete}</span><br><span class="line"> /></span><br><span class="line"> ))}</span><br><span class="line"> </ol></span><br><span class="line"> }</span><br><span class="line"> </li></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line">// place.js</span><br><span class="line">export const initialTravelPlan = {</span><br><span class="line"> 0: {</span><br><span class="line"> id: 0,</span><br><span class="line"> title: '(Root)',</span><br><span class="line"> childIds: [1, 42, 46],</span><br><span class="line"> },</span><br><span class="line"> 1: {</span><br><span class="line"> id: 1,</span><br><span class="line"> title: 'Earth',</span><br><span class="line"> childIds: [2, 10, 19, 26, 34]</span><br><span class="line"> },</span><br><span class="line"> 2: {</span><br><span class="line"> id: 2,</span><br><span class="line"> title: 'Africa',</span><br><span class="line"> childIds: [3, 4, 5, 6 , 7, 8, 9]</span><br><span class="line"> }, </span><br><span class="line"> 3: {</span><br><span class="line"> id: 3,</span><br><span class="line"> title: 'Botswana',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 4: {</span><br><span class="line"> id: 4,</span><br><span class="line"> title: 'Egypt',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 5: {</span><br><span class="line"> id: 5,</span><br><span class="line"> title: 'Kenya',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 6: {</span><br><span class="line"> id: 6,</span><br><span class="line"> title: 'Madagascar',</span><br><span class="line"> childIds: []</span><br><span class="line"> }, </span><br><span class="line"> 7: {</span><br><span class="line"> id: 7,</span><br><span class="line"> title: 'Morocco',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 8: {</span><br><span class="line"> id: 8,</span><br><span class="line"> title: 'Nigeria',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 9: {</span><br><span class="line"> id: 9,</span><br><span class="line"> title: 'South Africa',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 10: {</span><br><span class="line"> id: 10,</span><br><span class="line"> title: 'Americas',</span><br><span class="line"> childIds: [11, 12, 13, 14, 15, 16, 17, 18], </span><br><span class="line"> },</span><br><span class="line"> 11: {</span><br><span class="line"> id: 11,</span><br><span class="line"> title: 'Argentina',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 12: {</span><br><span class="line"> id: 12,</span><br><span class="line"> title: 'Brazil',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 13: {</span><br><span class="line"> id: 13,</span><br><span class="line"> title: 'Barbados',</span><br><span class="line"> childIds: []</span><br><span class="line"> }, </span><br><span class="line"> 14: {</span><br><span class="line"> id: 14,</span><br><span class="line"> title: 'Canada',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 15: {</span><br><span class="line"> id: 15,</span><br><span class="line"> title: 'Jamaica',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 16: {</span><br><span class="line"> id: 16,</span><br><span class="line"> title: 'Mexico',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 17: {</span><br><span class="line"> id: 17,</span><br><span class="line"> title: 'Trinidad and Tobago',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 18: {</span><br><span class="line"> id: 18,</span><br><span class="line"> title: 'Venezuela',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 19: {</span><br><span class="line"> id: 19,</span><br><span class="line"> title: 'Asia',</span><br><span class="line"> childIds: [20, 21, 22, 23, 24, 25], </span><br><span class="line"> },</span><br><span class="line"> 20: {</span><br><span class="line"> id: 20,</span><br><span class="line"> title: 'China',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 21: {</span><br><span class="line"> id: 21,</span><br><span class="line"> title: 'India',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 22: {</span><br><span class="line"> id: 22,</span><br><span class="line"> title: 'Singapore',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 23: {</span><br><span class="line"> id: 23,</span><br><span class="line"> title: 'South Korea',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 24: {</span><br><span class="line"> id: 24,</span><br><span class="line"> title: 'Thailand',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 25: {</span><br><span class="line"> id: 25,</span><br><span class="line"> title: 'Vietnam',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 26: {</span><br><span class="line"> id: 26,</span><br><span class="line"> title: 'Europe',</span><br><span class="line"> childIds: [27, 28, 29, 30, 31, 32, 33], </span><br><span class="line"> },</span><br><span class="line"> 27: {</span><br><span class="line"> id: 27,</span><br><span class="line"> title: 'Croatia',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 28: {</span><br><span class="line"> id: 28,</span><br><span class="line"> title: 'France',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 29: {</span><br><span class="line"> id: 29,</span><br><span class="line"> title: 'Germany',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 30: {</span><br><span class="line"> id: 30,</span><br><span class="line"> title: 'Italy',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 31: {</span><br><span class="line"> id: 31,</span><br><span class="line"> title: 'Portugal',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 32: {</span><br><span class="line"> id: 32,</span><br><span class="line"> title: 'Spain',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 33: {</span><br><span class="line"> id: 33,</span><br><span class="line"> title: 'Turkey',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 34: {</span><br><span class="line"> id: 34,</span><br><span class="line"> title: 'Oceania',</span><br><span class="line"> childIds: [35, 36, 37, 38, 39, 40, 41], </span><br><span class="line"> },</span><br><span class="line"> 35: {</span><br><span class="line"> id: 35,</span><br><span class="line"> title: 'Australia',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 36: {</span><br><span class="line"> id: 36,</span><br><span class="line"> title: 'Bora Bora (French Polynesia)',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 37: {</span><br><span class="line"> id: 37,</span><br><span class="line"> title: 'Easter Island (Chile)',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 38: {</span><br><span class="line"> id: 38,</span><br><span class="line"> title: 'Fiji',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 39: {</span><br><span class="line"> id: 39,</span><br><span class="line"> title: 'Hawaii (the USA)',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 40: {</span><br><span class="line"> id: 40,</span><br><span class="line"> title: 'New Zealand',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 41: {</span><br><span class="line"> id: 41,</span><br><span class="line"> title: 'Vanuatu',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 42: {</span><br><span class="line"> id: 42,</span><br><span class="line"> title: 'Moon',</span><br><span class="line"> childIds: [43, 44, 45]</span><br><span class="line"> },</span><br><span class="line"> 43: {</span><br><span class="line"> id: 43,</span><br><span class="line"> title: 'Rheita',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 44: {</span><br><span class="line"> id: 44,</span><br><span class="line"> title: 'Piccolomini',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 45: {</span><br><span class="line"> id: 45,</span><br><span class="line"> title: 'Tycho',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 46: {</span><br><span class="line"> id: 46,</span><br><span class="line"> title: 'Mars',</span><br><span class="line"> childIds: [47, 48]</span><br><span class="line"> },</span><br><span class="line"> 47: {</span><br><span class="line"> id: 47,</span><br><span class="line"> title: 'Corn Town',</span><br><span class="line"> childIds: []</span><br><span class="line"> },</span><br><span class="line"> 48: {</span><br><span class="line"> id: 48,</span><br><span class="line"> title: 'Green Hill',</span><br><span class="line"> childIds: []</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>这样就可以随心所欲嵌套state,“扁平化”可以解决很多问题。这使得state更容易更新,确保嵌套对象的不同部分中没有重复。</p><h2 id="在组件间共享状态"><a href="#在组件间共享状态" class="headerlink" title="在组件间共享状态"></a>在组件间共享状态</h2><p>如果我们需要俩个组件的状态始终同步修改。我们可以将相关状态从这俩个组件上移除,将这些状态移到最近的父级组件,然后通过<code>props</code>将这些状态传递给这俩个组件。<strong>这被称为“状态提升”。</strong></p><h2 id="对state进行保留和重置状态"><a href="#对state进行保留和重置状态" class="headerlink" title="对state进行保留和重置状态"></a>对state进行保留和重置状态</h2><p>当我们重新渲染一个组件时,React需要决定组件树哪些部分要保留和更新,以及丢弃或重新创建。在大多数情况下,React的自动处理机制已经做了大部分工作。默认情况下,React会保留树中与先前渲染组件树“匹配”的部分。</p><p>React允许覆盖默认行为,这时候可以通过向组件传递一个唯一<code>key</code>来<em>强制</em>重置其状态。这将告诉React,<strong>组件需要重新渲染。</strong></p><h2 id="提取状态到reducer中"><a href="#提取状态到reducer中" class="headerlink" title="提取状态到reducer中"></a>提取状态到reducer中</h2><p>对于需要更新多个状态的组件来说,会存在过于分散的事件处理程序。对于这种情况,我们可以在组件外部将所有的状态更新逻辑合并到一个称为“reducer”的函数中。这样,事件处理程序就会变的简洁,只需要我们指定对应的“action”。同时定义,<code>reducer</code>函数指定状态因该如何更新去响应每个<code>action</code>!</p><h2 id="使用Context进行深层数据传递"><a href="#使用Context进行深层数据传递" class="headerlink" title="使用Context进行深层数据传递"></a>使用Context进行深层数据传递</h2><p>通常,我们会存在需要通过<code>props</code>将信息从父组件传递给子组件。如果需要在组件树中深入传递<code>prop</code>,或者树中许多组件都需要使用相同的<code>prop</code>,那么传递<code>prop</code>可能会变得麻烦。<code>Context</code>允许父组件将一些信息提供给它下层的任何组件,不管组件多深层也无需通过<code>props</code>逐层透传。</p><h2 id="使用Reducer和Context进行状态扩展"><a href="#使用Reducer和Context进行状态扩展" class="headerlink" title="使用Reducer和Context进行状态扩展"></a>使用Reducer和Context进行状态扩展</h2><p>Reducer可以帮助我们合并组件的状态更新逻辑。Context可以帮助我们将信息深处传递给其他组件。可以将二者结合使用,以管理复杂应用的状态。</p>]]></content>
<summary type="html"><h1 id="react之状态管理"><a href="#react之状态管理" class="headerlink" title="react之状态管理"></a>react之状态管理</h1><p>随着应用的不断加大,我们应该更有意识的的去关注我们的应用状态如何去组织,以及</summary>
<category term="前端框架" scheme="https://ayozoo.github.io/categories/%E5%89%8D%E7%AB%AF%E6%A1%86%E6%9E%B6/"/>
<category term="react" scheme="https://ayozoo.github.io/tags/react/"/>
</entry>
<entry>
<title>React之Hook篇</title>
<link href="https://ayozoo.github.io/2023/11/08/%20React%E4%B9%8Bhook%E7%AF%87/"/>
<id>https://ayozoo.github.io/2023/11/08/%20React%E4%B9%8Bhook%E7%AF%87/</id>
<published>2023-11-08T15:21:48.000Z</published>
<updated>2023-11-13T14:52:38.345Z</updated>
<content type="html"><![CDATA[<h1 id="React之Hook篇"><a href="#React之Hook篇" class="headerlink" title="React之Hook篇"></a>React之Hook篇</h1><h2 id="useState"><a href="#useState" class="headerlink" title="useState"></a>useState</h2><h3 id="什么是state?"><a href="#什么是state?" class="headerlink" title="什么是state?"></a>什么是state?</h3><p>在react中,数据不称为<code>data</code>,而称为<code>state</code></p><p><code>data</code> = > <code>state</code>(状态)</p><p>React,其实是一个<code>view library </code>(view库—只关注视图)</p><p><code>view </code>=> <code>update</code> => 视图的具体状态</p><p><code>state</code> <=> <code>view</code></p><p>state和视图是相关联的。</p><p>视图是某一个状态发生了变化,所以视图要进行相应的更新。</p><p><code>useState()</code>=> <code>state setState</code></p><p>这个可以解释为,视图需要<code>state</code>状态。通过<code>useState()</code>创建了一个状态和设置状态的方法。 </p><h3 id="react设计理念"><a href="#react设计理念" class="headerlink" title="react设计理念"></a>react设计理念</h3><ul><li><p>react设计理念:一切操作函数化。</p></li><li><p>在react中,贯彻js—–“函数是一等公民”的理念。</p></li><li><p>react提供的东西都是朴素的,简单的。</p></li><li><p>react大部分都是运行时;vue都是编译时的行为。</p></li></ul><h3 id="使用姿势"><a href="#使用姿势" class="headerlink" title="使用姿势"></a>使用姿势</h3><p>根据状态变更,有以下俩种:</p><ul><li><code>setXxx(xxx)</code> <em>简单情况</em></li><li><code>setXxx((x)=>{ return 表达式})</code> <em>复杂情况</em></li></ul><ol><li><p>常用写法一:<code>setXxx(xxx)</code></p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from "react";</span><br><span class="line"></span><br><span class="line">let initialState = 0;</span><br><span class="line">export default function useStateHook() {</span><br><span class="line"> const [count, setCount] = useState(initialState);</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <div></span><br><span class="line"> <h1>{count}</h1></span><br><span class="line"> <button onClick={() => setCount(count + 1)}>+ </button></span><br><span class="line"> </div></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// 变式</span><br><span class="line">import { useState } from "react";</span><br><span class="line"></span><br><span class="line">let initialState = 0;</span><br><span class="line">export default function useStateHook() {</span><br><span class="line"> const [count, setCount] = useState(initialState);</span><br><span class="line"></span><br><span class="line"> function handleClick() {</span><br><span class="line"> return setCount(count + 1);</span><br><span class="line"> }</span><br><span class="line"> return (</span><br><span class="line"> <div></span><br><span class="line"> <h1>{count}</h1></span><br><span class="line"> <button onClick={handleClick}>+</button></span><br><span class="line"> </div></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure></li><li><p><code>setXxx((x)=>{ return 表达式})</code></p></li></ol><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from "react";</span><br><span class="line"></span><br><span class="line">let initialState = 1;</span><br><span class="line">export default function useStateHook() {</span><br><span class="line"> const [count, setCount] = useState(initialState);</span><br><span class="line"></span><br><span class="line"> function handleClick() {</span><br><span class="line"> return setCount((count) => {</span><br><span class="line"> return count * 2;</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"> return (</span><br><span class="line"> <div></span><br><span class="line"> <h1>{count}</h1></span><br><span class="line"> <button onClick={handleClick}>X</button></span><br><span class="line"> </div></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h2 id="useReducer"><a href="#useReducer" class="headerlink" title="useReducer"></a>useReducer</h2><h3 id="这个hook存在的价值"><a href="#这个hook存在的价值" class="headerlink" title="这个hook存在的价值"></a>这个hook存在的价值</h3><p>当存在逻辑分支如:<code>+ — * /</code> => 都是为了计算<code>count</code>,也就是<code>count</code>=>多种操作方案,每一种方案可能有很多地方都需要使用。</p><p><code>reducer</code>是一个非常好去解决,集成、对状态修改的方案集合的一种方法。</p><h3 id="useReducer是什么"><a href="#useReducer是什么" class="headerlink" title="useReducer是什么"></a><code>useReducer</code>是什么</h3><h4 id="useReducer含义、包含的概念"><a href="#useReducer含义、包含的概念" class="headerlink" title="useReducer含义、包含的概念"></a>useReducer含义、包含的概念</h4><h5 id="含义"><a href="#含义" class="headerlink" title="含义"></a>含义</h5><p><code>useReducer</code> => 会收集<strong>所有操作</strong>某<strong>一个数据</strong>的方案。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">const [ count , dispatch ] = useReducer(reducer,0)</span><br></pre></td></tr></tbody></table></figure><h5 id="包含的概念"><a href="#包含的概念" class="headerlink" title="包含的概念"></a>包含的概念</h5><h6 id="dispath"><a href="#dispath" class="headerlink" title="dispath"></a>dispath</h6><p><code>dispatch</code>派发器 => 传入的不同操作类型 => 调用不同的逻辑</p><p><code>dispatch({ type, payload })</code></p><p>以下为拿计算器<code>+ - * /</code>为例:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> *</span><br><span class="line"> * @param {*} count --- 初始值</span><br><span class="line"> * @param {*} action --- { type, payload }</span><br><span class="line"> * * type --- 动作类型</span><br><span class="line"> * * payload --- 动作携带的数据</span><br><span class="line"> * @returns</span><br><span class="line"> */</span><br><span class="line">function countReducer(count, { type, payload }) {</span><br><span class="line"> switch (type) {</span><br><span class="line"> case "PLUS":</span><br><span class="line"> return count + payload;</span><br><span class="line"> case "MINUS":</span><br><span class="line"> return count - payload;</span><br><span class="line"> case "TIMES":</span><br><span class="line"> return count * payload;</span><br><span class="line"> case "DIVIDE":</span><br><span class="line"> return count / payload;</span><br><span class="line"> default:</span><br><span class="line"> return count;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="useReducer用法"><a href="#useReducer用法" class="headerlink" title="useReducer用法"></a>useReducer用法</h4><p><code>useReducer(reducer,initialState)</code>接受俩个参数,第一个是<code>reducer</code>的函数,第二个是<code>initialState</code>初始值。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">const [ count , dispatch ] = useReducer(reducer,0)</span><br></pre></td></tr></tbody></table></figure><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line">// usReducer</span><br><span class="line"></span><br><span class="line">export default function Hook() {</span><br><span class="line"> /**</span><br><span class="line"> *</span><br><span class="line"> * @param {*} count --- 初始值</span><br><span class="line"> * @param {*} action --- { type, payload }</span><br><span class="line"> * * type --- 动作类型</span><br><span class="line"> * * payload --- 动作携带的数据</span><br><span class="line"> * @returns</span><br><span class="line"> */</span><br><span class="line"> function countReducer(count, { type, payload }) {</span><br><span class="line"> switch (type) {</span><br><span class="line"> case "PLUS":</span><br><span class="line"> return count + payload;</span><br><span class="line"> case "MINUS":</span><br><span class="line"> return count - payload;</span><br><span class="line"> case "TIMES":</span><br><span class="line"> return count * payload;</span><br><span class="line"> case "DIVIDE":</span><br><span class="line"> return count / payload;</span><br><span class="line"> default:</span><br><span class="line"> return count;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> /**</span><br><span class="line"> * * count --- 状态</span><br><span class="line"> * * dispatch --- 触发动作 --dispatch({ type,payload});</span><br><span class="line"> * * type --- 动作类型</span><br><span class="line"> * * payload --- 动作携带的数据</span><br><span class="line"> * @returns</span><br><span class="line"> */</span><br><span class="line"> const [count, dispatch] = useReducer(countReducer, 0);</span><br><span class="line"></span><br><span class="line"> /**</span><br><span class="line"> * dispatch 触发动作</span><br><span class="line"> * dispatch({ type: "PLUS", payload: 1 });</span><br><span class="line"> */</span><br><span class="line"> function plus() {</span><br><span class="line"> dispatch({ type: "PLUS", payload: 1 });</span><br><span class="line"> }</span><br><span class="line"> function minus() {</span><br><span class="line"> dispatch({ type: "MINUS", payload: 1 });</span><br><span class="line"> }</span><br><span class="line"> function times() {</span><br><span class="line"> dispatch({ type: "TIMES", payload: 2 });</span><br><span class="line"> }</span><br><span class="line"> function divide() {</span><br><span class="line"> dispatch({ type: "DIVIDE", payload: 2 });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <div></span><br><span class="line"> <h1>Count: {count}</h1></span><br><span class="line"> <button onClick={plus}>+</button></span><br><span class="line"> <button onClick={minus}>-</button></span><br><span class="line"> <button onClick={times}>x</button></span><br><span class="line"> <button onClick={divide}>÷</button></span><br><span class="line"> </div></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="useEffect"><a href="#useEffect" class="headerlink" title="useEffect"></a>useEffect</h2><h3 id="含义-1"><a href="#含义-1" class="headerlink" title="含义"></a>含义</h3><p><code>effect</code>—副作用 => 处理视图状态不相关的逻辑</p><p>例如:</p><ul><li>记时器;</li><li><code>console.log()</code>;</li><li>数据获取;</li><li>修改、操作<code>DOM;</code></li></ul><p>在<code>React</code>中所有副作用都必须在<code>useEffect()</code>中执行。</p><p>在组件挂载的时候存在一系列生命周期函数。在函数式组件中使用<code>useEffect()</code>代替类组件中生命周期的函数(简化)。</p><h3 id="作用"><a href="#作用" class="headerlink" title="作用"></a>作用</h3><ul><li><p>手动收集依赖;</p></li><li><p>处理渲染副作用;</p><p>使用一个返回值(返回值)去处理副作用清理</p></li><li><p>代替生命周期函数;</p><p>需要根据传递依赖来去代替哪一个生命周期函数</p></li></ul><h3 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h3><p>回调函数 + 参数:<code>useEffect(callback,depArr)</code></p><p>第二个参数为:<code>depArr</code> => <code> the Array of dependencies</code></p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">useEffect(() => {</span><br><span class="line"> </span><br><span class="line"> // []:`depArr` => ` the Array of dependencies`</span><br><span class="line"> </span><br><span class="line"> // 清除函数</span><br><span class="line"> return(()=>{})</span><br><span class="line">},[])</span><br></pre></td></tr></tbody></table></figure><p>回调函数<code>callback</code>中的逻辑是否执行,是依赖<code>depArr</code>数组里面状态是否改变。因此<code>depArr</code>一定要在外界保存。</p><ul><li><p>如果第二个参数为<code>undefined</code> => 任何状态改变时,都会重新执行。=> <strong>组件更新的生命周期。</strong></p><p>以下代码:每当按钮点击时,<code>count</code>变更时,<code>useEffect()</code>都会执行。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">export default function useEffectHook() {</span><br><span class="line"> const [count, setCount] = useState(0);</span><br><span class="line"> useEffect(() => {</span><br><span class="line"> console.log("useEffect");</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <div>{count}</div></span><br><span class="line"> <button onClick={() => setCount((count) => count + 1)}>+</button></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p>如果第二个参数不是一个数组则 => <strong>报警告。</strong></p><p>当按钮点击时,会报以下<code>warning</code>:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">// react-dom.development.js:86 Warning: useEffect received a final argument that is not an array (instead, received `object`). When specified, the final argument must be an array.</span><br><span class="line"></span><br><span class="line">// Warning: useEffect received a final argument that is not an array (instead, received `object`). When specified, the final argument must be an array.</span><br><span class="line">export default function useEffectHook() {</span><br><span class="line"> const [count, setCount] = useState(0);</span><br><span class="line"> useEffect(() => {</span><br><span class="line"> console.log("useEffect");</span><br><span class="line"> }, {});</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <div>{count}</div></span><br><span class="line"> <button onClick={() => setCount((count) => count + 1)}>+</button></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure></li><li><p>如果第二个参数是一个<strong>数组</strong>,且是<strong>一个空数组</strong> => <strong>回调只会在函数组件调用时执行一次。</strong>(是在根组件执行的时候在执行) => <strong><code>componentDidMount</code></strong></p><p>以下执行,只会执行一次。打印一次。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">export default function useEffectHook() {</span><br><span class="line"> const [count, setCount] = useState(0);</span><br><span class="line"> useEffect(() => {</span><br><span class="line"> console.log("useEffect");</span><br><span class="line"> }, []);</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <div>{count}</div></span><br><span class="line"> <button onClick={() => setCount((count) => count + 1)}>+</button></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p>如果第二个参数是一个有元素的数组 => 元素为状态的话,状态更新,回调重新执行一次。=> <strong><code>componentDidUpdate</code></strong></p><p>以下代码会先执行一次,当按钮点击时,<code>count</code>更新的同时,也会执行打印。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">export default function useEffectHook() {</span><br><span class="line"> const [count, setCount] = useState(0);</span><br><span class="line"> useEffect(() => {</span><br><span class="line"> console.log("useEffect");</span><br><span class="line"> }, [count]);</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <div>{count}</div></span><br><span class="line"> <button onClick={() => setCount((count) => count + 1)}>+</button></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure></li></ul><h3 id="清楚副作用"><a href="#清楚副作用" class="headerlink" title="清楚副作用"></a>清楚副作用</h3><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">useEffect(() => {</span><br><span class="line"> // 当页面卸载时:compoentWillUnmount</span><br><span class="line"> return(()=>{})</span><br><span class="line">},[])</span><br></pre></td></tr></tbody></table></figure><p>例子:清楚记时器</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">export default function useEffectHook() {</span><br><span class="line"> const [count, setCount] = useState(0);</span><br><span class="line"> useEffect(() => {</span><br><span class="line"> let t = setInterval(() => {</span><br><span class="line"> setCount((count) => count + 1);</span><br><span class="line"> }, 1000);</span><br><span class="line"></span><br><span class="line"> return () => {</span><br><span class="line"> clearInterval(t);</span><br><span class="line"> t = null;</span><br><span class="line"> };</span><br><span class="line"> }, []);</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <div>{count}</div></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="对比Vue中watchEffect"><a href="#对比Vue中watchEffect" class="headerlink" title="对比Vue中watchEffect"></a>对比Vue中watchEffect</h3><table><thead><tr><th align="center">不同</th><th align="center">Vue <code>watchEffect</code></th><th align="center">React <code>useEffect</code></th></tr></thead><tbody><tr><td align="center">依赖收集</td><td align="center"><code>watchEffect</code>自动收集,直接执行我们的回调</td><td align="center">需要开发者手动收集</td></tr><tr><td align="center">参数</td><td align="center">没有第二个参数,第二个参数是<code>watchEffect</code>自动收集、提供的</td><td align="center">有第二个参数,需手动追踪依赖</td></tr><tr><td align="center">清楚副作用</td><td align="center">回调不返回任何,<br>清楚通过提供的宏(函数执行)<code>onCleanup(xxx)</code></td><td align="center">通过回调<code> return () => { };</code>去清楚副作用</td></tr><tr><td align="center">设计理念</td><td align="center">观察副作用</td><td align="center">更多去代替生命周期函数</td></tr></tbody></table><p>相同点:<br>都是观察我们的副作用,执行我们的回调</p>]]></content>
<summary type="html"><h1 id="React之Hook篇"><a href="#React之Hook篇" class="headerlink" title="React之Hook篇"></a>React之Hook篇</h1><h2 id="useState"><a href="#useState"</summary>
<category term="前端框架" scheme="https://ayozoo.github.io/categories/%E5%89%8D%E7%AB%AF%E6%A1%86%E6%9E%B6/"/>
<category term="react" scheme="https://ayozoo.github.io/tags/react/"/>
</entry>
<entry>
<title>React入门之添加交互</title>
<link href="https://ayozoo.github.io/2023/11/02/%20React-%E6%B7%BB%E5%8A%A0%E4%BA%A4%E4%BA%92/"/>
<id>https://ayozoo.github.io/2023/11/02/%20React-%E6%B7%BB%E5%8A%A0%E4%BA%A4%E4%BA%92/</id>
<published>2023-11-02T15:21:22.000Z</published>
<updated>2023-11-08T15:31:43.168Z</updated>
<content type="html"><![CDATA[<hr><h1 id="React入门之添加交互"><a href="#React入门之添加交互" class="headerlink" title="React入门之添加交互"></a>React入门之添加交互</h1><p>界面上的控件对随着用户的输入而更新。例如点击按钮切换轮播图的展示。在React中,随着时间变化的数据称为<strong>状态(state)</strong>。可以向任何组件添加状态,<strong>按需去进行更新</strong>。</p><span id="more"></span><h2 id="响应事件"><a href="#响应事件" class="headerlink" title="响应事件"></a>响应事件</h2><h3 id="什么是响应事件"><a href="#什么是响应事件" class="headerlink" title="什么是响应事件"></a>什么是响应事件</h3><p>React允许我们在JSX中添加时间处理程序。事件处理程序是我们自己定义的函数。比如我们界面交互时:点击、悬停、焦点聚焦等交互事件。</p><p>我们在自己的组件中可以定义我们自己的<strong>事件处理程序</strong>。<strong>做法是往我们的组件时间处理程序props指定特定应用的名称</strong>。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">// 定义button组件 通过props接收onclick事件</span><br><span class="line">// {children} 作为插槽接收</span><br><span class="line">function Button({ onClick, children }) {</span><br><span class="line"> return (</span><br><span class="line"> <button onClick={onClick}></span><br><span class="line"> {children}</span><br><span class="line"> </button></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// 定义Toolbar 通过props接收onPlayMovie、onUploadImage事件</span><br><span class="line">// 这里的"Play Movie、 Upload Image" 作为子组件插入</span><br><span class="line">function Toolbar({ onPlayMovie, onUploadImage }) {</span><br><span class="line"> return (</span><br><span class="line"> <div></span><br><span class="line"> <Button onClick={onPlayMovie}></span><br><span class="line"> Play Movie</span><br><span class="line"> </Button></span><br><span class="line"> <Button onClick={onUploadImage}></span><br><span class="line"> Upload Image</span><br><span class="line"> </Button></span><br><span class="line"> </div></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// 导出最终组件</span><br><span class="line">// 这里的onPlayMovie、onUploadImage就是对应Toolbar组件接收的事件处理程序、最终传入Button的onClick事件中</span><br><span class="line">export default function App() {</span><br><span class="line"> return (</span><br><span class="line"> <Toolbar</span><br><span class="line"> onPlayMovie={() => alert('Playing!')}</span><br><span class="line"> onUploadImage={() => alert('Uploading!')}</span><br><span class="line"> /></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="事件处理函数"><a href="#事件处理函数" class="headerlink" title="事件处理函数"></a>事件处理函数</h3><h4 id="添加事件处理函数步骤:"><a href="#添加事件处理函数步骤:" class="headerlink" title="添加事件处理函数步骤:"></a>添加事件处理函数步骤:</h4><ol><li>首先定义一个函数组件</li><li>在函数组件中定义事件处理程序的函数,然后<strong>将其作为prop传入</strong>合适的JSX标签。</li></ol><h4 id="事件函数特点:"><a href="#事件函数特点:" class="headerlink" title="事件函数特点:"></a>事件函数特点:</h4><ul><li><p>通常是在<strong>组件内部</strong>定义</p></li><li><p>名称<code>handle</code>开头,后面跟事件名称</p><p>事件处理函数可以在JSX中有俩种定义方式:</p><ul><li>内联事件处理函数(<em><strong>函数体比较短使用较为方便</strong></em>)</li><li>简洁函数</li></ul></li></ul><p>这时候看个例子:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">export default function Button() {</span><br><span class="line"> function handleClick() {</span><br><span class="line"> alert('你点击了我!');</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <button onClick={handleClick}></span><br><span class="line"> 点我</span><br><span class="line"> </button></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// 简洁箭头函数</span><br><span class="line"> <button onClick={() => {</span><br><span class="line"> alert('你点击了我!');</span><br><span class="line">}}></span><br><span class="line"> </span><br><span class="line">// 内联写法</span><br><span class="line"><button onClick={function handleClick() {</span><br><span class="line"> alert('你点击了我!');</span><br><span class="line">}}></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h4 id="事件处理函数传递的陷阱"><a href="#事件处理函数传递的陷阱" class="headerlink" title="事件处理函数传递的陷阱"></a>事件处理函数传递的陷阱</h4><p>在Vue中会出现事件绑定时,直接触发事件处理程序。在这里存在陷阱。</p><p>传递给事件处理函数的函数应直接传递,而非调用。看下面的例子:</p><table><thead><tr><th align="center"><strong>传递一个函数(正确)</strong></th><th align="center"><strong>调用一个函数(错误)</strong></th></tr></thead><tbody><tr><td align="center"><button onclick="{handleClick}"></button></td><td align="center"><button onclick="{handleClick()}"></button></td></tr></tbody></table><p>这样看来其实,区别很微妙。</p><p>左侧示例中<code>handleClick</code>函数作为<code>onClick</code>的事件处理函数传递。这个是告诉React这个事件是当用户点击按钮时才会触发函数。</p><p>右侧示例中<code>handleClick()</code>中最后的<code>()</code>会在<strong>渲染过程中立即触发函数</strong>,即使没有任何点击。这是因为<strong>JSX<code>{</code>和<code>}</code>之间的Javascript会立即执行</strong>。</p><p>当传入内联函数时,会出现不同的陷阱。</p><table><thead><tr><th><strong>传递一个函数(正确)</strong></th><th><strong>调用一个函数(错误)</strong></th></tr></thead><tbody><tr><td><button onClick={() => alert(‘…’)}></td><td><button onClick={alert(‘…’)}></td></tr></tbody></table><p>右侧的写法,将会导致组件渲染时,每次都触发。</p><p>左侧就是创建了一个稍后调用的函数,而不是在每次渲染时执行其内部的代码。</p><p><strong>综上,就是想要定义内联函数事件处理函数,要将其包装在匿名函数中。</strong></p><h4 id="在事件处理函数中读取props"><a href="#在事件处理函数中读取props" class="headerlink" title="在事件处理函数中读取props"></a>在事件处理函数中读取props</h4><p>事件函数声明于组件内部,因此他们可以直接访问组件的props。</p><p>例如:事件处理函数就可以接收到message</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">function AlertButton({ message, children }) {</span><br><span class="line"> return (</span><br><span class="line"> <button onClick={() => alert(message)}></span><br><span class="line"> {children}</span><br><span class="line"> </button></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">export default function Toolbar() {</span><br><span class="line"> return (</span><br><span class="line"> <div></span><br><span class="line"> <AlertButton message="正在播放!"></span><br><span class="line"> 播放电影</span><br><span class="line"> </AlertButton></span><br><span class="line"> <AlertButton message="正在上传!"></span><br><span class="line"> 上传图片</span><br><span class="line"> </AlertButton></span><br><span class="line"> </div></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h4 id="将事件处理函数作为props传递"><a href="#将事件处理函数作为props传递" class="headerlink" title="将事件处理函数作为props传递"></a>将事件处理函数作为props传递</h4><p>通常,我们会在父组件中定义子组件的事件处理函数。为此将组件从父组件接收的prop作为事件处理函数传递。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line">// 定义一个接收事件处理函数的子组件 </span><br><span class="line">function Button({ onClick, children }) {</span><br><span class="line"> return (</span><br><span class="line"> <button onClick={onClick}></span><br><span class="line"> {children}</span><br><span class="line"> </button></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">// 这里作为父组件</span><br><span class="line">// 定义一个内部事件处理函数</span><br><span class="line">// 作为props传递给button组件</span><br><span class="line">function PlayButton({ movieName }) {</span><br><span class="line"> function handlePlayClick() {</span><br><span class="line"> alert(`正在播放 ${movieName}!`);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <Button onClick={handlePlayClick}></span><br><span class="line"> 播放 "{movieName}"</span><br><span class="line"> </Button></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">function UploadButton() {</span><br><span class="line"> return (</span><br><span class="line"> <Button onClick={() => alert('正在上传!')}></span><br><span class="line"> 上传图片</span><br><span class="line"> </Button></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">export default function Toolbar() {</span><br><span class="line"> return (</span><br><span class="line"> <div></span><br><span class="line"> <PlayButton movieName="魔女宅急便" /></span><br><span class="line"> <UploadButton /></span><br><span class="line"> </div></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="命名事件处理函数prop"><a href="#命名事件处理函数prop" class="headerlink" title="命名事件处理函数prop"></a>命名事件处理函数prop</h4><p>对于浏览器内置组件(<code><button></code> 和 <code><div></code>),仅支持浏览器事件名称,例如,onclick。<strong>但是当我们构建自己的组件时,可以任意命名事件处理函数的prop。</strong></p><p>当组件支持多种交互时,可以根据不同的应用程序命名事件处理函数props。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">// 这里onClick接收的还是浏览器内置的<button>(小写)</span><br><span class="line">// 仍然需要使用 onClick prop,而自定义的 Button 组件接收到的 prop 名称还是可以定义。</span><br><span class="line">function Button({ onSmash, children }) {</span><br><span class="line"> return (</span><br><span class="line"> <button onClick={onSmash}></span><br><span class="line"> {children}</span><br><span class="line"> </button></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">export default function App() {</span><br><span class="line"> return (</span><br><span class="line"> <div></span><br><span class="line"> <Button onSmash={() => alert('正在播放!')}></span><br><span class="line"> 播放电影</span><br><span class="line"> </Button></span><br><span class="line"> <Button onSmash={() => alert('正在上传!')}></span><br><span class="line"> 上传图片</span><br><span class="line"> </Button></span><br><span class="line"> </div></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h4 id="事件传播"><a href="#事件传播" class="headerlink" title="事件传播"></a>事件传播</h4><p><strong>事件处理函数还将捕获来自任何子组件的事件。</strong>通常,我们会说事件沿着树向上“冒泡”或者“传播”:他从事件发生的地方开始,然后沿着树向上传播。</p><p><strong>在React中所有的事件都会传播,除了onScroll,它仅适用于附加到的JSX标签中。</strong></p><p>例如:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">// 当你点击button时,先触发他自身的onClick</span><br><span class="line">// 在执行父级div的onClick</span><br><span class="line">// 但是如果只点击了父级的那么只会触发父级本身的onClick</span><br><span class="line">export default function Toolbar() {</span><br><span class="line"> return (</span><br><span class="line"> <div className="Toolbar" onClick={() => {</span><br><span class="line"> alert('你点击了 toolbar !');</span><br><span class="line"> }}></span><br><span class="line"> <button onClick={() => alert('正在播放!')}></span><br><span class="line"> 播放电影</span><br><span class="line"> </button></span><br><span class="line"> <button onClick={() => alert('正在上传!')}></span><br><span class="line"> 上传图片</span><br><span class="line"> </button></span><br><span class="line"> </div></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="阻止传播"><a href="#阻止传播" class="headerlink" title="阻止传播"></a>阻止传播</h4><p>事件处理函数接收一个**<u>事件对象</u><strong>作为唯一的参数。一般通常被称为<code>e</code>,代表`event(事件)。</strong><u><em>这个可以使用此对象读取事件的有关信息。</em></u>**</p><p>这个事件对象还允许阻止传播。例如:</p><p>当你点击按钮时:</p><ol><li>React 调用了传递给 <code><button></code> 的 <code>onClick</code> 处理函数。</li><li>定义在<button>中的处理函数执行了如下操作:<ul><li>调用 <code>e.stopPropagation()</code>,阻止事件进一步冒泡。</li><li>调用 <code>onClick</code> 函数,它是从 <code>Toolbar</code> 组件传递过来的 prop。</li></ul></button></li><li>在 <code>Toolbar</code> 组件中定义的函数,显示按钮对应的 alert。</li><li>由于传播被阻止,父级 <code><div></code> 的 <code>onClick</code> 处理函数不会执行。</li></ol><p>由于调用了 <code>e.stopPropagation()</code>,点击按钮现在将只显示一个 alert(来自 <code><button></code>),而并非两个(分别来自 <code><button></code> 和父级 toolbar <code><div></code>)。点击按钮与点击周围的 toolbar 不同,因此阻止传播对这个 UI 是有意义的。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">function Button({ onClick, children }) {</span><br><span class="line"> return (</span><br><span class="line"> <button onClick={e => {</span><br><span class="line"> e.stopPropagation();</span><br><span class="line"> onClick();</span><br><span class="line"> }}></span><br><span class="line"> {children}</span><br><span class="line"> </button></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">export default function Toolbar() {</span><br><span class="line"> return (</span><br><span class="line"> <div className="Toolbar" onClick={() => {</span><br><span class="line"> alert('你点击了 toolbar !');</span><br><span class="line"> }}></span><br><span class="line"> <Button onClick={() => alert('正在播放!')}></span><br><span class="line"> 播放电影</span><br><span class="line"> </Button></span><br><span class="line"> <Button onClick={() => alert('正在上传!')}></span><br><span class="line"> 上传图片</span><br><span class="line"> </Button></span><br><span class="line"> </div></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>拓展:</p><p>少数情况下,你可能需要捕获子元素上的所有事件,<strong>即便它们阻止了传播</strong>。例如,你可能想对每次点击进行埋点记录,传播逻辑暂且不论。那么你可以通过在事件名称末尾添加 <code>Capture</code> 来实现这一点:</p><h5 id="onClickCapture捕获所有事件"><a href="#onClickCapture捕获所有事件" class="headerlink" title="onClickCapture捕获所有事件"></a><strong>onClickCapture</strong>捕获所有事件</h5><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><div onClickCapture={() => { /* 这会首先执行 */ }}></span><br><span class="line"> <button onClick={e => e.stopPropagation()} /></span><br><span class="line"> <button onClick={e => e.stopPropagation()} /></span><br><span class="line"></div></span><br></pre></td></tr></tbody></table></figure><p>每个事件分三个阶段传播:</p><ol><li>它向下传播,调用所有的 <code>onClickCapture</code> 处理函数。</li><li>它执行被点击元素的 <code>onClick</code> 处理函数。</li><li>它向上传播,调用所有的 <code>onClick</code> 处理函数。</li></ol><p>捕获事件对于路由或数据分析之类的代码很有用,但你可能不会在应用程序代码中使用它们。</p><h4 id="传递处理函数作为事件传播的代替方案"><a href="#传递处理函数作为事件传播的代替方案" class="headerlink" title="传递处理函数作为事件传播的代替方案"></a>传递处理函数作为事件传播的代替方案</h4><p>看这一段代码</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">function Button({ onClick, children }) {</span><br><span class="line"> return (</span><br><span class="line"> <button onClick={e => {</span><br><span class="line"> e.stopPropagation();</span><br><span class="line"> onClick();</span><br><span class="line"> }}></span><br><span class="line"> {children}</span><br><span class="line"> </button></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>此处的点击事件处理函数先执行了一段代码,<strong>然后</strong>调用了父组件传递的 <code>onClick</code> prop。</p><p>也可以在调用父元素<code>onClick</code>函数之前,添加其他代码。此模式是事件传播的另一种 <strong>替代方案</strong> 。它让子组件处理事件,同时也让父组件指定一些额外的行为。与事件传播不同,它并非自动。但使用这种模式的好处是你可以清楚地追踪因某个事件的触发而执行的整条代码链。</p><p>如果你依赖于事件传播,而且很难追踪哪些处理程序在执行,及其执行的原因,可以尝试这种方法。</p><h4 id="阻止默认行为"><a href="#阻止默认行为" class="headerlink" title="阻止默认行为"></a>阻止默认行为</h4><p>某些浏览器事件具有与事件相关联的默认行为。例如,点击 <code><form></code> 表单内部的按钮会触发表单提交事件,默认情况下将重新加载整个页面。</p><p>可以调用事件对象中的 <code>e.preventDefault()</code> 来阻止这种情况发生:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">export default function Signup() {</span><br><span class="line"> return (</span><br><span class="line"> <form onSubmit={e => {</span><br><span class="line"> e.preventDefault();</span><br><span class="line"> alert('提交表单!');</span><br><span class="line"> }}></span><br><span class="line"> <input /></span><br><span class="line"> <button>发送</button></span><br><span class="line"> </form></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>不要混淆 <code>e.stopPropagation()</code> 和 <code>e.preventDefault()</code>。它们都很有用,但二者并不相关:</p><ul><li><a href="https://developer.mozilla.org/docs/Web/API/Event/stopPropagation"><code>e.stopPropagation()</code></a> 阻止触发绑定在外层标签上的事件处理函数。</li><li><a href="https://developer.mozilla.org/docs/Web/API/Event/preventDefault"><code>e.preventDefault()</code></a> 阻止少数事件的默认浏览器行为。</li></ul><h4 id="事件函数可以包含副作用吗"><a href="#事件函数可以包含副作用吗" class="headerlink" title="事件函数可以包含副作用吗"></a>事件函数可以包含副作用吗</h4><p>当然可以!事件处理函数是执行副作用的最佳位置。</p><p>与渲染函数不同,事件处理函数不需要是 <a href="https://react.docschina.org/learn/keeping-components-pure">纯函数</a>,因此它是用来 <em>更改</em> 某些值的绝佳位置。例如,更改输入框的值以响应键入,或者更改列表以响应按钮的触发。但是,为了更改某些信息,你首先需要某种方式存储它。在 React 中,这是通过 <a href="https://react.docschina.org/learn/state-a-components-memory">state(组件的记忆)</a> 来完成的。</p><h2 id="state:组件的记忆"><a href="#state:组件的记忆" class="headerlink" title="state:组件的记忆"></a>state:组件的记忆</h2><p>组件通常需要根据交互更改屏幕上显示的内容。在我们表单输入的时候应该更新字段、单机轮播图上的点击下一个应该更改的图片。组件需要“记住”这些东西:当前输入值、当前轮播图。在React中,这种组件持有的记忆被称为<code>state</code>。</p><p>当使用普通变量时,事件处理函数会更新局部的变量,但是没有达到预期的效果。</p><p>原因有二:</p><ul><li><strong>局部变量无法在多次渲染中持久化保存。</strong>当React在此渲染这个组件时,他会使用事件处理函数中最初的值重新开始渲染,<strong>他不会考虑之前局部变量的任何修改。</strong></li><li><strong>更新局部变量不会触发渲染。</strong>React没有意识它需要去使用新数据渲染数组。</li></ul><h3 id="如何使用新数据更新组件"><a href="#如何使用新数据更新组件" class="headerlink" title="如何使用新数据更新组件?"></a>如何使用新数据更新组件?</h3><p>需要做俩件事:</p><ol><li><strong>保留</strong>渲染之间的数据</li><li><strong>触发</strong>React使用新数据渲染组件(重新渲染)</li></ol><h3 id="这时候就引出了,主人公:useStateHook。"><a href="#这时候就引出了,主人公:useStateHook。" class="headerlink" title="这时候就引出了,主人公:useStateHook。"></a>这时候就引出了,主人公:<code>useState</code>Hook。</h3><p><code>useState</code>Hook提供了俩个功能:</p><ol><li><strong>State变量</strong>用于保存渲染间的数据。</li><li><strong>State setter函数</strong>更新变量并触发React再次渲染组件。</li></ol><h4 id="使用姿势(如何添加一个state变量)"><a href="#使用姿势(如何添加一个state变量)" class="headerlink" title="使用姿势(如何添加一个state变量)"></a>使用姿势(如何添加一个state变量)</h4><ol><li><p>现在顶部文件React导入<code>useState</code></p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br></pre></td></tr></tbody></table></figure></li><li><p>定义state变量</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">const [index, setIndex] = useState(0);</span><br></pre></td></tr></tbody></table></figure><p><strong>其中index为<code>State</code>变量,<code>setIndex</code>是对应的<code>setter</code>函数。</strong></p><p><strong>这里的 <code>[</code> 和 <code>]</code> 语法称为<a href="https://react.docschina.org/learn/a-javascript-refresher#array-destructuring">数组解构</a>,它允许你从数组中读取值。 <code>useState</code> 返回的数组总是正好有两项。</strong></p></li></ol><p>在React中,<code>useState</code>以及其他以<code>use</code>开头的函数都被称为<strong>Hook</strong></p><p>Hook是特殊的函数,只在React<a href="https://react.docschina.org/learn/render-and-commit#step-1-trigger-a-render">渲染</a>是有效。</p><p><strong><u><em>注意:</em></u></strong></p><p>Hooks,以<code>use</code>开头的函数,只能在组件或者**<a href="https://react.docschina.org/learn/reusing-logic-with-custom-hooks">自定义 Hook</a>** 的最顶层调用。**<u><em>不能在条件语句、循环语句或其他嵌套函数内调用 Hook。</em></u>**Hook是函数。</p><h3 id="深度剖析useState"><a href="#深度剖析useState" class="headerlink" title="深度剖析useState"></a>深度剖析<code>useState</code></h3><p>当调用<code>useState</code>时,是在告诉React你想让组件记住一些东西。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">const [index, setIndex] = useState(0);</span><br></pre></td></tr></tbody></table></figure><p>在这段代码中,我们希望React记住index。</p><p><code>useState</code>的唯一参数是state变量的<strong>初始值</strong>。这个例子中<code>index</code><strong>初始值</strong>被<code>useState(0)</code>设置为0。</p><p>每当我们组件渲染时,<code>useState</code>都会返回一个包含俩个值的数组:</p><ol><li><strong>state变量</strong>(index)会保存上次渲染的值。</li><li><strong>state setter函数</strong>(setIndex)可以更新state变量并触发React重新渲染组件。</li></ol><p>以下是具体的执行顺序:</p><ol><li><strong>组件进行第一次渲染</strong>。会将<code>index</code>初始值0传递给<code>useState</code>,他就会返回<code>[0,setIndex]</code>。这时候React会记住<code>0</code>是最新的值。</li><li><strong>更新了state。</strong>当用户点击按钮的时候,他会调用<code>setIndex(index+1)</code>。index初始值<code>0</code>,所以就会变成<code>setIndex(1)</code>。这将告诉React记住<code>index</code>是<code>1</code>触发下一次渲染。</li><li><strong>组件进行二次渲染。</strong>React仍然看到<code>useState(0)</code>,但是这时候React记住了<code>index</code>设置为<code>1</code>,他将返回<code>[1,setIndex]</code>。</li><li>后续渲染过程如此反复。</li></ol><p>我们可以为一个组件赋予多个state变量。并且这些组件可以拥有任意的多种类型的state变量。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line">import { sculptureList } from './data.js';</span><br><span class="line"></span><br><span class="line">export default function Gallery() {</span><br><span class="line"> const [index, setIndex] = useState(0);</span><br><span class="line"> const [showMore, setShowMore] = useState(false);</span><br><span class="line"></span><br><span class="line"> function handleNextClick() {</span><br><span class="line"> setIndex(index + 1);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> function handleMoreClick() {</span><br><span class="line"> setShowMore(!showMore);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> let sculpture = sculptureList[index];</span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <button onClick={handleNextClick}></span><br><span class="line"> Next</span><br><span class="line"> </button></span><br><span class="line"> <h2></span><br><span class="line"> <i>{sculpture.name} </i> </span><br><span class="line"> by {sculpture.artist}</span><br><span class="line"> </h2></span><br><span class="line"> <h3> </span><br><span class="line"> ({index + 1} of {sculptureList.length})</span><br><span class="line"> </h3></span><br><span class="line"> <button onClick={handleMoreClick}></span><br><span class="line"> {showMore ? 'Hide' : 'Show'} details</span><br><span class="line"> </button></span><br><span class="line"> {showMore && <p>{sculpture.description}</p>}</span><br><span class="line"> <img </span><br><span class="line"> src={sculpture.url} </span><br><span class="line"> alt={sculpture.alt}</span><br><span class="line"> /></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>如果它们不相关,那么存在多个 state 变量是一个好主意,例如本例中的 <code>index</code> 和 <code>showMore</code>。但是,如果你发现经常同时更改两个 state 变量,那么最好将它们合并为一个。例如,如果你有一个包含多个字段的表单,那么有一个值为对象的 state 变量比每个字段对应一个 state 变量更方便。 <a href="https://react.docschina.org/learn/choosing-the-state-structure">选择 state 结构</a>在这方面有更多提示。</p><h4 id="拓展:为什么React如何知道要返回哪个state??"><a href="#拓展:为什么React如何知道要返回哪个state??" class="headerlink" title="拓展:为什么React如何知道要返回哪个state??"></a>拓展:为什么React如何知道要返回哪个state??</h4><p> <a href="https://medium.com/@ryardley/react-hooks-not-magic-just-arrays-cd4f1857236e">React Hooks: not magic, just arrays</a></p><h4 id="State是隔离且私有的"><a href="#State是隔离且私有的" class="headerlink" title="State是隔离且私有的"></a>State是隔离且私有的</h4><p>State是屏幕上组件实例内部的状态。<strong>也就是说每次渲染都会产生完全隔离的state副本!</strong>一个改变会会影响另一个。</p><p>这是因为<code>state</code>与生命在模块顶部的普通变量不同的原因。State不依赖于特定的函数调用或在代码中的位置。<strong>他的作用域“只限于”屏幕上模块特定的区域。重复渲染组件,他们的<code>state</code>是分开存储的。</strong></p><p><strong>state完全私有于声明他的组件。父组件无法更改它。</strong></p><p><strong><u><em>State 变量仅用于在组件重渲染时保存信息。在单个事件处理函数中,普通变量就足够了。当普通变量运行良好时,不要引入 state 变量。</em></u></strong></p><h2 id="渲染和提交"><a href="#渲染和提交" class="headerlink" title="渲染和提交"></a>渲染和提交</h2><p>设想我们是一个厨师,把食材做成美味的菜肴。在这个场景下,React就是一个服务员。这种请求和提供UI的过程分为三步:</p><ol><li><strong>触发</strong>一次渲染(把客人的点单派发厨房)</li><li><strong>渲染</strong>组件(厨房准备订单)</li><li><strong>提交</strong>到DOM(将菜品放到桌子上)</li></ol><h3 id="步骤1:触发一次渲染"><a href="#步骤1:触发一次渲染" class="headerlink" title="步骤1:触发一次渲染"></a>步骤1:触发一次渲染</h3><p>触发渲染的原因有二:</p><ol><li>组件的<strong>初次渲染</strong>。</li><li>组件(或者其祖先之一)的<strong>状态发生了改变</strong></li></ol><h4 id="初次渲染"><a href="#初次渲染" class="headerlink" title="初次渲染"></a>初次渲染</h4><p>当应用启动的时候,会触发初次渲染,框架和沙箱有时候会隐藏这段代码,但是它通过调用目标DOM节点的 <a href="https://react.docschina.org/reference/react-dom/client/createRoot"><code>createRoot</code></a>,然后组件调用<code>render</code>函数完成的。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">import Image from './Image.js';</span><br><span class="line">import { createRoot } from 'react-dom/client';</span><br><span class="line"></span><br><span class="line">const root = createRoot(document.getElementById('root'))</span><br><span class="line">root.render(<Image />);</span><br></pre></td></tr></tbody></table></figure><h4 id="状态更新时重现渲染"><a href="#状态更新时重现渲染" class="headerlink" title="状态更新时重现渲染"></a>状态更新时重现渲染</h4><p>一旦组件被初次渲染,我们就可以通过<a href="https://react.docschina.org/reference/react/useState#setstate"><code>set</code> 函数</a>更新其状态来触发之后的渲染。更新组件的状态会自动将一次渲染送入队列。</p><h3 id="步骤2-React渲染你的组件"><a href="#步骤2-React渲染你的组件" class="headerlink" title="步骤2:React渲染你的组件"></a>步骤2:React渲染你的组件</h3><p>在触发初次渲染之后,React会调用组件的来确定屏幕上渲染显示的内容。<strong>“渲染中”</strong>即React在调用你的组件。</p><ul><li><strong>在初次渲染时</strong>,React会调用根组件。</li><li><strong>对于后续的渲染</strong>,React会调用内部状态更新触发了渲染的函数组件。</li></ul><p>这个过程是递归的:如果更新的组件会返回某个另外组件,那么React接下来就会渲染<em>那个</em>组件,如果哪个组件又返回了某个组件,那么React接下来会渲染<em>那个</em>组件。以此类推,这个过程会持续下去,知道没有更多的嵌套组件并且React确定知道哪些东西应该显示到屏幕上为止。</p><p>我们看个例子,React将会调用<code>Gallery()</code>和<code>Image()</code>若干次。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">// Gallery组件</span><br><span class="line">export default function Gallery() {</span><br><span class="line"> return (</span><br><span class="line"> <section></span><br><span class="line"> <h1>鼓舞人心的雕塑</h1></span><br><span class="line"> <Image /></span><br><span class="line"> <Image /></span><br><span class="line"> <Image /></span><br><span class="line"> </section></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">function Image() {</span><br><span class="line"> return (</span><br><span class="line"> <img</span><br><span class="line"> src="https://i.imgur.com/ZF6s192.jpg"</span><br><span class="line"> alt="'Floralis Genérica' by Eduardo Catalano: a gigantic metallic flower sculpture with reflective petals"</span><br><span class="line"> /></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// app</span><br><span class="line">import Gallery from './Gallery.js';</span><br><span class="line">import { createRoot } from 'react-dom/client';</span><br><span class="line"></span><br><span class="line">const root = createRoot(document.getElementById('root'))</span><br><span class="line">root.render(<Gallery />);</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><ul><li><strong>初次渲染中</strong>,React将会为<code><section></code>、<code><h1></code> 和三个 <code><img></code> 标签<a href="https://developer.mozilla.org/docs/Web/API/Document/createElement">创建 DOM 节点</a></li><li><strong>在一次重渲染过程中,</strong>React将计算它们的那些属性(如果有的话)自上次渲染以来已经更改。再下一步(提交阶段)之前,他不会对这些信息执行任何操作。</li></ul><p><em><strong><u>注意:</u></strong></em></p><p>渲染必须是一次 <a href="https://react.docschina.org/learn/keeping-components-pure">纯计算</a>:</p><ul><li><strong>输入相同,输出相同。</strong>给定相同的输入,组件应该始终返回相同的JSX。就好比,食客点了西红柿沙拉,不应该收到洋葱沙拉!</li><li><strong>只做它们自己的事情。</strong>他不应该更改更改任何存在于渲染之前的对象或者变量。就好比一个订单不应该更改其他任何人的订单。</li></ul><p>否则,随着代码库复杂性的增加,可能会遇到令人困惑的错误和不可预测的行为。在“严格模式‘下开发时,React会调用每个组件函数俩次,这可以检测不纯函数引起的错误。</p><h5 id="性能优化"><a href="#性能优化" class="headerlink" title="性能优化"></a>性能优化</h5><p>如果更新的组件在树中的位置非常高,渲染更新后的组件内部所有嵌套组件的默认行为将不会获得最佳性能。如果你遇到了性能问题,<a href="https://reactjs.org/docs/optimizing-performance.html">性能</a> 章节描述了几种可选的解决方案 。<strong>不要过早进行优化!</strong></p><h3 id="步骤3-React把更改提交到DOM上"><a href="#步骤3-React把更改提交到DOM上" class="headerlink" title="步骤3:React把更改提交到DOM上"></a>步骤3:React把更改提交到DOM上</h3><p>在渲染(调用)你的组件之后,React将会修改DOM</p><ul><li><strong>对于初次渲染,</strong>React会使用<a href="https://developer.mozilla.org/docs/Web/API/Node/appendChild"><code>appendChild()</code></a>DOM API将其创建所有的DOM节点放在屏幕上。</li><li><strong>对于重渲染,</strong>React将应用最少的必要操作(在渲染时计算!),以使得DOM与最新的渲染输出互相匹配。</li></ul><p><strong>React仅在渲染之间存在差异时才会更新DOM节点。</strong></p><p>有一个组件,它每秒使用从父组件传递下来的不同属性重新渲染一次。注意,你可以添加一些文本到 <code><input></code> 标签,更新它的 <code>value</code>,但是文本不会在组件重渲染时消失:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">export default function Clock({ time }) {</span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <h1>{time}</h1></span><br><span class="line"> <input /></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>这个例子之所以会正常运行,是因为在最后一步中,React 只会使用最新的 <code>time</code> 更新 <code><h1></code> 标签的内容。它看到 <code><input></code> 标签出现在 JSX 中与上次相同的位置,因此 React 不会修改 <code><input></code> 标签或它的 <code>value</code>!</p><h3 id="浏览器绘制"><a href="#浏览器绘制" class="headerlink" title="浏览器绘制"></a>浏览器绘制</h3><p>在渲染完成并且React更新DOM之后,浏览器就会重新绘制屏幕。尽管这个过程称为“浏览器渲染”(“browser rendering”),这里还是称为“绘制”(“painting”),以避免在这些文档的其余部分中出现混淆。</p><h2 id="state如同一张快照"><a href="#state如同一张快照" class="headerlink" title="state如同一张快照"></a>state如同一张快照</h2><p>也许state变量看起来就和一般的可读写的JavaScript变量类似。但state在其表现出的特性上更像是一张快照。设置他不会更爱你已有的state变量,但会触发重新渲染。</p><h3 id="设置state会触发渲染"><a href="#设置state会触发渲染" class="headerlink" title="设置state会触发渲染"></a>设置state会触发渲染</h3><p>你可能会认为你的用户界面会直接对点击之类的用户输入做出相应并发生变化。在React中,他的工作方式与这种思维模型略有不同。上一章节我们知道通过设置<code>state</code>请求重新渲染。这就意味着要使界面对输入做出反应,需要使用设置state。</p><p>看个例子:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line"></span><br><span class="line">export default function Form() {</span><br><span class="line"> const [isSent, setIsSent] = useState(false);</span><br><span class="line"> const [message, setMessage] = useState('Hi!');</span><br><span class="line"> if (isSent) {</span><br><span class="line"> return <h1>Your message is on its way!</h1></span><br><span class="line"> }</span><br><span class="line"> return (</span><br><span class="line"> <form onSubmit={(e) => {</span><br><span class="line"> e.preventDefault();</span><br><span class="line"> setIsSent(true);</span><br><span class="line"> sendMessage(message);</span><br><span class="line"> }}></span><br><span class="line"> <textarea</span><br><span class="line"> placeholder="Message"</span><br><span class="line"> value={message}</span><br><span class="line"> onChange={e => setMessage(e.target.value)}</span><br><span class="line"> /></span><br><span class="line"> <button type="submit">Send</button></span><br><span class="line"> </form></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">function sendMessage(message) {</span><br><span class="line"> // ...</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>当单击按钮时会发生以下情况:</p><ol><li>执行 <code>onSubmit</code> 事件处理函数。</li><li><code>setIsSent(true)</code> 将 <code>isSent</code> 设置为 <code>true</code> 并排列一个新的渲染。</li><li>React 根据新的 <code>isSent</code> 值重新渲染组件。</li></ol><h3 id="渲染会及时生出一张快照"><a href="#渲染会及时生出一张快照" class="headerlink" title="渲染会及时生出一张快照"></a>渲染会及时生出一张快照</h3><p><a href="https://react.docschina.org/learn/render-and-commit#step-2-react-renders-your-components">“正在渲染”</a>就意味着React正在调用组件—- 一个函数。你从该函数返回的JSX就像是UI的一张及时的快照。它的props、事件处理函数和内部变量都是<strong>根据当前渲染时的state被计算出来的。</strong></p><p>相较于照片或电影画面不同,你返回的UI“快照”是可交互的。他其中包含着类似事件处理函数的逻辑,这些逻辑对于指定如何输入作出响应。React随后会更新屏幕来匹配这张快照,并绑定事件处理函数。因此,按下按钮即会触发你的JSX的点击事件处理函数。</p><p>当React重新渲染一组件时:</p><ol><li>React会再次调用你的函数</li><li>函数会返回新的JSX快照</li><li>React会更新界面以匹配返回的快照</li></ol><p>作为一个组件的记忆,<code>state</code>不同于在你的函数返回之后就会消失的普通变量。state实际是“活”在React本身– 就像摆在一个架子上!– 位于你的函数之外。当React调用组件时,他会为特定的那一次渲染提供一张state快照。你的组件会在会在JSX中返回一张包含一整套新的props和事件处理函数的UI快照,其中所有的值都是<strong>根据一次渲染中state的值</strong>被计算出来的!</p><p>下面看个例子:</p><p>试想下结果!</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line"></span><br><span class="line">export default function Counter() {</span><br><span class="line"> const [number, setNumber] = useState(0);</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <h1>{number}</h1></span><br><span class="line"> <button onClick={() => {</span><br><span class="line"> setNumber(number + 1);</span><br><span class="line"> setNumber(number + 1);</span><br><span class="line"> setNumber(number + 1);</span><br><span class="line"> }}>+3</button></span><br><span class="line"> </></span><br><span class="line"> )</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p><em>点击按钮后其实发现,每次点击<code>number</code>递增一次!!!</em></p><p><strong>设置state只会为下一次渲染变更state的值。</strong>在第一次渲染期间,<code>number</code>为<code>0</code>。这也解释了为什么在<strong>那次渲染中的</strong> <code>onClick</code> 处理函数中,即便在调用了 <code>setNumber(number + 1)</code> 之后,<code>number</code> 的值也仍然是 <code>0</code>:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">button onClick={() => {</span><br><span class="line"> setNumber(number + 1);</span><br><span class="line"> setNumber(number + 1);</span><br><span class="line"> setNumber(number + 1);</span><br><span class="line">}}>+3</button></span><br></pre></td></tr></tbody></table></figure><p>分析下这个按钮点击事件处理函数通知React要做的事情:</p><ol><li><code>setNumber(number + 1)</code>:<code>number</code> 是 <code>0</code> 所以 <code>setNumber(0 + 1)</code>。<ul><li>React 准备在下一次渲染时将 <code>number</code> 更改为 <code>1</code>。</li></ul></li><li><code>setNumber(number + 1)</code>:<code>number</code> 是<code>0</code> 所以 <code>setNumber(0 + 1)</code>。<ul><li>React 准备在下一次渲染时将 <code>number</code> 更改为 <code>1</code>。</li></ul></li><li><code>setNumber(number + 1)</code>:<code>number</code> 是<code>0</code> 所以 <code>setNumber(0 + 1)</code>。<ul><li>React 准备在下一次渲染时将 <code>number</code> 更改为 <code>1</code>。</li></ul></li></ol><p>尽管调用了三次 <code>setNumber(number + 1)</code>,但是在<strong>这次渲染的</strong>的事件处理函数中<code>number</code>会一直是<code>0</code>,所以你会三次将state设置为<code>1</code>。这就是为什么你在事件处理函数执行完后,React重新渲染的组件中的<code>number</code>等于<code>1</code>而不是<code>3</code></p><p>其实就是可以把state变量放入这次渲染中。由于<strong>这次渲染</strong>中的state变量就是<code>0</code>,其实事件处理函数就是以下这种:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><button onClick={() => {</span><br><span class="line"> setNumber(0 + 1);</span><br><span class="line"> setNumber(0 + 1);</span><br><span class="line"> setNumber(0 + 1);</span><br><span class="line">}}>+3</button></span><br></pre></td></tr></tbody></table></figure><p>所以对于下一次渲染来说,<code>number</code>是<code>1</code>,因此<strong>那次渲染中的</strong>点击事件处理函数就是这样:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><button onClick={() => {</span><br><span class="line"> setNumber(1 + 1);</span><br><span class="line"> setNumber(1 + 1);</span><br><span class="line"> setNumber(1 + 1);</span><br><span class="line">}}>+3</button></span><br></pre></td></tr></tbody></table></figure><p>以上这就是为什么每次都是递增<code>1</code>。</p><h3 id="随着时间变化的state"><a href="#随着时间变化的state" class="headerlink" title="随着时间变化的state"></a>随着时间变化的state</h3><p>来看来段代码:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line"></span><br><span class="line">export default function Counter() {</span><br><span class="line"> const [number, setNumber] = useState(0);</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <h1>{number}</h1></span><br><span class="line"> <button onClick={() => {</span><br><span class="line"> setNumber(number + 5);</span><br><span class="line"> alert(number);</span><br><span class="line"> }}>+5</button></span><br><span class="line"> </></span><br><span class="line"> )</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>以上代码会出现 <code>alert</code>先显示<code>0</code>,页面显示累加后的。也就是<code>alert</code>会先显示上一次的数值,页面在显示。</p><p>在<code>alert</code>加上记时器,使得在组件重新渲染<strong>之后</strong>才触发。又会怎么样呢?</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line"></span><br><span class="line">export default function Counter() {</span><br><span class="line"> const [number, setNumber] = useState(0);</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <h1>{number}</h1></span><br><span class="line"> <button onClick={() => {</span><br><span class="line"> setNumber(number + 5);</span><br><span class="line"> setTimeout(() => {</span><br><span class="line"> alert(number);</span><br><span class="line"> }, 3000);</span><br><span class="line"> }}>+5</button></span><br><span class="line"> </></span><br><span class="line"> )</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>这时候就会发现页面先渲染出累加后的数值,<code>alert</code>才会输出累计后的数值。</p><p>到提示框运行时,React中存储的state可能已经发生了改变,但他是使用用户与之交互时的快照进行调度的!</p><p><strong>一个state变量的值永远不会在一次渲染的内部发生变化</strong>,即使事件处理函数的代码是异步的。在<strong>那个渲染的</strong><code>onClick</code>内部,<code>number</code>的值即使在调用的<code>setNumber(number + 5)</code>之后也是<code>0</code>。它的值是在React通过调用你的组件“获取UI的快照”时就被“固定”了。</p><p><strong>React 会使 state 的值始终”固定“在一次渲染的各个事件处理函数内部。</strong> 你无需担心代码运行时 state 是否发生了变化。</p><h2 id="把一系列state更新加入队列"><a href="#把一系列state更新加入队列" class="headerlink" title="把一系列state更新加入队列"></a>把一系列state更新加入队列</h2><h3 id="React会对state更新进行批处理"><a href="#React会对state更新进行批处理" class="headerlink" title="React会对state更新进行批处理"></a>React会对state更新进行批处理</h3><p>在上节这个示例中,我们发现当按钮点击后,组件页面渲染数值一直是每次累加一次的。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line"></span><br><span class="line">export default function Counter() {</span><br><span class="line"> const [number, setNumber] = useState(0);</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <h1>{number}</h1></span><br><span class="line"> <button onClick={() => {</span><br><span class="line"> setNumber(number + 1);</span><br><span class="line"> setNumber(number + 1);</span><br><span class="line"> setNumber(number + 1);</span><br><span class="line"> }}>+3</button></span><br><span class="line"> </></span><br><span class="line"> )</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p><a href="https://react.docschina.org/learn/state-as-a-snapshot#rendering-takes-a-snapshot-in-time">每一次渲染的 state 值都是固定的</a>,在第一次渲染的事件处理函数内部的<code>number</code>值总是<code>0</code>。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">setNumber(0 + 1);</span><br><span class="line">setNumber(0 + 1);</span><br><span class="line">setNumber(0 + 1);</span><br></pre></td></tr></tbody></table></figure><p>在Reac机制里,<strong>React会等到事件处理函数中的所有代码都运行完毕在处理你的state更新。</strong>这也就是为什么重新渲染只会发生在所有这些<code>setNumber()</code>调用<strong>之后的原因</strong>。</p><p>就好比,点餐时。服务员不会在你说第一道菜的时候,就去厨房下单,而是等你,把菜点完、如有修改修改完后,再一次性去下单。</p><p>这样就可以更新多个state变量–甚至来自多个组件的state变量–而不会触发太多的 <a href="https://react.docschina.org/learn/render-and-commit#re-renders-when-state-updates">重新渲染</a>。这样也意味着只有在我们的事件处理函数以及其中任何代码执行完成<strong>之后</strong>,UI才会更新。这种特性也就是<strong>批处理</strong>,他会使React应用运行得更快。这样也可以帮助我们避免处理只更新了一部分state变量的令人困惑的“半成品”渲染。</p><p><strong>React不会垮<em>多个</em>需要刻意触发的事件(如点击)进行批处理</strong>–每次点击都是单独处理的。React只会在一般来说安全的情况下才进行批处理。例如,如果第一次点击按钮会禁用表单,那么第二次点击就不会再次提交它。</p><h3 id="在下次渲染前读次更新同一个state"><a href="#在下次渲染前读次更新同一个state" class="headerlink" title="在下次渲染前读次更新同一个state"></a>在下次渲染前读次更新同一个state</h3><p>如果想在下次渲染之前多次更新同一个state,我们可以使用<code>setNumber(n => n + 1)</code>这样传入一个根据队列中的前一个state计算下一个state函数,而不是像<code>setNumber(number + 1)</code>这样传入<strong>下一个state的值</strong>。这是告诉React“用state值做某事”而不是仅仅替换它的方法。</p><p>现在尝试递增计数器:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line"></span><br><span class="line">export default function Counter() {</span><br><span class="line"> const [number, setNumber] = useState(0);</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <h1>{number}</h1></span><br><span class="line"> <button onClick={() => {</span><br><span class="line"> setNumber(n => n + 1);</span><br><span class="line"> setNumber(n => n + 1);</span><br><span class="line"> setNumber(n => n + 1);</span><br><span class="line"> }}>+3</button></span><br><span class="line"> </></span><br><span class="line"> )</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>在这里,<code>n => n + 1</code>被称为<strong>更新函数</strong>。当我们给他传递一个state设置函数时:</p><ol><li>React会将此函数加入队列,以便在事件处理函数中的所有其他代码运行后进行处理。</li><li>在下一次渲染期间,React会遍历队列并更新之后的最终state。</li></ol><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">setNumber(n => n + 1);</span><br><span class="line">setNumber(n => n + 1);</span><br><span class="line">setNumber(n => n + 1);</span><br></pre></td></tr></tbody></table></figure><p>以下是React在执行事件处理函数时处理这几行代码的过程:</p><ol><li><code>setNumber(n => n + 1)</code>:<code>n => n + 1</code> 是一个函数。React 将它加入队列。</li><li><code>setNumber(n => n + 1)</code>:<code>n => n + 1</code> 是一个函数。React 将它加入队列。</li><li><code>setNumber(n => n + 1)</code>:<code>n => n + 1</code> 是一个函数。React 将它加入队列。</li></ol><p>在下次渲染期间调用<code>useState</code>时,React会遍历队列。之前的<code>number</code>state的值是<code>0</code>,所以这就是React作为参数<code>n</code>传递给第一个更新函数的值。然后React会获取上一个更新函数的返回值,并将其作为<code>n</code>传递给下一个更新函数,以此类推:</p><table><thead><tr><th align="center"><strong>更新队列</strong></th><th align="center"><strong><code>n</code></strong></th><th align="center"><strong>返回值</strong></th></tr></thead><tbody><tr><td align="center"><code>n => n + 1</code></td><td align="center"><code>0</code></td><td align="center"><code>0 + 1 = 1</code></td></tr><tr><td align="center"><code>n => n + 1</code></td><td align="center"><code>1</code></td><td align="center"><code>1 + 1 = 2</code></td></tr><tr><td align="center"><code>n => n + 1</code></td><td align="center"><code>2</code></td><td align="center"><code>2 + 1 = 3</code></td></tr></tbody></table><h3 id="如果在替换state后更新state会发生什么"><a href="#如果在替换state后更新state会发生什么" class="headerlink" title="如果在替换state后更新state会发生什么"></a>如果在替换state后更新state会发生什么</h3><p>看看下面这个例子,思考下下一次<code>number</code>渲染的值是什么?</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><button onClick={() => {</span><br><span class="line"> setNumber(number + 5);</span><br><span class="line"> setNumber(n => n + 1);</span><br><span class="line">}}></span><br></pre></td></tr></tbody></table></figure><p><strong>实际结果是:每次递增6</strong></p><p>这个事件处理函数告诉React要做的事情:</p><ol><li><code>setNumber(number + 5)</code>:<code>number</code> 为 <code>0</code>,所以 <code>setNumber(0 + 5)</code>。React 将 <em>“替换为 <code>5</code>”</em> 添加到其队列中。</li><li><code>setNumber(n => n + 1)</code>:<code>n => n + 1</code> 是一个更新函数。 React 将 <strong>该函数</strong> 添加到其队列中。</li></ol><p>在下一次渲染期间,React会遍历state队列:</p><table><thead><tr><th>更新队列</th><th><code>n</code></th><th>返回值</th></tr></thead><tbody><tr><td>“替换为 <code>5</code>”</td><td><code>0</code>(未使用)</td><td><code>5</code></td></tr><tr><td><code>n => n + 1</code></td><td><code>5</code></td><td><code>5 + 1 = 6</code></td></tr></tbody></table><p>React会保存<code>6</code>为最终结果并从<code>useState</code>中返回。</p><p>注意:其实这时候就可以发现,<code>setState(x)</code>实际上会像<code>setState(n => x)</code>一样运行,只不过没有使用<code>n</code>!</p><h3 id="如果在更新state后替换state会发生什么"><a href="#如果在更新state后替换state会发生什么" class="headerlink" title="如果在更新state后替换state会发生什么"></a>如果在更新state后替换state会发生什么</h3><p>看看这例子,你认为<code>number</code>在下一次渲染中的值是什么</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><button onClick={() => {</span><br><span class="line"> setNumber(number + 5);</span><br><span class="line"> setNumber(n => n + 1);</span><br><span class="line"> setNumber(42);</span><br><span class="line">}}></span><br></pre></td></tr></tbody></table></figure><p><strong>实际结果:第一次变更为<code>42</code>,后续一直为<code>42</code>。</strong></p><p>以下是 React 在执行事件处理函数时处理这几行代码的过程:</p><ol><li><code>setNumber(number + 5)</code>:<code>number</code> 为 <code>0</code>,所以 <code>setNumber(0 + 5)</code>。React 将 <em>“替换为 <code>5</code>”</em> 添加到其队列中。</li><li><code>setNumber(n => n + 1)</code>:<code>n => n + 1</code> 是一个更新函数。React 将该函数添加到其队列中。</li><li><code>setNumber(42)</code>:React 将 <em>“替换为 <code>42</code>”</em> 添加到其队列中。</li></ol><p>在下一次渲染期间,React 会遍历 state 队列:</p><table><thead><tr><th>更新队列</th><th><code>n</code></th><th>返回值</th></tr></thead><tbody><tr><td>“替换为 <code>5</code>”</td><td><code>0</code>(未使用)</td><td><code>5</code></td></tr><tr><td><code>n => n + 1</code></td><td><code>5</code></td><td><code>5 + 1 = 6</code></td></tr><tr><td>“替换为 <code>42</code>”</td><td><code>6</code>(未使用)</td><td><code>42</code></td></tr></tbody></table><p>然后 React 会保存 <code>42</code> 为最终结果并从 <code>useState</code> 中返回。</p><p>总而言之,以下是我们可以考虑传递给<code>setNumber</code>state设置函数的内容:</p><ul><li><strong>一个更新函数</strong>(例如:<code>n => n + 1</code>)会被添加到队列中。</li><li><strong>任何其他的值</strong>(例如:数字<code>5</code>)会导致“替换为<code>5</code>”被添加到队列中,已经在队列中的内容会被忽略。</li></ul><p>事件处理函数执行完成之后,React将重新触发渲染。再重新渲染期间,React将处理队列。更新函数会在渲染期间执行,因此<strong>更新函数必须是</strong> <strong><a href="https://react.docschina.org/learn/keeping-components-pure">纯函数</a></strong> 并且只返回结果。不要尝试从他内部设置state或者执行其他副作用。在严格模式下,React会更新每个更新函数俩次(但是丢弃第二个结果),以便帮助发现错误。</p><h3 id="命名惯例"><a href="#命名惯例" class="headerlink" title="命名惯例"></a>命名惯例</h3><p>通常使用相应的state变量的第一个字母来命名更新函数的的参数:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">setEnabled(e => !e);</span><br><span class="line">setLastName(ln => ln.reverse());</span><br><span class="line">setFriendCount(fc => fc * 2);</span><br></pre></td></tr></tbody></table></figure><p>另一个常见的惯例是重复使用完整的 state 变量名称,如 <code>setEnabled(enabled => !enabled)</code>,或使用前缀,如 <code>setEnabled(prevEnabled => !prevEnabled)</code>。</p><h2 id="更新state中的对象"><a href="#更新state中的对象" class="headerlink" title="更新state中的对象"></a>更新state中的对象</h2><p>state中可以保存任意类型的<strong>JavaScript</strong>值,包括对象。但是,在修改对象的时候不应该直接修改存放在<strong>React state</strong>中的对象。当我们想更新一个对象时,需要创建一个新的对象(或者将其拷贝一份),然后将state更次为此对象。</p><h3 id="什么是mutation"><a href="#什么是mutation" class="headerlink" title="什么是mutation"></a>什么是mutation</h3><p>我们可以在state中存放任意类型的JavaScript值。</p><p>我们在state中存放的数字、字符串和布尔值,这些类型的值在JavaScript中是不可变(immutable)的,这意味着它们不能被改变或是只读的。这些值可以通过替换它们的值来触发下一次重新渲染。</p><h4 id="state存放数字"><a href="#state存放数字" class="headerlink" title="state存放数字"></a>state存放数字</h4><p>state<code>x</code>从<code>0</code>到<code>5</code>,数字<code>0</code>本身没有发生改变。在JavaScript中,无法对内置的原始值,如数字、字符串和布尔值,进行任何更改。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">const [x,setX] = useState(0)</span><br></pre></td></tr></tbody></table></figure><h4 id="state存放对象"><a href="#state存放对象" class="headerlink" title="state存放对象"></a>state存放对象</h4><p><strong>当我们改变对象本身的内容时,就制造了一个mutation</strong></p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">const [position, setPosition] = useState({ x: 0, y: 0 });</span><br></pre></td></tr></tbody></table></figure><p>严格来说,React state中存放的对象是可变的,但是应该像处理数字、布尔值、字符串一样视为不可变。当要改变的时候,应该考虑去替换它们的值,而不是对他们进行修改。</p><h3 id="将state视为只读的"><a href="#将state视为只读的" class="headerlink" title="将state视为只读的"></a>将state视为只读的</h3><p>在React中我们应该<strong>将所有放在state中的JavaScript对象都视为只读的。</strong></p><p>我们来看这个例子,我们使用存放在state中的对象来表示指针当前的位置。当我们在预览区域触发或移动光标时,红色移动。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line">export default function MovingDot() {</span><br><span class="line"> const [position, setPosition] = useState({</span><br><span class="line"> x: 0,</span><br><span class="line"> y: 0</span><br><span class="line"> });</span><br><span class="line"> return (</span><br><span class="line"> <div</span><br><span class="line"> onPointerMove={e => {</span><br><span class="line"> setPosition({</span><br><span class="line"> x: e.clientX,</span><br><span class="line"> y: e.clientY</span><br><span class="line"> })</span><br><span class="line">}}</span><br><span class="line"> style={{</span><br><span class="line"> position: 'relative',</span><br><span class="line"> width: '100vw',</span><br><span class="line"> height: '100vh',</span><br><span class="line"> }}></span><br><span class="line"> <div style={{</span><br><span class="line"> position: 'absolute',</span><br><span class="line"> backgroundColor: 'red',</span><br><span class="line"> borderRadius: '50%',</span><br><span class="line"> transform: `translate(${position.x}px, ${position.y}px)`,</span><br><span class="line"> left: -10,</span><br><span class="line"> top: -10,</span><br><span class="line"> width: 20,</span><br><span class="line"> height: 20,</span><br><span class="line"> }} /></span><br><span class="line"> </div></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>为了真正 <a href="https://react.docschina.org/learn/state-as-a-snapshot#setting-state-triggers-renders">触发一次重新渲染</a>,<strong>我们需要创建一个新的对象并把它传递给state的设置函数</strong>:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">onPointerMove={e => {</span><br><span class="line"> setPosition({</span><br><span class="line"> x: e.clientX,</span><br><span class="line"> y: e.clientY</span><br><span class="line"> });</span><br><span class="line">}}</span><br></pre></td></tr></tbody></table></figure><p>通过使用<code>setPosition</code>,你在告诉React:</p><ul><li>使用这个新的对象替换<code>positon</code>的值</li><li>然后再次渲染这个组件</li></ul><h4 id="局部的mutation是可以接受的"><a href="#局部的mutation是可以接受的" class="headerlink" title="局部的mutation是可以接受的"></a>局部的mutation是可以接受的</h4><p>像这样的代码是有问题的,因为它改变了state中现有的对象:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">position.x = e.clientX;</span><br><span class="line">position.y = e.clientY;</span><br></pre></td></tr></tbody></table></figure><p>但是像这样的代码就<strong>没有任何问题</strong>,因为改变的是刚刚创建的一个循对象,并将这个对象传递给了state:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">const nextPosition = {};</span><br><span class="line">nextPosition.x = e.clientX;</span><br><span class="line">nextPosition.y = e.clientY;</span><br><span class="line">setPosition(nextPosition);</span><br></pre></td></tr></tbody></table></figure><p>这种写法完全等于这这写法:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">setPosition({</span><br><span class="line"> x: e.clientX,</span><br><span class="line"> y: e.clientY</span><br><span class="line">});</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>只有改变处于state中的<strong>现有</strong>对象时,mutation才会成为问题。而修改一个刚刚创建的对象就不会出现任何问题,因为<strong>还没有其他代码引用它。</strong>改变它并不会意外的影响到其他依赖它的东西。这叫做“局部mutation”。我们也可以在 <a href="https://react.docschina.org/learn/keeping-components-pure#local-mutation-your-components-little-secret">在渲染的过程中</a> 进行“局部mutation”的操作。这种操作既便捷又没有任何问题!</p><h3 id="使用展开语法复制对象"><a href="#使用展开语法复制对象" class="headerlink" title="使用展开语法复制对象"></a>使用展开语法复制对象</h3><p>在之前的例子,都会根据指针的位置创建出一个新的<code>position</code>对象。当我们只需要改变一个属性值的时候,也或者是将<strong>现有</strong>数据作为新对象的一部分。</p><p>看下面的例子,输入框并不会直接正常运行,因为<code>onChange</code>直接修改了state:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line"></span><br><span class="line">export default function Form() {</span><br><span class="line"> const [person, setPerson] = useState({</span><br><span class="line"> firstName: 'Barbara',</span><br><span class="line"> lastName: 'Hepworth',</span><br><span class="line"> email: '[email protected]'</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> function handleFirstNameChange(e) {</span><br><span class="line"> person.firstName = e.target.value;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> function handleLastNameChange(e) {</span><br><span class="line"> person.lastName = e.target.value;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> function handleEmailChange(e) {</span><br><span class="line"> person.email = e.target.value;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <label></span><br><span class="line"> First name:</span><br><span class="line"> <input</span><br><span class="line"> value={person.firstName}</span><br><span class="line"> onChange={handleFirstNameChange}</span><br><span class="line"> /></span><br><span class="line"> </label></span><br><span class="line"> <label></span><br><span class="line"> Last name:</span><br><span class="line"> <input</span><br><span class="line"> value={person.lastName}</span><br><span class="line"> onChange={handleLastNameChange}</span><br><span class="line"> /></span><br><span class="line"> </label></span><br><span class="line"> <label></span><br><span class="line"> Email:</span><br><span class="line"> <input</span><br><span class="line"> value={person.email}</span><br><span class="line"> onChange={handleEmailChange}</span><br><span class="line"> /></span><br><span class="line"> </label></span><br><span class="line"> <p></span><br><span class="line"> {person.firstName}{' '}</span><br><span class="line"> {person.lastName}{' '}</span><br><span class="line"> ({person.email})</span><br><span class="line"> </p></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>以上的,这段代码直接修改了上一次渲染中的state:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">person.firstName = e.target.value;</span><br></pre></td></tr></tbody></table></figure><p>如果我们想在获取<code>firstName</code>的需求,最可靠的办法就是创建一个新的对象将它传递给<code>stePerson</code>。但是在这里,我们还需要将<strong>当前的数据复制到新对象中</strong>,因为我们只改了一个字段。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">setPerson({</span><br><span class="line"> firstName: e.target.value, // 从 input 中获取新的 first name</span><br><span class="line"> lastName: person.lastName,</span><br><span class="line"> email: person.email</span><br><span class="line">});</span><br></pre></td></tr></tbody></table></figure><p>我们也可以使用<code>...</code><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Spread_syntax#spread_in_object_literals">对象展开</a> 语法,这样就不需要单独复制某个属性。这里注意:<strong>新的属性值应该放在最后。</strong></p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">setPerson({</span><br><span class="line"> ...person, // 复制上一个 person 中的所有字段</span><br><span class="line"> firstName: e.target.value // 但是覆盖 firstName 字段 </span><br><span class="line">});</span><br></pre></td></tr></tbody></table></figure><p>这样就可以看来,并没有为每个输入框单独生命一个state。对于大型表单,将所有数据都放在同一个对象中是非常方便的–前提是必须要正确地更新它!</p><p><strong>请注意<code>...</code>展开愈发本质是“浅拷贝”—它只会复制一层。这样就使得它的执行速度很快,这也意味着我们要更新一个嵌套属性时,就必须多次使用展开语法。</strong></p><h4 id="使用一个事件处理函数来更新多个字段"><a href="#使用一个事件处理函数来更新多个字段" class="headerlink" title="使用一个事件处理函数来更新多个字段"></a>使用一个事件处理函数来更新多个字段</h4><p>我们也可以在对象的定义中使用<code>[xxx]</code>括号来实现属性的动态命名。</p><p>看下面这个例子:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line"></span><br><span class="line">export default function Form() {</span><br><span class="line"> const [person, setPerson] = useState({</span><br><span class="line"> firstName: 'Barbara',</span><br><span class="line"> lastName: 'Hepworth',</span><br><span class="line"> email: '[email protected]'</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> function handleChange(e) {</span><br><span class="line"> setPerson({</span><br><span class="line"> ...person,</span><br><span class="line"> [e.target.name]: e.target.value</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <label></span><br><span class="line"> First name:</span><br><span class="line"> <input</span><br><span class="line"> name="firstName"</span><br><span class="line"> value={person.firstName}</span><br><span class="line"> onChange={handleChange}</span><br><span class="line"> /></span><br><span class="line"> </label></span><br><span class="line"> <label></span><br><span class="line"> Last name:</span><br><span class="line"> <input</span><br><span class="line"> name="lastName"</span><br><span class="line"> value={person.lastName}</span><br><span class="line"> onChange={handleChange}</span><br><span class="line"> /></span><br><span class="line"> </label></span><br><span class="line"> <label></span><br><span class="line"> Email:</span><br><span class="line"> <input</span><br><span class="line"> name="email"</span><br><span class="line"> value={person.email}</span><br><span class="line"> onChange={handleChange}</span><br><span class="line"> /></span><br><span class="line"> </label></span><br><span class="line"> <p></span><br><span class="line"> {person.firstName}{' '}</span><br><span class="line"> {person.lastName}{' '}</span><br><span class="line"> ({person.email})</span><br><span class="line"> </p></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>在这里,<code>e.target.name</code> 引用了 <code><input></code> 这个 DOM 元素的 <code>name</code> 属性。</p><h3 id="更新一个嵌套对象"><a href="#更新一个嵌套对象" class="headerlink" title="更新一个嵌套对象"></a>更新一个嵌套对象</h3><p>如果出现了嵌套对象:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">const [person, setPerson] = useState({</span><br><span class="line"> name: 'Niki de Saint Phalle',</span><br><span class="line"> artwork: {</span><br><span class="line"> title: 'Blue Nana',</span><br><span class="line"> city: 'Hamburg',</span><br><span class="line"> image: 'https://i.imgur.com/Sd1AgUOm.jpg',</span><br><span class="line"> }</span><br><span class="line">});</span><br></pre></td></tr></tbody></table></figure><p>如果想要更新 <code>person.artwork.city</code> 的值,用mutation来实现的方法就很好理解了:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">person.artwork.city = 'New Delhi';</span><br></pre></td></tr></tbody></table></figure><p>但是在React中,需要将state是为不可变得!如果要修改<code>city</code>的值,先要创建一个新的<code>artwork</code>对象(其中预先填充上一个<code>artworkd</code>对象中的数据),然后创建一个你的<code>person</code>对象,并且使得其中的<code>artwork</code>属性指向新创建的<code>artwork</code>对象:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">const nextArtwork = { ...person.artwork, city: 'new ork'}</span><br><span class="line">const nextPerson = { ...person, artwork: nextArtwork}</span><br><span class="line">setPerson(nextPerson)</span><br></pre></td></tr></tbody></table></figure><p>或者,也可以写成一个函数调用:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">setPerson({</span><br><span class="line"> ...person, // 复制其它字段的数据 </span><br><span class="line"> artwork: { // 替换 artwork 字段 </span><br><span class="line"> ...person.artwork, // 复制之前 person.artwork 中的数据</span><br><span class="line"> city: 'New Delhi' // 但是将 city 的值替换为 New Delhi!</span><br><span class="line"> }</span><br><span class="line">});</span><br></pre></td></tr></tbody></table></figure><h4 id="对象并非是嵌套的"><a href="#对象并非是嵌套的" class="headerlink" title="对象并非是嵌套的"></a>对象并非是嵌套的</h4><p>下面这个对象从代码上来看是“嵌套的”:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">let obj = {</span><br><span class="line"> name: 'Niki de Saint Phalle',</span><br><span class="line"> artwork: {</span><br><span class="line"> title: 'Blue Nana',</span><br><span class="line"> city: 'Hamburg',</span><br><span class="line"> image: 'https://i.imgur.com/Sd1AgUOm.jpg',</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure><p>其实,思考对象的特性时(key:value),“嵌套”并不是一个非常准确的方式。其实这个对象在运行时,会被解释为:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">let obj1 = {</span><br><span class="line"> title: 'Blue Nana',</span><br><span class="line"> city: 'Hamburg',</span><br><span class="line"> image: 'https://i.imgur.com/Sd1AgUOm.jpg',</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">let obj2 = {</span><br><span class="line"> name: 'Niki de Saint Phalle',</span><br><span class="line"> artwork: obj1</span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure><p>这么看来其实<code>obj1</code>并不处于<code>obj2</code>的内部。同时,下面代码中<code>obj3</code>中的属性也可以指向<code>obj1</code>:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">let obj1 = {</span><br><span class="line"> title: 'Blue Nana',</span><br><span class="line"> city: 'Hamburg',</span><br><span class="line"> image: 'https://i.imgur.com/Sd1AgUOm.jpg',</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">let obj2 = {</span><br><span class="line"> name: 'Niki de Saint Phalle',</span><br><span class="line"> artwork: obj1</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">let obj3 = {</span><br><span class="line"> name: 'Copycat',</span><br><span class="line"> artwork: obj1</span><br><span class="line">};</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>如果你直接修改 <code>obj3.artwork.city</code>,就会同时影响 <code>obj2.artwork.city</code> 和 <code>obj1.city</code>。这是因为 <code>obj3.artwork</code>、<code>obj2.artwork</code> 和 <code>obj1</code> 都指向同一个对象。当你用“嵌套”的方式看待对象时,很难看出这一点。相反,它们是相互独立的对象,只不过是用属性“指向”彼此而已。</p><h3 id="使用Immer编写简介的更新逻辑"><a href="#使用Immer编写简介的更新逻辑" class="headerlink" title="使用Immer编写简介的更新逻辑"></a>使用Immer编写简介的更新逻辑</h3><p>如果state有多层的嵌套,应该考虑将 <a href="https://react.docschina.org/learn/choosing-the-state-structure#avoid-deeply-nested-state">将其扁平化</a>。但是同时,不想改拜年state的数据结构,我们可以使用<a href="https://github.com/immerjs/use-immer">Immer</a> 这个库,他可以让你使用简便但是可以直接修改的语法编写代码,并会处理好复制的过程。通过使用 Immer,你写出的代码看起来就像是你“打破了规则”而直接修改了对象:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">updatePerson(draft => {</span><br><span class="line"> draft.artwork.city = 'Lagos';</span><br><span class="line">});</span><br></pre></td></tr></tbody></table></figure><p>不同于一般的mutation,他并不会覆盖之前的state!</p><h4 id="Immer-是如何运行的?"><a href="#Immer-是如何运行的?" class="headerlink" title="Immer 是如何运行的?"></a>Immer 是如何运行的?</h4><p>由 Immer 提供的 <code>draft</code> 是一种特殊类型的对象,被称为 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy">Proxy</a>,它会记录你用它所进行的操作。这就是你能够随心所欲地直接修改对象的原因所在!从原理上说,Immer 会弄清楚 <code>draft</code> 对象的哪些部分被改变了,并会依照你的修改创建出一个全新的对象。</p><h4 id="如何使用Immer"><a href="#如何使用Immer" class="headerlink" title="如何使用Immer"></a>如何使用Immer</h4><p>尝试使用 Immer:</p><ol><li>运行 <code>npm install use-immer</code> 添加 Immer 依赖</li><li>用 <code>import { useImmer } from 'use-immer'</code> 替换掉 <code>import { useState } from 'react'</code></li></ol><p>看以下例子:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br></pre></td><td class="code"><pre><span class="line">import { useImmer } from 'use-immer';</span><br><span class="line"></span><br><span class="line">export default function Form() {</span><br><span class="line"> const [person, updatePerson] = useImmer({</span><br><span class="line"> name: 'Niki de Saint Phalle',</span><br><span class="line"> artwork: {</span><br><span class="line"> title: 'Blue Nana',</span><br><span class="line"> city: 'Hamburg',</span><br><span class="line"> image: 'https://i.imgur.com/Sd1AgUOm.jpg',</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> function handleNameChange(e) {</span><br><span class="line"> updatePerson(draft => {</span><br><span class="line"> draft.name = e.target.value;</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> function handleTitleChange(e) {</span><br><span class="line"> updatePerson(draft => {</span><br><span class="line"> draft.artwork.title = e.target.value;</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> function handleCityChange(e) {</span><br><span class="line"> updatePerson(draft => {</span><br><span class="line"> draft.artwork.city = e.target.value;</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> function handleImageChange(e) {</span><br><span class="line"> updatePerson(draft => {</span><br><span class="line"> draft.artwork.image = e.target.value;</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <label></span><br><span class="line"> Name:</span><br><span class="line"> <input</span><br><span class="line"> value={person.name}</span><br><span class="line"> onChange={handleNameChange}</span><br><span class="line"> /></span><br><span class="line"> </label></span><br><span class="line"> <label></span><br><span class="line"> Title:</span><br><span class="line"> <input</span><br><span class="line"> value={person.artwork.title}</span><br><span class="line"> onChange={handleTitleChange}</span><br><span class="line"> /></span><br><span class="line"> </label></span><br><span class="line"> <label></span><br><span class="line"> City:</span><br><span class="line"> <input</span><br><span class="line"> value={person.artwork.city}</span><br><span class="line"> onChange={handleCityChange}</span><br><span class="line"> /></span><br><span class="line"> </label></span><br><span class="line"> <label></span><br><span class="line"> Image:</span><br><span class="line"> <input</span><br><span class="line"> value={person.artwork.image}</span><br><span class="line"> onChange={handleImageChange}</span><br><span class="line"> /></span><br><span class="line"> </label></span><br><span class="line"> <p></span><br><span class="line"> <i>{person.artwork.title}</i></span><br><span class="line"> {' by '}</span><br><span class="line"> {person.name}</span><br><span class="line"> <br /></span><br><span class="line"> (located in {person.artwork.city})</span><br><span class="line"> </p></span><br><span class="line"> <img </span><br><span class="line"> src={person.artwork.image} </span><br><span class="line"> alt={person.artwork.title}</span><br><span class="line"> /></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>这样,事件处理函数就变得简洁了。可以随意在一个组件中同时使用 <code>useState</code> 和 <code>useImmer</code>。如果你、想要写出更简洁的更新处理函数,Immer 会是一个不错的选择,尤其是当你的 state 中有嵌套,并且复制对象会带来重复的代码时。</p><h2 id="更新state中的数组"><a href="#更新state中的数组" class="headerlink" title="更新state中的数组"></a>更新state中的数组</h2><p>数组也是另外一种可以存储在state中的JavaScript对象,数组本身是可变的,但是应该视为不可变。同对象一样,如果想要更新存储于state中的数组时,需要创建一个新的数组(或者创意一份已有数组的拷贝值),并使用新数组设置state。</p><h3 id="在没有mutation的前提下更新数组"><a href="#在没有mutation的前提下更新数组" class="headerlink" title="在没有mutation的前提下更新数组"></a>在没有mutation的前提下更新数组</h3><p>在JavaScript中,数组是另一种对象。<a href="https://react.docschina.org/learn/updating-objects-in-state">同对象一样</a>,<strong>需要将React state中的数值是为只读的。</strong>这意味着不应该使用类似于 <code>arr[0] = 'bird'</code>这样的方式来修改数组中的元素,也不<strong>应该使用会直接修改原数组的方法</strong>,例如<code>push()</code>和<code>pop()</code>。</p><p>相反,在需要更新一个数组时,需要将一个<strong>新的数组</strong>传入state中的setting方法中。为此我们可以通过 <code>filter()</code> 和 <code>map()</code> 这样不会直接修改原始值的方法,从原始数组生成一个新的数组。然后你就可以将 state 设置为这个新生成的数组。</p><p>以下是常见数组操作的api。当操作React state中的数组时,应该避免左列的方法,而选择右列的方法。</p><table><thead><tr><th></th><th align="center">避免使用 <br>(会改变原始数组)</th><th align="center"><strong>推荐使用<br> (会返回一个新数组)</strong></th></tr></thead><tbody><tr><td>添加元素</td><td align="center"><code>push</code>,<code>unshift</code></td><td align="center"><code>concat</code>,<code>[...arr]</code> 展开语法(<a href="https://react.docschina.org/learn/updating-arrays-in-state#adding-to-an-array">例子</a>)</td></tr><tr><td>删除元素</td><td align="center"><code>pop</code>,<code>shift</code>,<code>splice</code></td><td align="center"><code>filter</code>,<code>slice</code>(<a href="https://react.docschina.org/learn/updating-arrays-in-state#removing-from-an-array">例子</a>)</td></tr><tr><td>替换元素</td><td align="center"><code>splice</code>,<code>arr[i] = ...</code> 赋值</td><td align="center"><code>map</code>(<a href="https://react.docschina.org/learn/updating-arrays-in-state#replacing-items-in-an-array">例子</a>)</td></tr><tr><td>排序</td><td align="center"><code>reverse</code>,<code>sort</code></td><td align="center">先将数组复制一份(<a href="https://react.docschina.org/learn/updating-arrays-in-state#making-other-changes-to-an-array">例子</a>)</td></tr></tbody></table><p>或者,可以使用<a href="https://react.docschina.org/learn/updating-arrays-in-state#write-concise-update-logic-with-immer">使用 Immer</a> ,这样就可以使用表格中的所有方法了。</p><p>注意:</p><p><code>slice</code>和<code>splice</code>方法作用不同</p><ul><li><code>slice</code>可以拷贝数组或者数组的一部分。</li><li><code>splice</code><strong>会直接修改</strong>原始数组(插入或者删除元素)</li></ul><h3 id="向数组中添加元素"><a href="#向数组中添加元素" class="headerlink" title="向数组中添加元素"></a>向数组中添加元素</h3><p>如果利用<code>push()</code>会直接修改原始数组,而这个不是我们期望的。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line"></span><br><span class="line">let nextId = 0;</span><br><span class="line"></span><br><span class="line">export default function List() {</span><br><span class="line"> const [name, setName] = useState('');</span><br><span class="line"> const [artists, setArtists] = useState([]);</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <h1>振奋人心的雕塑家们:</h1></span><br><span class="line"> <input</span><br><span class="line"> value={name}</span><br><span class="line"> onChange={e => setName(e.target.value)}</span><br><span class="line"> /></span><br><span class="line"> <button onClick={() => {</span><br><span class="line"> artists.push({</span><br><span class="line"> id: nextId++,</span><br><span class="line"> name: name,</span><br><span class="line"> });</span><br><span class="line"> }}>添加</button></span><br><span class="line"> <ul></span><br><span class="line"> {artists.map(artist => (</span><br><span class="line"> <li key={artist.id}>{artist.name}</li></span><br><span class="line"> ))}</span><br><span class="line"> </ul></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>以上例子中,我们应该创建一个<strong>新的数组</strong>,其包含了原始数组中的所有元素<strong>以及</strong>一个在末尾添加的新元素。可以有很多种办法实现。最简单的一种就是使用 <code>...</code> <a href="https://react.docschina.org/a-javascript-refresher#array-spread">数组展开</a> 语法:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">setArtists( // 替换 state</span><br><span class="line"> [ // 是通过传入一个新数组实现的</span><br><span class="line"> ...artists, // 新数组包含原数组的所有元素</span><br><span class="line"> { id: nextId++, name: name } // 并在末尾添加了一个新的元素</span><br><span class="line"> ]</span><br><span class="line">);</span><br></pre></td></tr></tbody></table></figure><p>还可以通过展开运算符将新添的元素放在原始的<code>...artists</code>之前:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">setArtists([</span><br><span class="line"> { id: nextId++, name: name },</span><br><span class="line"> ...artists // 将原数组中的元素放在末尾</span><br><span class="line">]);</span><br></pre></td></tr></tbody></table></figure><p>这样看来,展开运算符就可以完成<code>push()</code>和<code>unshift()</code>的效果。</p><h3 id="从数组中删除元素"><a href="#从数组中删除元素" class="headerlink" title="从数组中删除元素"></a>从数组中删除元素</h3><p>从数组中删除元素最简单的方法就是将它<strong>过滤出去</strong>。可以通过<code>filter</code>方法实现:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line"></span><br><span class="line">let initialArtists = [</span><br><span class="line"> { id: 0, name: 'Marta Colvin Andrade' },</span><br><span class="line"> { id: 1, name: 'Lamidi Olonade Fakeye'},</span><br><span class="line"> { id: 2, name: 'Louise Nevelson'},</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line">export default function List() {</span><br><span class="line"> const [artists, setArtists] = useState(</span><br><span class="line"> initialArtists</span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <h1>振奋人心的雕塑家们:</h1></span><br><span class="line"> <ul></span><br><span class="line"> {artists.map(artist => (</span><br><span class="line"> <li key={artist.id}></span><br><span class="line"> {artist.name}{' '}</span><br><span class="line"> <button onClick={() => {</span><br><span class="line"> setArtists(</span><br><span class="line"> artists.filter(a =></span><br><span class="line"> a.id !== artist.id</span><br><span class="line"> )</span><br><span class="line"> );</span><br><span class="line"> }}></span><br><span class="line"> 删除</span><br><span class="line"> </button></span><br><span class="line"> </li></span><br><span class="line"> ))}</span><br><span class="line"> </ul></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>点击“删除”按钮几次,每次都会触发</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">setArtists(</span><br><span class="line"> artists.filter(a => a.id !== artist.id)</span><br><span class="line">);</span><br></pre></td></tr></tbody></table></figure><p>与<code>push()</code>不同的是这里的<code>filter()</code>表示<strong>“创建一个新的数组”</strong>。也就是生成了新的数组,通过state更新,并触发渲染。但是<code>filter()</code>不会改变原数组。</p><h3 id="转换数组"><a href="#转换数组" class="headerlink" title="转换数组"></a>转换数组</h3><p>如果想改变数组中的某些或者所有元素。使用<code>map()</code>创建一个<strong>新数组</strong>。传入的<code>map</code>函数据定了根据每个元素或者索引(或二者都作为条件)对元素进行处理。</p><p>在下面的例子中,一个数组记录了两个圆形和一个正方形的坐标。当你点击按钮时,仅有两个圆形会向下移动 100 像素。这是通过使用 <code>map()</code> 生成一个新数组实现的。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line"></span><br><span class="line">let initialShapes = [</span><br><span class="line"> { id: 0, type: 'circle', x: 50, y: 100 },</span><br><span class="line"> { id: 1, type: 'square', x: 150, y: 100 },</span><br><span class="line"> { id: 2, type: 'circle', x: 250, y: 100 },</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line">export default function ShapeEditor() {</span><br><span class="line"> const [shapes, setShapes] = useState(</span><br><span class="line"> initialShapes</span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> function handleClick() {</span><br><span class="line"> const nextShapes = shapes.map(shape => {</span><br><span class="line"> if (shape.type === 'square') {</span><br><span class="line"> // 不作改变</span><br><span class="line"> return shape;</span><br><span class="line"> } else {</span><br><span class="line"> // 返回一个新的圆形,位置在下方 50px 处</span><br><span class="line"> return {</span><br><span class="line"> ...shape,</span><br><span class="line"> y: shape.y + 50,</span><br><span class="line"> };</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> // 使用新的数组进行重渲染</span><br><span class="line"> setShapes(nextShapes);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <button onClick={handleClick}></span><br><span class="line"> 所有圆形向下移动!</span><br><span class="line"> </button></span><br><span class="line"> {shapes.map(shape => (</span><br><span class="line"> <div</span><br><span class="line"> key={shape.id}</span><br><span class="line"> style={{</span><br><span class="line"> background: 'purple',</span><br><span class="line"> position: 'absolute',</span><br><span class="line"> left: shape.x,</span><br><span class="line"> top: shape.y,</span><br><span class="line"> borderRadius:</span><br><span class="line"> shape.type === 'circle'</span><br><span class="line"> ? '50%' : '',</span><br><span class="line"> width: 20,</span><br><span class="line"> height: 20,</span><br><span class="line"> }} /></span><br><span class="line"> ))}</span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h3 id="替换数组中的元素"><a href="#替换数组中的元素" class="headerlink" title="替换数组中的元素"></a>替换数组中的元素</h3><p>如果存在替换数组中或者多个元素需求的话。类似 <code>arr[0] = 'bird'</code> 这样的赋值语句会直接修改原始数组,所以在这种情况下,我们也应该使用 <code>map</code>。</p><p>要替换一个元素,请使用 <code>map</code> 创建一个新数组。在你的 <code>map</code> 回调里,第二个参数是元素的索引。使用索引来判断最终是返回原始的元素(即回调的第一个参数)还是替换成其他值:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line"></span><br><span class="line">let initialCounters = [</span><br><span class="line"> 0, 0, 0</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line">export default function CounterList() {</span><br><span class="line"> const [counters, setCounters] = useState(</span><br><span class="line"> initialCounters</span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> function handleIncrementClick(index) {</span><br><span class="line"> const nextCounters = counters.map((c, i) => {</span><br><span class="line"> if (i === index) {</span><br><span class="line"> // 递增被点击的计数器数值</span><br><span class="line"> return c + 1;</span><br><span class="line"> } else {</span><br><span class="line"> // 其余部分不发生变化</span><br><span class="line"> return c;</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> setCounters(nextCounters);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <ul></span><br><span class="line"> {counters.map((counter, i) => (</span><br><span class="line"> <li key={i}></span><br><span class="line"> {counter}</span><br><span class="line"> <button onClick={() => {</span><br><span class="line"> handleIncrementClick(i);</span><br><span class="line"> }}>+1</button></span><br><span class="line"> </li></span><br><span class="line"> ))}</span><br><span class="line"> </ul></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h3 id="向数组中插入元素"><a href="#向数组中插入元素" class="headerlink" title="向数组中插入元素"></a>向数组中插入元素</h3><p>有时候也会存在向数组特定位置插入一个元素,这个位置既不在开头,又不在末尾。这时候就可以使用数组展开运算符 <code>...</code> 和 <code>slice()</code> 方法一起使用。<code>slice()</code> 方法可以从数组中切出“一片”。为了将元素插入数组,需要先展开原数组在插入点之前的切片,然后插入新元素,最后展开原数组中剩下的部分。</p><p>下面的例子中,插入按钮总是会将元素插入到数组中索引为 <code>1</code> 的位置。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line"></span><br><span class="line">let nextId = 3;</span><br><span class="line">const initialArtists = [</span><br><span class="line"> { id: 0, name: 'Marta Colvin Andrade' },</span><br><span class="line"> { id: 1, name: 'Lamidi Olonade Fakeye'},</span><br><span class="line"> { id: 2, name: 'Louise Nevelson'},</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line">export default function List() {</span><br><span class="line"> const [name, setName] = useState('');</span><br><span class="line"> const [artists, setArtists] = useState(</span><br><span class="line"> initialArtists</span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> function handleClick() {</span><br><span class="line"> const insertAt = 1; // 可能是任何索引</span><br><span class="line"> const nextArtists = [</span><br><span class="line"> // 插入点之前的元素:</span><br><span class="line"> ...artists.slice(0, insertAt),</span><br><span class="line"> // 新的元素:</span><br><span class="line"> { id: nextId++, name: name },</span><br><span class="line"> // 插入点之后的元素:</span><br><span class="line"> ...artists.slice(insertAt)</span><br><span class="line"> ];</span><br><span class="line"> setArtists(nextArtists);</span><br><span class="line"> setName('');</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <h1>振奋人心的雕塑家们:</h1></span><br><span class="line"> <input</span><br><span class="line"> value={name}</span><br><span class="line"> onChange={e => setName(e.target.value)}</span><br><span class="line"> /></span><br><span class="line"> <button onClick={handleClick}></span><br><span class="line"> 插入</span><br><span class="line"> </button></span><br><span class="line"> <ul></span><br><span class="line"> {artists.map(artist => (</span><br><span class="line"> <li key={artist.id}>{artist.name}</li></span><br><span class="line"> ))}</span><br><span class="line"> </ul></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h3 id="其他改变数组情况"><a href="#其他改变数组情况" class="headerlink" title="其他改变数组情况"></a>其他改变数组情况</h3><p>还会存在,依靠展开运算符和<code>map()</code>或者<code>filter()</code>等不回直接修改原值的方法无法做到。例如:翻转数组、数组排序。而因为JavaScript中的 <code>reverse()</code> 和 <code>sort()</code> 方法会改变原数组,所以你无法直接使用它们。</p><p><strong>然而我们可以先拷贝这个数组,在改变这个拷贝后的值</strong></p><p>例如:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line"></span><br><span class="line">let nextId = 3;</span><br><span class="line">const initialList = [</span><br><span class="line"> { id: 0, title: 'Big Bellies' },</span><br><span class="line"> { id: 1, title: 'Lunar Landscape' },</span><br><span class="line"> { id: 2, title: 'Terracotta Army' },</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line">export default function List() {</span><br><span class="line"> const [list, setList] = useState(initialList);</span><br><span class="line"></span><br><span class="line"> function handleClick() {</span><br><span class="line"> const nextList = [...list];</span><br><span class="line"> nextList.reverse();</span><br><span class="line"> setList(nextList);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <button onClick={handleClick}></span><br><span class="line"> 翻转</span><br><span class="line"> </button></span><br><span class="line"> <ul></span><br><span class="line"> {list.map(artwork => (</span><br><span class="line"> <li key={artwork.id}>{artwork.title}</li></span><br><span class="line"> ))}</span><br><span class="line"> </ul></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>在这里,我们现使用展开运算符进行了原数组的拷贝。有了这个拷贝值,就可以直接通过<code>nextList.reverse()</code> 或 <code>nextList.sort()</code> 这样直接修改原数组的方法。甚至可以通过 <code>nextList[0] = "something"</code> 这样的方式对数组中的特定元素进行赋值。</p><p><strong>即使拷贝了数组,还是不能直接修改其内部的元素</strong>。这是因为数组的拷贝是浅拷贝–新的数组中依然保留了与原始数组相同的元素。</p><p>因此,修改了拷贝数组内部的某个对象,其实就是直接在修改当前的state。如下面的代码;</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">const nextList = [...list];</span><br><span class="line">nextList[0].seen = true; // 问题:直接修改了 list[0] 的值</span><br><span class="line">setList(nextList);</span><br></pre></td></tr></tbody></table></figure><p>虽然 <code>nextList</code> 和 <code>list</code> 是两个不同的数组,**<code>nextList[0]</code> 和 <code>list[0]</code> 却指向了同一个对象**。因此,通过改变 <code>nextList[0].seen</code>,<code>list[0].seen</code> 的值也被改变了。这是一种 state 的 mutation 操作,你应该避免这么做!可以用类似于 <a href="https://react.docschina.org/learn/updating-objects-in-state#updating-a-nested-object">更新嵌套的 JavaScript 对象</a> 的方式解决这个问题——拷贝想要修改的特定元素,而不是直接修改它。下面是具体的操作。</p><h3 id="更新数组内部的对象"><a href="#更新数组内部的对象" class="headerlink" title="更新数组内部的对象"></a>更新数组内部的对象</h3><p><strong>对象并不是真的位于数组“内部”</strong>。可能在代码层面上来看像是在“内部”,但其实际数组中的每个对象都是这个数组“指向”的一个存储其他位置的值。所以在处理<code>list[0]</code>嵌套字段的时候需要格外小心,其他元素的值kennel也指向了数组的同一个元素。</p><p><strong>当更新一个嵌套的 state 时,需要从想要更新的地方创建拷贝值,一直这样,直到顶层。</strong></p><p>在下面的例子中,两个不同的艺术品清单有着相同的初始 state。他们本应该互不影响,但是因为一次 mutation,他们的 state 被意外地共享了,勾选一个清单中的事项会影响另外一个清单:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line"></span><br><span class="line">let nextId = 3;</span><br><span class="line">const initialList = [</span><br><span class="line"> { id: 0, title: 'Big Bellies', seen: false },</span><br><span class="line"> { id: 1, title: 'Lunar Landscape', seen: false },</span><br><span class="line"> { id: 2, title: 'Terracotta Army', seen: true },</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line">export default function BucketList() {</span><br><span class="line"> const [myList, setMyList] = useState(initialList);</span><br><span class="line"> const [yourList, setYourList] = useState(</span><br><span class="line"> initialList</span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> function handleToggleMyList(artworkId, nextSeen) {</span><br><span class="line"> const myNextList = [...myList];</span><br><span class="line"> const artwork = myNextList.find(</span><br><span class="line"> a => a.id === artworkId</span><br><span class="line"> );</span><br><span class="line"> artwork.seen = nextSeen;</span><br><span class="line"> setMyList(myNextList);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> function handleToggleYourList(artworkId, nextSeen) {</span><br><span class="line"> const yourNextList = [...yourList];</span><br><span class="line"> const artwork = yourNextList.find(</span><br><span class="line"> a => a.id === artworkId</span><br><span class="line"> );</span><br><span class="line"> artwork.seen = nextSeen;</span><br><span class="line"> setYourList(yourNextList);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <h1>艺术愿望清单</h1></span><br><span class="line"> <h2>我想看的艺术清单:</h2></span><br><span class="line"> <ItemList</span><br><span class="line"> artworks={myList}</span><br><span class="line"> onToggle={handleToggleMyList} /></span><br><span class="line"> <h2>你想看的艺术清单:</h2></span><br><span class="line"> <ItemList</span><br><span class="line"> artworks={yourList}</span><br><span class="line"> onToggle={handleToggleYourList} /></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">function ItemList({ artworks, onToggle }) {</span><br><span class="line"> return (</span><br><span class="line"> <ul></span><br><span class="line"> {artworks.map(artwork => (</span><br><span class="line"> <li key={artwork.id}></span><br><span class="line"> <label></span><br><span class="line"> <input</span><br><span class="line"> type="checkbox"</span><br><span class="line"> checked={artwork.seen}</span><br><span class="line"> onChange={e => {</span><br><span class="line"> onToggle(</span><br><span class="line"> artwork.id,</span><br><span class="line"> e.target.checked</span><br><span class="line"> );</span><br><span class="line"> }}</span><br><span class="line"> /></span><br><span class="line"> {artwork.title}</span><br><span class="line"> </label></span><br><span class="line"> </li></span><br><span class="line"> ))}</span><br><span class="line"> </ul></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>其实问题就是出现在</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">const myNextList = [...myList];</span><br><span class="line">const artwork = myNextList.find(a => a.id === artworkId);</span><br><span class="line">artwork.seen = nextSeen; // 问题:直接修改了已有的元素</span><br><span class="line">setMyList(myNextList);</span><br></pre></td></tr></tbody></table></figure><p>虽然复制出来了新的数组<code>myNextList</code>,但是其<strong>内部的元素本身</strong>与原数组<code>myList</code>是相同的。因此修改了新数组<code>myNextList</code>中 <code>artwork.seen</code>,其实是在修改<strong>原始的</strong> artwork 对象。而这个对象也在<code>youList</code>中使用,这样就导致存在问题。</p><p><strong>其实可以使用<code>map()</code>在没有mutation的前提下将一个旧的元素替换成新的更新版本。</strong></p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">setMyList(myList.map(artwork => {</span><br><span class="line"> if (artwork.id === artworkId) {</span><br><span class="line"> // 创建包含变更的*新*对象</span><br><span class="line"> return { ...artwork, seen: nextSeen };</span><br><span class="line"> } else {</span><br><span class="line"> // 没有变更</span><br><span class="line"> return artwork;</span><br><span class="line"> }</span><br><span class="line">}));</span><br></pre></td></tr></tbody></table></figure><p>此处的 <code>...</code> 是一个对象展开语法,被用来<a href="https://react.docschina.org/learn/updating-objects-in-state#copying-objects-with-the-spread-syntax">创建一个对象的拷贝</a>.</p><p><strong>通常来讲,我们应该只修改刚刚创建的对象。</strong>如果正在插入一个<strong>新</strong>的元素,可以修改它,但是如果想改变state中已经存在的东西,就需要先拷贝一份了。</p><h3 id="使用Immer编写简洁的更新逻辑"><a href="#使用Immer编写简洁的更新逻辑" class="headerlink" title="使用Immer编写简洁的更新逻辑"></a>使用Immer编写简洁的更新逻辑</h3><p>在没有 mutation 的前提下更新嵌套数组可能会变得有点重复。<a href="https://react.docschina.org/learn/updating-objects-in-state#write-concise-update-logic-with-immer">就像对对象一样</a>:</p><ul><li>通常情况下,我们应该不需要更新处于非常深层级的 state 。如果有此类需求,或许需要<a href="https://react.docschina.org/learn/choosing-the-state-structure#avoid-deeply-nested-state">调整一下数据的结构</a>,让数据变得扁平一些。</li><li>如果不想改变 state 的数据结构,使用 <a href="https://github.com/immerjs/use-immer">Immer</a> ,它可以让我们继续使用方便的,但会直接修改原值的语法,并负责生成拷贝值。</li></ul><p>下面是,使用Immer改写的例子</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line">import { useState } from 'react';</span><br><span class="line">import { useImmer } from 'use-immer';</span><br><span class="line"></span><br><span class="line">let nextId = 3;</span><br><span class="line">const initialList = [</span><br><span class="line"> { id: 0, title: 'Big Bellies', seen: false },</span><br><span class="line"> { id: 1, title: 'Lunar Landscape', seen: false },</span><br><span class="line"> { id: 2, title: 'Terracotta Army', seen: true },</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line">export default function BucketList() {</span><br><span class="line"> const [myList, updateMyList] = useImmer(</span><br><span class="line"> initialList</span><br><span class="line"> );</span><br><span class="line"> const [yourList, updateYourList] = useImmer(</span><br><span class="line"> initialList</span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> function handleToggleMyList(id, nextSeen) {</span><br><span class="line"> updateMyList(draft => {</span><br><span class="line"> const artwork = draft.find(a =></span><br><span class="line"> a.id === id</span><br><span class="line"> );</span><br><span class="line"> artwork.seen = nextSeen;</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> function handleToggleYourList(artworkId, nextSeen) {</span><br><span class="line"> updateYourList(draft => {</span><br><span class="line"> const artwork = draft.find(a =></span><br><span class="line"> a.id === artworkId</span><br><span class="line"> );</span><br><span class="line"> artwork.seen = nextSeen;</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <h1>艺术愿望清单</h1></span><br><span class="line"> <h2>我想看的艺术清单:</h2></span><br><span class="line"> <ItemList</span><br><span class="line"> artworks={myList}</span><br><span class="line"> onToggle={handleToggleMyList} /></span><br><span class="line"> <h2>你想看的艺术清单:</h2></span><br><span class="line"> <ItemList</span><br><span class="line"> artworks={yourList}</span><br><span class="line"> onToggle={handleToggleYourList} /></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">function ItemList({ artworks, onToggle }) {</span><br><span class="line"> return (</span><br><span class="line"> <ul></span><br><span class="line"> {artworks.map(artwork => (</span><br><span class="line"> <li key={artwork.id}></span><br><span class="line"> <label></span><br><span class="line"> <input</span><br><span class="line"> type="checkbox"</span><br><span class="line"> checked={artwork.seen}</span><br><span class="line"> onChange={e => {</span><br><span class="line"> onToggle(</span><br><span class="line"> artwork.id,</span><br><span class="line"> e.target.checked</span><br><span class="line"> );</span><br><span class="line"> }}</span><br><span class="line"> /></span><br><span class="line"> {artwork.title}</span><br><span class="line"> </label></span><br><span class="line"> </li></span><br><span class="line"> ))}</span><br><span class="line"> </ul></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>请注意当使用 Immer 时,<strong>类似 <code>artwork.seen = nextSeen</code> 这种会产生 mutation 的语法不会再有任何问题了:</strong></p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">updateMyTodos(draft => {</span><br><span class="line"> const artwork = draft.find(a => a.id === artworkId);</span><br><span class="line"> artwork.seen = nextSeen;</span><br><span class="line">});</span><br></pre></td></tr></tbody></table></figure><p>这是因为并不是在修改原始的state,而是在修改Immer提供的特殊<code>draft</code>对象。同理,也可以为<code>draft</code>的内容使用<code>push()``pop()</code>这些会直接修改原值的方法。</p><p>幕后,Immer 总是会根据你对 <code>draft</code> 的修改来从头开始构建下一个 state。这使得你的事件处理程序非常的简洁,同时也不会直接修改 state。</p>]]></content>
<summary type="html"><hr>
<h1 id="React入门之添加交互"><a href="#React入门之添加交互" class="headerlink" title="React入门之添加交互"></a>React入门之添加交互</h1><p>界面上的控件对随着用户的输入而更新。例如点击按钮切换轮播图的展示。在React中,随着时间变化的数据称为<strong>状态(state)</strong>。可以向任何组件添加状态,<strong>按需去进行更新</strong>。</p></summary>
<category term="前端框架" scheme="https://ayozoo.github.io/categories/%E5%89%8D%E7%AB%AF%E6%A1%86%E6%9E%B6/"/>
<category term="react" scheme="https://ayozoo.github.io/tags/react/"/>
</entry>
<entry>
<title>React入门之描述UI</title>
<link href="https://ayozoo.github.io/2023/10/31/%20React-%E6%8F%8F%E8%BF%B0UI/"/>
<id>https://ayozoo.github.io/2023/10/31/%20React-%E6%8F%8F%E8%BF%B0UI/</id>
<published>2023-10-31T15:50:22.000Z</published>
<updated>2023-11-08T15:30:05.380Z</updated>
<content type="html"><![CDATA[<hr><h1 id="React"><a href="#React" class="headerlink" title="React"></a>React</h1><p>React应用就是被组件的独立UI片段构建,React组件本质就是可以添加任意标签的JavaScript函数。</p><span id="more"></span><h2 id="React组件的基本要素"><a href="#React组件的基本要素" class="headerlink" title="React组件的基本要素"></a>React组件的基本要素</h2><ul><li>基本的组件js文件存在要素:<br>1、可以return出存在任意标签的JavaScript函数<br>2、函数中需要存在return(xxx) html结构<br>3、最终需要将渲染的组件export出去</li></ul><figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Profile</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">img</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">src</span>=<span class="string">"https://i.imgur.com/MK3eW3As.jpg"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">alt</span>=<span class="string">"Katherine Johnson"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Gallery</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">section</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">h1</span>></span>Amazing scientists<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Profile</span> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Profile</span> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Profile</span> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">section</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h2 id="React-组件导入导出"><a href="#React-组件导入导出" class="headerlink" title="React 组件导入导出"></a>React 组件导入导出</h2><ul><li>组件导入导出语法</li></ul><table><thead><tr><th>语法</th><th>导出语句</th><th>导入语句</th></tr></thead><tbody><tr><td>默认</td><td>export default function Button() {}</td><td>import Button from ‘./Button.js’;</td></tr><tr><td>具名</td><td>export function Button() {}</td><td>import { Button } from ‘./Button.js’</td></tr></tbody></table><ul><li>组件导入导出案例解释</li></ul><ol><li>Gallery.js</li></ol><ul><li>定义了Profile组件</li><li>该组件采用默认导出(export default function)</li></ul><ol start="2"><li>app.js</li></ol><ul><li>使用默认导入,导入Gallery文件的组件Gallery</li><li>使用默认导出方式将app组件导出</li></ul><figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">## app.<span class="property">js</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">Gallery</span> <span class="keyword">from</span> <span class="string">'./Gallery.js'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">App</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">Gallery</span> /></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">## <span class="title class_">Gallery</span>.<span class="property">js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Profile</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">img</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">src</span>=<span class="string">"https://i.imgur.com/QIrZWGIs.jpg"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">alt</span>=<span class="string">"Alan L. Hart"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Gallery</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">section</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">h1</span>></span>了不起的科学家们<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Profile</span> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Profile</span> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Profile</span> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">section</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h2 id="JSX书写标签语言"><a href="#JSX书写标签语言" class="headerlink" title="JSX书写标签语言"></a>JSX书写标签语言</h2><h3 id="什么是JSX"><a href="#什么是JSX" class="headerlink" title="什么是JSX"></a>什么是JSX</h3><p>JSX 是 JavaScript 语法扩展,可以让在 JavaScript 文件中书写类似 HTML 的标签。JSX 看起来和 HTML 很像,但它的语法更加严格并且可以动态展示信息。</p><h3 id="为什么出现JSX"><a href="#为什么出现JSX" class="headerlink" title="为什么出现JSX"></a>为什么出现JSX</h3><p>随着Web交互性越来越强,逻辑开始决定页面的内容。也就是可以说JavaScript负责HTML内容。因此,在React中,渲染逻辑和标签存在同一个组件文件中。</p><h3 id="使用JSX的优势"><a href="#使用JSX的优势" class="headerlink" title="使用JSX的优势"></a>使用JSX的优势</h3><p>例如将一个按钮的渲染逻辑和标签放在一起,可以确保他们在编辑的时候保持同步。反之,也可以说彼此无关的细节是隔离的。</p><h3 id="JSX与HTML的区别"><a href="#JSX与HTML的区别" class="headerlink" title="JSX与HTML的区别"></a>JSX与HTML的区别</h3><p>每个 React 组件都是一个 JavaScript 函数,它会返回一些标签,React 会将这些标签渲染到浏览器上。React 组件使用一种被称为 JSX 的语法扩展来描述这些标签。JSX对于HTML来说,语法更加严格并且可以动态的展示信息。</p><p><strong>JSX and React 是相互独立的东西。通常配合使用,也可以单独使用它们中的任意一个,JSX 是一种语法扩展,而 React 则是一个 JavaScript 的库。</strong> </p><h3 id="HTML转换为JSX"><a href="#HTML转换为JSX" class="headerlink" title="HTML转换为JSX"></a>HTML转换为JSX</h3><p>现在存在一个html标签</p><figure class="highlight html"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">h1</span>></span>海蒂·拉玛的待办事项<span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"><span class="tag"><<span class="name">img</span> </span></span><br><span class="line"><span class="tag"> <span class="attr">src</span>=<span class="string">"https://i.imgur.com/yXOvdOSs.jpg"</span> </span></span><br><span class="line"><span class="tag"> <span class="attr">alt</span>=<span class="string">"Hedy Lamarr"</span> </span></span><br><span class="line"><span class="tag"> <span class="attr">class</span>=<span class="string">"photo"</span></span></span><br><span class="line"><span class="tag">></span></span><br><span class="line"><span class="tag"><<span class="name">ul</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>发明一种新式交通信号灯</span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>排练一个电影场景</span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>改进频谱技术</span><br><span class="line"><span class="tag"></<span class="name">ul</span>></span></span><br></pre></td></tr></tbody></table></figure><p>此时需要将它转化为React组件,可以这么做,是的没错。 采用 *** <> 或者其他标签*** 包裹。</p><figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">TodoList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">h1</span>></span>海蒂·拉玛的待办事项<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">img</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">src</span>=<span class="string">"https://i.imgur.com/yXOvdOSs.jpg"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">alt</span>=<span class="string">"Hedy Lamarr"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">className</span>=<span class="string">"photo"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>发明一种新式交通信号灯<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>排练一个电影场景<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>改进频谱技术<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></></span></span></span><br></pre></td></tr></tbody></table></figure><h3 id="JSX语法规则"><a href="#JSX语法规则" class="headerlink" title="JSX语法规则"></a>JSX语法规则</h3><ul><li><p>只能返回一个根元素</p><ol><li>就是return()中必须只有一个根元素</li><li>不想在标签中增加一个额外的 <div>,可以用 <> 和 </> 元素来代替</div></li></ol></li><li><p>标签必须闭合</p></li><li><p>使用驼峰式命名法给大部分属性命名!</p><p><em><strong><u>JSX 最终会被转化为 JavaScript,而 JSX 中的属性也会变成 JavaScript 对象中的键值对。在组件中,经常会遇到需要用变量的方式读取这些属性的时候。但 JavaScript 对变量的命名有限制。例如,变量名称不能包含 - 符号或者像 class 这样的保留字。</u></strong></em></p><p><u><em><strong>JSX 虽然看起来很像 HTML,但在底层其实被转化为了 JavaScript 对象,你不能在一个函数中返回多个对象,除非用一个数组把他们包装起来。这就是为什么多个 JSX 标签必须要用一个父元素或者 Fragment 来包裹。</strong></em></u></p></li></ul><h4 id="JSX中通过大括号使用javaScript"><a href="#JSX中通过大括号使用javaScript" class="headerlink" title="JSX中通过大括号使用javaScript"></a>JSX中通过大括号使用javaScript</h4><p>JSX可以在JavaScript中编写类似HTML的标签,保证渲染逻辑和内容绑定。在需要在标签中添加Js逻辑或者引用标签的动态属性的时候可以在JSX的大括号里面编写Js。</p><h4 id="JSX使用引号字符串"><a href="#JSX使用引号字符串" class="headerlink" title="JSX使用引号字符串"></a>JSX使用引号字符串</h4><ul><li><p>当你想将字符串属性传递给JSX时,放在<em><strong>单引号</strong></em>或者<em><strong>双引号</strong></em>中</p></li><li><p>当你想动态传值时,可以使用{ 和 } 替代 “ 和 “</p></li></ul><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 字符串</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Avatar</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">img</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">className</span>=<span class="string">"avatar"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">src</span>=<span class="string">"https://i.imgur.com/7vQD0fPs.jpg"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">alt</span>=<span class="string">"Gregorio Y. Zara"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 变量</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Avatar</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">const</span> avatar = <span class="string">'https://i.imgur.com/7vQD0fPs.jpg'</span>;</span><br><span class="line"> <span class="keyword">const</span> description = <span class="string">'Gregorio Y. Zara'</span>;</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">img</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">className</span>=<span class="string">"avatar"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">src</span>=<span class="string">{avatar}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">alt</span>=<span class="string">{description}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p><u><strong>注意</strong></u>:当使用src={ } 这种写法会去读取花括号中js中这个变量的值。</p><h4 id="标签插值"><a href="#标签插值" class="headerlink" title="标签插值"></a>标签插值</h4><ul><li><p>JSX中允许在标签中插入大括号<code>{}</code>中使用变量</p></li><li><p>JSX中允许在标签中插入大括号<code>{}</code>中使用函数表达式</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 变量</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">TodoList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">const</span> name = <span class="string">'Gregorio Y. Zara'</span>;</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">h1</span>></span>{name}'s To Do List<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 函数表达式</span></span><br><span class="line"><span class="keyword">const</span> today = <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">formatDate</span>(<span class="params">date</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Intl</span>.<span class="title class_">DateTimeFormat</span>(</span><br><span class="line"> <span class="string">'zh-CN'</span>,</span><br><span class="line"> { <span class="attr">weekday</span>: <span class="string">'long'</span> }</span><br><span class="line"> ).<span class="title function_">format</span>(date);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">TodoList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">h1</span>></span>To Do List for {formatDate(today)}<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h4 id="大括号的使用场景"><a href="#大括号的使用场景" class="headerlink" title="大括号的使用场景"></a>大括号的使用场景</h4><p>主要场景是字符串、数字、变量、和js表达式。</p><ul><li>用作JSX标签*<u><strong>内部的文本</strong></u>*:<code><h1>{name}'s To Do List</h1></code></li><li>标签***=*<strong>后面紧跟的</strong><u><em>属性</em></u>**:<code>src={avatar}</code> 会读取 <code>avatar</code> 变量,但是!!!<u><em>对于<code>src="{avatar}"只会传一个字符串{avatar}</code></em></u></li></ul><h4 id="双大括号的使用场景"><a href="#双大括号的使用场景" class="headerlink" title="双大括号的使用场景"></a>双大括号的使用场景</h4><p>双大括号其实是<code>{}</code>传递对象的传递方式。</p><ul><li>对象也用大括号表示,例如 <code>{ name: "Hedy Lamarr", inventions: 5 }</code>。因此,为了能在 JSX 中传递,必须用另一对额外的大括号包裹对象:<code>person={{ name: "Hedy Lamarr", inventions: 5 }}</code>。</li><li>也可使用嵌套对象,在jsx大括号中使用。</li></ul><p><u><em><strong>注意:</strong></em></u>内联 <code>style</code> 属性 使用驼峰命名法编写。例如,HTML <code><ul style="background-color: black"></code> 在你的组件里应该写成 <code><ul style={{ backgroundColor: 'black' }}></code>。</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 对象传递</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">TodoList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">ul</span> <span class="attr">style</span>=<span class="string">{{</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">backgroundColor:</span> '<span class="attr">black</span>',</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">color:</span> '<span class="attr">pink</span>'</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> }}></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Improve the videophone<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Prepare aeronautics lectures<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Work on the alcohol-fuelled engine<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 嵌套对象传递</span></span><br><span class="line"><span class="keyword">const</span> person = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'Gregorio Y. Zara'</span>,</span><br><span class="line"> <span class="attr">theme</span>: {</span><br><span class="line"> <span class="attr">backgroundColor</span>: <span class="string">'black'</span>,</span><br><span class="line"> <span class="attr">color</span>: <span class="string">'pink'</span></span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">TodoList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">{person.theme}</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">h1</span>></span>{person.name}'s Todos<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">img</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">className</span>=<span class="string">"avatar"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">src</span>=<span class="string">"https://i.imgur.com/7vQD0fPs.jpg"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">alt</span>=<span class="string">"Gregorio Y. Zara"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Improve the videophone<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Prepare aeronautics lectures<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Work on the alcohol-fuelled engine<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="Props组件传递"><a href="#Props组件传递" class="headerlink" title="Props组件传递"></a>Props组件传递</h4><p>React组件使用<em>Props</em>互相通信。每个父组件都可以提供props给他的自组件传递信息。可以通过Props传递认识JavaScript值,包括对象、数组、和函数。</p><p>Props传递类似HTML预定义的属性。像组件传递的时候可以传递任意的props。传递的props可以通过不同的方式进行渲染。</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 其中person就是props</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">其中的花括号也就是props传递对象而已</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1、传递</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Profile</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">Avatar</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">person</span>=<span class="string">{{</span> <span class="attr">name:</span> '<span class="attr">Lin</span> <span class="attr">Lanying</span>', <span class="attr">imageId:</span> '<span class="attr">1bX5QH6</span>' }}</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">size</span>=<span class="string">{100}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2、使用/读取</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">1.以下这种方式是采用解构,将person的props解构为单独的props传递</span></span><br><span class="line"><span class="comment">2.prop可以存在默认值,例如下面的prop size</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Avatar</span>(<span class="params">{ person, size=<span class="number">100</span> }</span>) {</span><br><span class="line"> <span class="comment">// 在这里 person 和 size 是可访问的</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>Props的使用,必须要关注你的父子组件。需要考虑好父组件需要向自组件传的props,而子组件需要考虑传递的props通过什么方式去进行渲染。事实上,**<u><em>就可以总结出来,React函数组件就是接受一个参数,那就是Props对象。</em></u>**</p><h5 id="Props使用妙计"><a href="#Props使用妙计" class="headerlink" title="Props使用妙计"></a>Props使用妙计</h5><ul><li><p>prop可以指定默认值</p><p>如果渲染时不存在size的prop,那么size将被赋值100进行渲染</p><p>其实就是size的prop属性不存在或者值undefined时会生效</p><p><u><em><strong>但是,如果传递<code>size={null}</code> 或 <code>size={0}</code>,默认值将 不 被使用。</strong></em></u></p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Avatar</span>(<span class="params">{ person, size = <span class="number">100</span> }</span>) {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p>可以使用JSX展开语法传递Props</p><p>会存在需要传递pros很多,需要声明prop传递</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Profile</span>(<span class="params">{ person, size, isSepia, thickBorder }</span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">className</span>=<span class="string">"card"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Avatar</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">person</span>=<span class="string">{person}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">size</span>=<span class="string">{size}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isSepia</span>=<span class="string">{isSepia}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">thickBorder</span>=<span class="string">{thickBorder}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"> );</span><br></pre></td></tr></tbody></table></figure><p>如果存在将所有的props传递给子组件可以采用简洁展开语法,这种写法,将父组件需要传递的props全部传递。<strong>如果滥用,那么建议拆分组件。</strong></p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Profile</span>(<span class="params">props</span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">className</span>=<span class="string">"card"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Avatar</span> {<span class="attr">...props</span>} /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p>将JSX作为子组件传递</p><p>类似HTML标签嵌套,以下例子在父组件card中将children组件作为prop传递,类似vue插槽</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// app.js</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">Avatar</span> <span class="keyword">from</span> <span class="string">'./Avatar.js'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Card</span>(<span class="params">{ children }</span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">className</span>=<span class="string">"card"</span>></span></span></span><br><span class="line"><span class="language-xml"> {children}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Profile</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">Card</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Avatar</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">size</span>=<span class="string">{100}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">person</span>=<span class="string">{{</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name:</span> '<span class="attr">Katsuko</span> <span class="attr">Saruhashi</span>',</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">imageId:</span> '<span class="attr">YfeOqp2</span>'</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> }}</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">Card</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Avatar.js</span></span><br><span class="line"><span class="keyword">import</span> { getImageUrl } <span class="keyword">from</span> <span class="string">'./utils.js'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Avatar</span>(<span class="params">{ person, size }</span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">img</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">className</span>=<span class="string">"avatar"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">src</span>=<span class="string">{getImageUrl(person)}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">alt</span>=<span class="string">{person.name}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">width</span>=<span class="string">{size}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">height</span>=<span class="string">{size}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// utils.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">getImageUrl</span>(<span class="params">person, size = <span class="string">'s'</span></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="string">'https://i.imgur.com/'</span> +</span><br><span class="line"> person.<span class="property">imageId</span> +</span><br><span class="line"> size +</span><br><span class="line"> <span class="string">'.jpg'</span></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h5 id="注意"><a href="#注意" class="headerlink" title="注意"></a>注意</h5><p><strong>一个组件可能会随着时间的推移收到不同的 props。</strong> Props 并不总是静态的!Props 反映了组件在任何时间点的数据,并不仅仅是在开始时。</p><p>然而,props 是 <a href="https://en.wikipedia.org/wiki/Immutable_object">不可变的</a>(一个计算机科学术语,意思是“不可改变”)。当一个组件需要改变它的 props(例如,响应用户交互或新数据)时,它不得不“请求”它的父组件传递 <strong>不同的 props</strong> —— 一个新对象!它的旧 props 将被丢弃,最终 JavaScript 引擎将回收它们占用的内存。<strong>不要尝试“更改 props”。</strong> </p><h4 id="条件渲染"><a href="#条件渲染" class="headerlink" title="条件渲染"></a>条件渲染</h4><p>组件中会存在很多根据条件渲染的内容。在React中可以使用js中的<code>if</code> 语句、<code>&&</code> 和 <code>? :</code> 运算符来选择性地渲染 JSX。</p><h5 id="如何根据条件返回JSX"><a href="#如何根据条件返回JSX" class="headerlink" title="如何根据条件返回JSX"></a>如何根据条件返回JSX</h5><p>根据组件Prop传入的属性值来动态展示内容</p><ul><li>使用 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/if...else">if/else 语句</a> 去判断</li></ul><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//其中isPacked是组件接受的Prop</span></span><br><span class="line"><span class="comment">// 用来动态显示 不同li内容的展示</span></span><br><span class="line"><span class="comment">// 可以选择 return 的组件为null 那么他将不会显示任何内容</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Item</span>(<span class="params">{ name, isPacked }</span>) {</span><br><span class="line"> <span class="keyword">if</span> (isPacked) {</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span>{name} ✔<span class="tag"></<span class="name">li</span>></span></span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span>{name}<span class="tag"></<span class="name">li</span>></span></span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">PackingList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">section</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">h1</span>></span>Sally Ride 的行李清单<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{true}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"宇航服"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{true}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"带金箔的头盔"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{false}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"Tam 的照片"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">section</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li><p>选择性包含JSX</p><p>一般的不同条件会return不同的HTML片段,此刻可以选择包含JSX的书写,一般使用以下运算符:</p><h6 id="1、三目运算符(-)"><a href="#1、三目运算符(-)" class="headerlink" title="1、三目运算符(? :)"></a>1、三目运算符(<code>? :</code>)</h6><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (isPacked) {</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span>{name} ✔<span class="tag"></<span class="name">li</span>></span></span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span>{name}<span class="tag"></<span class="name">li</span>></span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 以上代码使用三目运算符修改为</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span></span></span><br><span class="line"><span class="language-xml"> {isPacked ? name + ' ✔' : name}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line">);</span><br></pre></td></tr></tbody></table></figure><p>**<u><em>注意:</em></u>**简单的内容展示适合使用三目这种运算符来展示,如果展示的逻辑较为复杂,可选择提取自组件来渲染。</p><h6 id="2、与运算符(-amp-amp-)"><a href="#2、与运算符(-amp-amp-)" class="headerlink" title="2、与运算符(&&)"></a>2、与运算符(<code>&&</code>)</h6><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 当 JavaScript && 表达式 的左侧(我们的条件)为 true 时,它则返回其右侧的值(在我们的例子里是勾选符号)。但条件的结果是 false,则整个表达式会变成 false。在 JSX 里,React 会将 false 视为一个“空值”,就像 null 或者 undefined,这样 React 就不会在这里进行任何渲染</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span></span></span><br><span class="line"><span class="language-xml"> {name} {isPacked && '✔'}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line">);</span><br></pre></td></tr></tbody></table></figure><p><u><em>注意:</em></u><strong>切勿将数字放在 <code>&&</code> 左侧.</strong></p><p>JavaScript 会<u><em><strong>自动将左侧的值转换成布尔类型</strong></em></u>以判断条件成立与否。然而,如果左侧是 <code>0</code>,整个表达式将变成左侧的值(<code>0</code>),React 此时则会渲染 <code>0</code> 而不是不进行渲染。</p><p>例如,一个常见的错误是 <code>messageCount && <p>New messages</p></code>。其原本是想当 <code>messageCount</code> 为 0 的时候不进行渲染,但实际上却渲染了 <code>0</code>。</p><p>为了更正,可以将左侧的值改成布尔类型:<code>messageCount > 0 && <p>New messages</p></code>。</p></li><li><p>选择性赋值JSX</p><h6 id="1、可以选择将需要渲染的JSX表达式文本赋值给变量"><a href="#1、可以选择将需要渲染的JSX表达式文本赋值给变量" class="headerlink" title="1、可以选择将需要渲染的JSX表达式文本赋值给变量"></a>1、可以选择将需要渲染的JSX表达式文本赋值给变量</h6><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 其中 渲染的JSX文本 itemContent赋值变量</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Item</span>(<span class="params">{ name, isPacked }</span>) {</span><br><span class="line"> <span class="keyword">let</span> itemContent = name;</span><br><span class="line"> <span class="keyword">if</span> (isPacked) {</span><br><span class="line"> itemContent = name + <span class="string">" ✔"</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span></span></span><br><span class="line"><span class="language-xml"> {itemContent}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">PackingList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">section</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">h1</span>></span>Sally Ride 的行李清单<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{true}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"宇航服"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{true}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"带金箔的头盔"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{false}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"Tam 的照片"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">section</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h6 id="2、对于任意的JSX也是适用的"><a href="#2、对于任意的JSX也是适用的" class="headerlink" title="2、对于任意的JSX也是适用的"></a>2、对于任意的JSX也是适用的</h6><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 对于JSX itemContent 提取为变量 在return时便可直接使用变量 迭代修改时容易扩展</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Item</span>(<span class="params">{ name, isPacked }</span>) {</span><br><span class="line"> <span class="keyword">let</span> itemContent = name;</span><br><span class="line"> <span class="keyword">if</span> (isPacked) {</span><br><span class="line"> itemContent = (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">del</span>></span></span></span><br><span class="line"><span class="language-xml"> {name + " ✔"}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">del</span>></span></span></span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span></span></span><br><span class="line"><span class="language-xml"> {itemContent}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">PackingList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">section</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">h1</span>></span>Sally Ride 的行李清单<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{true}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"宇航服"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{true}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"带金箔的头盔"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{false}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"Tam 的照片"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">section</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h4 id="渲染列表"><a href="#渲染列表" class="headerlink" title="渲染列表"></a>渲染列表</h4><p>一般来说,我们经常通过<strong>JavaScript数组方法</strong>来操作数组中的数据,从而将数组中的数据渲染为多个类似的组件。</p><h5 id="遍历渲染"><a href="#遍历渲染" class="headerlink" title="遍历渲染"></a>遍历渲染</h5><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 首先我们拥有一个数组数据。</span></span><br><span class="line"><span class="keyword">const</span> people = [</span><br><span class="line"> <span class="string">'凯瑟琳·约翰逊: 数学家'</span>,</span><br><span class="line"> <span class="string">'马里奥·莫利纳: 化学家'</span>,</span><br><span class="line"> <span class="string">'穆罕默德·阿卜杜勒·萨拉姆: 物理学家'</span>,</span><br><span class="line"> <span class="string">'珀西·莱温·朱利亚: 化学家'</span>,</span><br><span class="line"> <span class="string">'苏布拉马尼扬·钱德拉塞卡: 天体物理学家'</span>,</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 我们可以使用数组的遍历方法map这个数组来遍历元素</span></span><br><span class="line"><span class="keyword">const</span> listItem = people.<span class="title function_">map</span>(<span class="function"><span class="params">person</span>=></span><span class="language-xml"><span class="tag"><<span class="name">li</span>></span>{person}<span class="tag"></<span class="name">li</span>></span></span>)</span><br><span class="line"> </span><br><span class="line"><span class="comment">// 用ul将listItem包围</span></span><br><span class="line"><span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">ul</span>></span>{listItem}<span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 最终代码</span></span><br><span class="line"><span class="keyword">const</span> people = [</span><br><span class="line"> <span class="string">'凯瑟琳·约翰逊: 数学家'</span>,</span><br><span class="line"> <span class="string">'马里奥·莫利纳: 化学家'</span>,</span><br><span class="line"> <span class="string">'穆罕默德·阿卜杜勒·萨拉姆: 物理学家'</span>,</span><br><span class="line"> <span class="string">'珀西·莱温·朱利亚: 化学家'</span>,</span><br><span class="line"> <span class="string">'苏布拉马尼扬·钱德拉塞卡: 天体物理学家'</span>,</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">List</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">const</span> listItems = people.<span class="title function_">map</span>(<span class="function"><span class="params">person</span> =></span></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">li</span>></span>{person}<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"> );</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">ul</span>></span>{listItems}<span class="tag"></<span class="name">ul</span>></span></span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p><em><strong><u>注意:</u>这样会导致没有唯一key报错。</strong></em></p><h5 id="过滤渲染"><a href="#过滤渲染" class="headerlink" title="过滤渲染"></a>过滤渲染</h5><p>我们有个需求需要将职业为化学家的元素渲染出来</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 数组</span></span><br><span class="line"><span class="keyword">const</span> people = [</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">id</span>: <span class="number">0</span>,</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'凯瑟琳·约翰逊'</span>,</span><br><span class="line"> <span class="attr">profession</span>: <span class="string">'数学家'</span>,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">id</span>: <span class="number">1</span>,</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'马里奥·莫利纳'</span>,</span><br><span class="line"> <span class="attr">profession</span>: <span class="string">'化学家'</span>,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">id</span>: <span class="number">2</span>,</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'穆罕默德·阿卜杜勒·萨拉姆'</span>,</span><br><span class="line"> <span class="attr">profession</span>: <span class="string">'物理学家'</span>,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'珀西·莱温·朱利亚'</span>,</span><br><span class="line"> <span class="attr">profession</span>: <span class="string">'化学家'</span>,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'苏布拉马尼扬·钱德拉塞卡'</span>,</span><br><span class="line"> <span class="attr">profession</span>: <span class="string">'天体物理学家'</span>,</span><br><span class="line"> },</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 采用数组filter方法进行过滤</span></span><br><span class="line"><span class="keyword">const</span> liItem = people.<span class="title function_">filter</span>(<span class="function"><span class="params">person</span>=></span>person.<span class="property">profession</span>===<span class="string">'化学家'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 用ul将liIte包围</span></span><br><span class="line"><span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">ul</span>></span>{liItem}<span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 完整代码</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">List</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">const</span> listItems = people.<span class="title function_">map</span>(<span class="function"><span class="params">person</span> =></span></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">li</span>></span>{person}<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"> );</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">ul</span>></span>{listItems}<span class="tag"></<span class="name">ul</span>></span></span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h5 id="用key能保证渲染顺序"><a href="#用key能保证渲染顺序" class="headerlink" title="用key能保证渲染顺序"></a>用key能保证渲染顺序</h5><p><em><strong><u>直接放在 <code>map()</code> 方法里的 JSX 元素一般都需要指定 <code>key</code> 值!</u></strong></em></p><p>这些key会告诉React,每个组件对应的数组数据那一项,去进行匹配。</p><p>这些将在数组项进行移动、插入或者删除等操作是非常重要的。合适的key将保证React正常的更新DMO树。</p><p><strong>key值在渲染之前,要存在数据里面key必须存在。</strong></p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line">// app.js</span><br><span class="line">import { people } from './data.js';</span><br><span class="line">import { getImageUrl } from './utils.js';</span><br><span class="line"></span><br><span class="line">export default function List() {</span><br><span class="line"> const listItems = people.map(person =></span><br><span class="line"> <li key={person.id}></span><br><span class="line"> <img</span><br><span class="line"> src={getImageUrl(person)}</span><br><span class="line"> alt={person.name}</span><br><span class="line"> /></span><br><span class="line"> <p></span><br><span class="line"> <b>{person.name}</b></span><br><span class="line"> {' ' + person.profession + ' '}</span><br><span class="line"> 因{person.accomplishment}而闻名世界</span><br><span class="line"> </p></span><br><span class="line"> </li></span><br><span class="line"> );</span><br><span class="line"> return <ul>{listItems}</ul>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// data.js</span><br><span class="line">export const people = [</span><br><span class="line"> {</span><br><span class="line"> id: 0, // 在 JSX 中作为 key 使用</span><br><span class="line"> name: '凯瑟琳·约翰逊',</span><br><span class="line"> profession: '数学家',</span><br><span class="line"> accomplishment: '太空飞行相关数值的核算',</span><br><span class="line"> imageId: 'MK3eW3A',</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> id: 1, // 在 JSX 中作为 key 使用</span><br><span class="line"> name: '马里奥·莫利纳',</span><br><span class="line"> profession: '化学家',</span><br><span class="line"> accomplishment: '北极臭氧空洞的发现',</span><br><span class="line"> imageId: 'mynHUSa',</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> id: 2, // 在 JSX 中作为 key 使用</span><br><span class="line"> name: '穆罕默德·阿卜杜勒·萨拉姆',</span><br><span class="line"> profession: '物理学家',</span><br><span class="line"> accomplishment: '关于基本粒子间弱相互作用和电磁相互作用的统一理论',</span><br><span class="line"> imageId: 'bE7W1ji',</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> id: 3, // 在 JSX 中作为 key 使用</span><br><span class="line"> name: '珀西·莱温·朱利亚',</span><br><span class="line"> profession: '化学家',</span><br><span class="line"> accomplishment: '开创性的可的松药物、类固醇和避孕药',</span><br><span class="line"> imageId: 'IOjWm71',</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> id: 4, // 在 JSX 中作为 key 使用</span><br><span class="line"> name: '苏布拉马尼扬·钱德拉塞卡',</span><br><span class="line"> profession: '天体物理学家',</span><br><span class="line"> accomplishment: '白矮星质量计算',</span><br><span class="line"> imageId: 'lrWQx8l',</span><br><span class="line"> },</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line">// util.js</span><br><span class="line">export function getImageUrl(person) {</span><br><span class="line"> return (</span><br><span class="line"> 'https://i.imgur.com/' +</span><br><span class="line"> person.imageId +</span><br><span class="line"> 's.jpg'</span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p><strong><u><em>如何在列表项显示多个DOM节点??</em></u></strong></p><p>我需要将每个列表多渲染其他DOM节点。使用<code>Fragment</code>的语法简写<code><></></code>无法接受key值。写法存在要不使用</p><div>包裹,或者使用明确的<code><Fragment></code>。<p></p><p>Fragment标签不会出现在DOM上,代码会转成最终的<code><h1></code>、<code><p></code>、<code><h1></code>、<code><p></code>…… 的列表。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">import { Fragment } from 'react';</span><br><span class="line"></span><br><span class="line">// ...</span><br><span class="line"></span><br><span class="line">const listItems = people.map(person =></span><br><span class="line"> <Fragment key={person.id}></span><br><span class="line"> <h1>{person.name}</h1></span><br><span class="line"> <p>{person.bio}</p></span><br><span class="line"> </Fragment></span><br><span class="line">);</span><br></pre></td></tr></tbody></table></figure><h5 id="如何设置key"><a href="#如何设置key" class="headerlink" title="如何设置key"></a>如何设置key</h5><h6 id="不同来源数据对应不同的key值获取方式:"><a href="#不同来源数据对应不同的key值获取方式:" class="headerlink" title="不同来源数据对应不同的key值获取方式:"></a>不同来源数据对应不同的key值获取方式:</h6><ul><li><strong>来自数据库的数据</strong>:数据存在唯一的主键,可以直接使用。</li><li><strong>本地产生的数据</strong>:可以使用自增计数器或者uuid的库生成唯一的key。</li></ul><h6 id="key值需要满足的条件"><a href="#key值需要满足的条件" class="headerlink" title="key值需要满足的条件"></a>key值需要满足的条件</h6><ul><li><strong>key在兄弟节点必须是唯一的。</strong>不需要全局唯一,不同数组可以使用相同的key。</li><li><strong>key值不可改变</strong>。一定不可以在渲染时候的动态生成key。</li></ul><h6 id="为什么需要key??"><a href="#为什么需要key??" class="headerlink" title="为什么需要key??"></a>为什么需要key??</h6><p>React的key作用,其实都是<strong>可以从众多兄弟元素中能够标识出某一项(JSX节点)</strong>。这个提供的key提供的信息不止是这个元素所在的位置。即使元素的位置在渲染工程中发现了改变,它对应的key能够让React在整个生命周期一直可以使用。</p><p><strong>注意:</strong></p><ul><li>不可以直接使用数组的索引作为key值来使用。如果没有显式指定key,React会默认讲索引作为key。当数组项的顺序在插入、删除或者重新排序等操作中发生改变。此时会出现一些难以排查的bug。</li><li>不要使用随机数去动态生成key,这样会导致每次渲染key不一样,React这样会认为你在频繁修改DOM,将会导致所有的组件和DOM元素重新渲染。会导致渲染问题,还会导致用户输入丢失。一定要保证key值的稳定性。</li><li>组件不会将key当作props的一部分。key值只会对React起到提示作用。</li></ul><h4 id="保持组件纯粹"><a href="#保持组件纯粹" class="headerlink" title="保持组件纯粹"></a>保持组件纯粹</h4><h5 id="纯函数"><a href="#纯函数" class="headerlink" title="纯函数"></a>纯函数</h5><p>纯函数具有以下特征:</p><ul><li><strong>只负责自己的任务</strong>。他不会更改在该函数调用前就已经存在的对象和变量。</li><li><strong>输入相同,则输出相同。</strong>给定相同的输入,纯函数总是返回相同的结果。</li></ul><p>React围绕纯函数的概念进行设计。<strong>React假设你编写的所有组件都是纯函数。</strong>也就是说对于相同的输入,你所编写的React组件将会总是返回相同的JSX。</p><p>就像数学公式一样。你传入的值相同,你将会永远得到相同的结果。</p><p>再比如,可以将组件比作食谱。如果你遵循正常的菜谱(也就是相同的规律或者规则),在烹饪过程中不引进新食材,那么每次得到的菜肴都是相同的。这个菜肴就是React组件渲染返回的JSX。</p><h5 id="副作用:(不符合)预期的结果"><a href="#副作用:(不符合)预期的结果" class="headerlink" title="副作用:(不符合)预期的结果"></a>副作用:(不符合)预期的结果</h5><p>React的渲染过程自始至终都是纯粹的。组件就应该只<strong>返回</strong>他们的JSX,而不是<strong>改变</strong>,在渲染前,就存在的任何对象或者变量,都会是他们变得不纯粹。</p><p>例如:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">let guest = 0;</span><br><span class="line"></span><br><span class="line">function Cup() {</span><br><span class="line"> // Bad: changing a preexisting variable!</span><br><span class="line"> guest = guest + 1;</span><br><span class="line"> return <h2>Tea cup for guest #{guest}</h2>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">export default function TeaSet() {</span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <Cup /></span><br><span class="line"> <Cup /></span><br><span class="line"> <Cup /></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>以上组件,在读取外部<code>guest</code>的变量,这将导致React组件每次返回的JSX都是不同的。如果这时候,其他组件也在读取这个变量的话,他们也会产生不同的JSX。</p><p>这时候就可以这样修改:</p><p>我们将变量作为<code>Props</code>(输入),输入到我们的React组件中。现在对于<code>Props</code>输入的变量是一样的,这时候React组件返回的JSX都是一样的。<strong>这时候React组件返回的JSX只依赖这个<code>guest</code>prop。</strong></p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">function Cup({ guest }) {</span><br><span class="line"> return <h2>Tea cup for guest #{guest}</h2>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">export default function TeaSet() {</span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <Cup guest={1} /></span><br><span class="line"> <Cup guest={2} /></span><br><span class="line"> <Cup guest={3} /></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>一般来说,你不应该期望你的组件以特定的顺序去渲染。你要注意,其实每个组件都应该是独立的,同样地,每个组件都应该去“独立思考”去考虑自己的输入,而不是在渲染过程中试图去与其他组件协调,或者依赖于其他组件。</p><h5 id="使用严格模式检测不纯的计算"><a href="#使用严格模式检测不纯的计算" class="headerlink" title="使用严格模式检测不纯的计算"></a>使用严格模式检测不纯的计算</h5><p>当用户输入<u><em>改变</em></u>某些内容时,你应该<strong>设置状态</strong>,而不是直接写入变量。当组件渲染时,你永远不应该改变预先存在的变量或者对象。</p><p>React提供了“严格模式”,在严格模式下开发,他将会调用每个组件函数俩次。<strong>通过重复调用组件函数,严格模式有助找到违反这些规则的组件。</strong></p><p><strong>纯函数仅仅执行计算,因此调用它们两次不会改变任何东西</strong> — 就像两次调用 <code>double(2)</code> 并不会改变返回值,两次求解 y = 2x 不会改变 y 的值一样。相同的输入,总是返回相同的输出。</p><p>严格模式在生产环境下不生效,因此它不会降低应用程序的速度。如需引入严格模式,你可以用 <code><React.StrictMode></code> 包裹根组件。一些框架会默认这样做。</p><h5 id="局部mutation:组件的小秘密"><a href="#局部mutation:组件的小秘密" class="headerlink" title="局部mutation:组件的小秘密"></a>局部mutation:组件的小秘密</h5><p>以上示例的问题是在渲染过程中,组件改变了<strong>预先存在</strong>的变量的值。</p><p>这种现象我们称之为<strong>突变(mutation)</strong>。纯函数不会改变函数作用域以外的变量、或在函数调用前创建的对象,这会使函数变得不纯粹!</p><p>但是,你完全可以在渲染时更改你**<u><em>刚刚</em></u>**创建的变量和对象。</p><p>看下面示例:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">function Cup({ guest }) {</span><br><span class="line"> return <h2>Tea cup for guest #{guest}</h2>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">export default function TeaGathering() {</span><br><span class="line"> let cups = [];</span><br><span class="line"> for (let i = 1; i <= 12; i++) {</span><br><span class="line"> cups.push(<Cup key={i} guest={i} />);</span><br><span class="line"> }</span><br><span class="line"> return cups;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>上面<code>cups</code>变量是在<code>TeaGathering</code>函数内部创建的,函数外部不知道发生了什么。这就被称为 <strong>“局部 mutation”</strong> — 如同藏在组件里的小秘密。</p><h5 id="哪些地方可能引发副作用"><a href="#哪些地方可能引发副作用" class="headerlink" title="哪些地方可能引发副作用"></a>哪些地方可能引发副作用</h5><p>函数式编程很大程度依赖纯函数,但在<strong>某些事物</strong>在特定情况下不得不发生改变。这些变动包含更新屏幕、启动动画、更改数据等,他们被称为<strong>副作用</strong>。这些都是“额外”发生的事情,与渲染过程无关。</p><p>在React中,副作用通常属于<strong>事件处理程序</strong>。事件处理程序是React在你执行某些操作(如单击按钮)时运行的函数。即使事件处理程序是在组件<strong>内部</strong>定义的,他们也不会在渲染期间运行!**<u><em>因此事件处理程序无需事纯函数。</em></u>**</p><p>无法为副作用找到合适的事件处理程序,你还可以调用组件中的<code>useEffect</code>方法将其附加到返回的JSX中,这会将告诉React在渲染结束后执行他。<strong>然而,这种方法应该是最后的手段。</strong></p><h6 id="为什么React如此侧重于纯函数??"><a href="#为什么React如此侧重于纯函数??" class="headerlink" title="为什么React如此侧重于纯函数??"></a>为什么React如此侧重于纯函数??</h6><p>纯函数的编写需要遵循一些习惯和规程。</p><ul><li>你的组件在不同环境运行。这样针对相同的输入,都会返回相同的结果。</li><li>可以为那些未更改的组件来跳过渲染,可以提高性能。因为纯函数总是返回相同的结果,所以可以安全的缓存他们。</li><li>如果在渲染深层组件树的过程中,数据发生了变化,React可以重新开始渲染,不会浪费时间完成过时的渲染。纯粹性使得它随时可以安全的停止计算。</li></ul><h4 id="将UI视为树"><a href="#将UI视为树" class="headerlink" title="将UI视为树"></a>将UI视为树</h4><p>树是项目和UI之间的关系模型,通常使用树结构来表示UI。例如,浏览器使用树结构来构建HTM(DOM)与CSS(CSSOM)。移动平台也使用树来表示试图层次机构。</p><p>与浏览器和移动平台一样,React 还使用树结构来管理和建模 React 应用程序中组件之间的关系。这些树是有用的工具,用于理解数据如何在 React 应用程序中流动以及如何优化呈现和应用程序大小。</p><h5 id="渲染树"><a href="#渲染树" class="headerlink" title="渲染树"></a>渲染树</h5><p>组件的主要特性是能够根据其他组件组合而成。组件是能够嵌套的,其中一个组件的父组件也可能是其他组件的自组件。</p><p>React在渲染的时候,会在渲染树中建模这种层级关系。这棵树是由节点组成,每个节点代表一个组件。</p><p>在React渲染树中,根节点是应用程序的跟组件。这种情况下,根组件是<code>App</code>,他是React渲染的第一个组件。树的每个箭头从父组件指向子组件。</p><p>渲染树表示 React 应用程序的单个渲染过程。在 <a href="https://react.docschina.org/learn/conditional-rendering">条件渲染</a> 中,父组件可以根据传递的数据渲染不同的子组件。</p><p>存在条件渲染的时候,渲染过程的渲染树可能都不同,但是这些树有助于识别React应用程序中的顶级和叶子组件。顶级组件是距离根组件最近的组件,他会影响其下所有的组件渲染性能,通常包含最多复杂性。叶子组件位于树的底部,没有子组件,通常会频繁重新渲染。</p><p>识别这些组件类别有助于应用程序的数据流和性能。</p><h5 id="模块依赖树"><a href="#模块依赖树" class="headerlink" title="模块依赖树"></a>模块依赖树</h5><p>在React应用程序中,可以用树来建模的另一个关系是应用程序的模块依赖关系。当拆分组件和逻辑到不同的文件时,就创建了JavaScript模块,在这些模块中可以导出组件、函数或常量。</p><p>模块依赖树中的每个节点都是一个模块,每个分支代表该模块中的 <code>import</code> 语句。</p><p>以之前的 Inspirations 应用程序为例,可以构建一个模块依赖树,简称依赖树。</p><p>树的根节点是根模块,也称入口文件。它通常包含根组件的模块。</p><p>与同一应用程序的渲染树相比,存在相似的结构,但也存在一些显著的差异:</p><ul><li>构成树的节点代表模块,而不是组件。</li><li>非组件模块。树中也有体现,渲染树仅封装组件。</li><li>组件嵌套中,父组件接受JSX作为<code>children props</code>,子组件渲染出来,但不导入该模块。</li></ul><p>依赖树对于确定React应用程序所需要的模块是很有用的。在生产环境构建React应用时,通常会有构建步骤,该步骤将捆绑所有有必要的JavaScript以供客户端使用。负责操作的工具成为<code>bundler(捆绑器)</code>,并且bunder将使用依赖树来确定应该包含哪些模块。</p><p>随着应用程序的增长,捆绑包大小通常也会增加。大型捆绑包大小对于客户端来说下载和运行成本高昂,并延迟 UI 绘制的时间。了解应用程序的依赖树可能有助于调试这些问题</p><p>现在存在一个html标签</p><figure class="highlight html"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">h1</span>></span>海蒂·拉玛的待办事项<span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"><span class="tag"><<span class="name">img</span> </span></span><br><span class="line"><span class="tag"> <span class="attr">src</span>=<span class="string">"https://i.imgur.com/yXOvdOSs.jpg"</span> </span></span><br><span class="line"><span class="tag"> <span class="attr">alt</span>=<span class="string">"Hedy Lamarr"</span> </span></span><br><span class="line"><span class="tag"> <span class="attr">class</span>=<span class="string">"photo"</span></span></span><br><span class="line"><span class="tag">></span></span><br><span class="line"><span class="tag"><<span class="name">ul</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>发明一种新式交通信号灯</span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>排练一个电影场景</span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>改进频谱技术</span><br><span class="line"><span class="tag"></<span class="name">ul</span>></span></span><br></pre></td></tr></tbody></table></figure><p>此时需要将它转化为React组件,可以这么做,是的没错。 采用 *** <> 或者其他标签*** 包裹。</p><figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">TodoList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">h1</span>></span>海蒂·拉玛的待办事项<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">img</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">src</span>=<span class="string">"https://i.imgur.com/yXOvdOSs.jpg"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">alt</span>=<span class="string">"Hedy Lamarr"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">className</span>=<span class="string">"photo"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>发明一种新式交通信号灯<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>排练一个电影场景<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>改进频谱技术<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></></span></span></span><br></pre></td></tr></tbody></table></figure><h3 id="JSX语法规则-1"><a href="#JSX语法规则-1" class="headerlink" title="JSX语法规则"></a>JSX语法规则</h3><ul><li><p>只能返回一个根元素</p><ol><li>就是return()中必须只有一个根元素</li><li>不想在标签中增加一个额外的 <div>,可以用 <> 和 </> 元素来代替</div></li></ol></li><li><p>标签必须闭合</p></li><li><p>使用驼峰式命名法给大部分属性命名!</p><p><em><strong><u>JSX 最终会被转化为 JavaScript,而 JSX 中的属性也会变成 JavaScript 对象中的键值对。在组件中,经常会遇到需要用变量的方式读取这些属性的时候。但 JavaScript 对变量的命名有限制。例如,变量名称不能包含 - 符号或者像 class 这样的保留字。</u></strong></em></p><p><u><em><strong>JSX 虽然看起来很像 HTML,但在底层其实被转化为了 JavaScript 对象,你不能在一个函数中返回多个对象,除非用一个数组把他们包装起来。这就是为什么多个 JSX 标签必须要用一个父元素或者 Fragment 来包裹。</strong></em></u></p></li></ul><h4 id="JSX中通过大括号使用javaScript-1"><a href="#JSX中通过大括号使用javaScript-1" class="headerlink" title="JSX中通过大括号使用javaScript"></a>JSX中通过大括号使用javaScript</h4><p>JSX可以在JavaScript中编写类似HTML的标签,保证渲染逻辑和内容绑定。在需要在标签中添加Js逻辑或者引用标签的动态属性的时候可以在JSX的大括号里面编写Js。</p><h4 id="JSX使用引号字符串-1"><a href="#JSX使用引号字符串-1" class="headerlink" title="JSX使用引号字符串"></a>JSX使用引号字符串</h4><ul><li><p>当你想将字符串属性传递给JSX时,放在<em><strong>单引号</strong></em>或者<em><strong>双引号</strong></em>中</p></li><li><p>当你想动态传值时,可以使用{ 和 } 替代 “ 和 “</p></li></ul><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 字符串</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Avatar</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">img</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">className</span>=<span class="string">"avatar"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">src</span>=<span class="string">"https://i.imgur.com/7vQD0fPs.jpg"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">alt</span>=<span class="string">"Gregorio Y. Zara"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 变量</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Avatar</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">const</span> avatar = <span class="string">'https://i.imgur.com/7vQD0fPs.jpg'</span>;</span><br><span class="line"> <span class="keyword">const</span> description = <span class="string">'Gregorio Y. Zara'</span>;</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">img</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">className</span>=<span class="string">"avatar"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">src</span>=<span class="string">{avatar}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">alt</span>=<span class="string">{description}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p><u><strong>注意</strong></u>:当使用src={ } 这种写法会去读取花括号中js中这个变量的值。</p><h4 id="标签插值-1"><a href="#标签插值-1" class="headerlink" title="标签插值"></a>标签插值</h4><ul><li><p>JSX中允许在标签中插入大括号<code>{}</code>中使用变量</p></li><li><p>JSX中允许在标签中插入大括号<code>{}</code>中使用函数表达式</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 变量</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">TodoList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">const</span> name = <span class="string">'Gregorio Y. Zara'</span>;</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">h1</span>></span>{name}'s To Do List<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 函数表达式</span></span><br><span class="line"><span class="keyword">const</span> today = <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">formatDate</span>(<span class="params">date</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Intl</span>.<span class="title class_">DateTimeFormat</span>(</span><br><span class="line"> <span class="string">'zh-CN'</span>,</span><br><span class="line"> { <span class="attr">weekday</span>: <span class="string">'long'</span> }</span><br><span class="line"> ).<span class="title function_">format</span>(date);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">TodoList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">h1</span>></span>To Do List for {formatDate(today)}<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h4 id="大括号的使用场景-1"><a href="#大括号的使用场景-1" class="headerlink" title="大括号的使用场景"></a>大括号的使用场景</h4><p>主要场景是字符串、数字、变量、和js表达式。</p><ul><li>用作JSX标签*<u><strong>内部的文本</strong></u>*:<code><h1>{name}'s To Do List</h1></code></li><li>标签***=*<strong>后面紧跟的</strong><u><em>属性</em></u>**:<code>src={avatar}</code> 会读取 <code>avatar</code> 变量,但是!!!<u><em>对于<code>src="{avatar}"只会传一个字符串{avatar}</code></em></u></li></ul><h4 id="双大括号的使用场景-1"><a href="#双大括号的使用场景-1" class="headerlink" title="双大括号的使用场景"></a>双大括号的使用场景</h4><p>双大括号其实是<code>{}</code>传递对象的传递方式。</p><ul><li>对象也用大括号表示,例如 <code>{ name: "Hedy Lamarr", inventions: 5 }</code>。因此,为了能在 JSX 中传递,必须用另一对额外的大括号包裹对象:<code>person={{ name: "Hedy Lamarr", inventions: 5 }}</code>。</li><li>也可使用嵌套对象,在jsx大括号中使用。</li></ul><p><u><em><strong>注意:</strong></em></u>内联 <code>style</code> 属性 使用驼峰命名法编写。例如,HTML <code><ul style="background-color: black"></code> 在你的组件里应该写成 <code><ul style={{ backgroundColor: 'black' }}></code>。</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 对象传递</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">TodoList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">ul</span> <span class="attr">style</span>=<span class="string">{{</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">backgroundColor:</span> '<span class="attr">black</span>',</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">color:</span> '<span class="attr">pink</span>'</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> }}></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Improve the videophone<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Prepare aeronautics lectures<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Work on the alcohol-fuelled engine<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 嵌套对象传递</span></span><br><span class="line"><span class="keyword">const</span> person = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'Gregorio Y. Zara'</span>,</span><br><span class="line"> <span class="attr">theme</span>: {</span><br><span class="line"> <span class="attr">backgroundColor</span>: <span class="string">'black'</span>,</span><br><span class="line"> <span class="attr">color</span>: <span class="string">'pink'</span></span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">TodoList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">{person.theme}</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">h1</span>></span>{person.name}'s Todos<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">img</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">className</span>=<span class="string">"avatar"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">src</span>=<span class="string">"https://i.imgur.com/7vQD0fPs.jpg"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">alt</span>=<span class="string">"Gregorio Y. Zara"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Improve the videophone<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Prepare aeronautics lectures<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Work on the alcohol-fuelled engine<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="Props组件传递-1"><a href="#Props组件传递-1" class="headerlink" title="Props组件传递"></a>Props组件传递</h4><p>React组件使用<em>Props</em>互相通信。每个父组件都可以提供props给他的自组件传递信息。可以通过Props传递认识JavaScript值,包括对象、数组、和函数。</p><p>Props传递类似HTML预定义的属性。像组件传递的时候可以传递任意的props。传递的props可以通过不同的方式进行渲染。</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 其中person就是props</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">其中的花括号也就是props传递对象而已</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1、传递</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Profile</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">Avatar</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">person</span>=<span class="string">{{</span> <span class="attr">name:</span> '<span class="attr">Lin</span> <span class="attr">Lanying</span>', <span class="attr">imageId:</span> '<span class="attr">1bX5QH6</span>' }}</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">size</span>=<span class="string">{100}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2、使用/读取</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">1.以下这种方式是采用解构,将person的props解构为单独的props传递</span></span><br><span class="line"><span class="comment">2.prop可以存在默认值,例如下面的prop size</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Avatar</span>(<span class="params">{ person, size=<span class="number">100</span> }</span>) {</span><br><span class="line"> <span class="comment">// 在这里 person 和 size 是可访问的</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>Props的使用,必须要关注你的父子组件。需要考虑好父组件需要向自组件传的props,而子组件需要考虑传递的props通过什么方式去进行渲染。事实上,**<u><em>就可以总结出来,React函数组件就是接受一个参数,那就是Props对象。</em></u>**</p><h5 id="Props使用妙计-1"><a href="#Props使用妙计-1" class="headerlink" title="Props使用妙计"></a>Props使用妙计</h5><ul><li><p>prop可以指定默认值</p><p>如果渲染时不存在size的prop,那么size将被赋值100进行渲染</p><p>其实就是size的prop属性不存在或者值undefined时会生效</p><p><u><em><strong>但是,如果传递<code>size={null}</code> 或 <code>size={0}</code>,默认值将 不 被使用。</strong></em></u></p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Avatar</span>(<span class="params">{ person, size = <span class="number">100</span> }</span>) {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p>可以使用JSX展开语法传递Props</p><p>会存在需要传递pros很多,需要声明prop传递</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Profile</span>(<span class="params">{ person, size, isSepia, thickBorder }</span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">className</span>=<span class="string">"card"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Avatar</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">person</span>=<span class="string">{person}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">size</span>=<span class="string">{size}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isSepia</span>=<span class="string">{isSepia}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">thickBorder</span>=<span class="string">{thickBorder}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"> );</span><br></pre></td></tr></tbody></table></figure><p>如果存在将所有的props传递给子组件可以采用简洁展开语法,这种写法,将父组件需要传递的props全部传递。<strong>如果滥用,那么建议拆分组件。</strong></p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Profile</span>(<span class="params">props</span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">className</span>=<span class="string">"card"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Avatar</span> {<span class="attr">...props</span>} /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p>将JSX作为子组件传递</p><p>类似HTML标签嵌套,以下例子在父组件card中将children组件作为prop传递,类似vue插槽</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// app.js</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">Avatar</span> <span class="keyword">from</span> <span class="string">'./Avatar.js'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Card</span>(<span class="params">{ children }</span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">className</span>=<span class="string">"card"</span>></span></span></span><br><span class="line"><span class="language-xml"> {children}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Profile</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">Card</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Avatar</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">size</span>=<span class="string">{100}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">person</span>=<span class="string">{{</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name:</span> '<span class="attr">Katsuko</span> <span class="attr">Saruhashi</span>',</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">imageId:</span> '<span class="attr">YfeOqp2</span>'</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> }}</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">Card</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Avatar.js</span></span><br><span class="line"><span class="keyword">import</span> { getImageUrl } <span class="keyword">from</span> <span class="string">'./utils.js'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Avatar</span>(<span class="params">{ person, size }</span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">img</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">className</span>=<span class="string">"avatar"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">src</span>=<span class="string">{getImageUrl(person)}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">alt</span>=<span class="string">{person.name}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">width</span>=<span class="string">{size}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">height</span>=<span class="string">{size}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// utils.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">getImageUrl</span>(<span class="params">person, size = <span class="string">'s'</span></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="string">'https://i.imgur.com/'</span> +</span><br><span class="line"> person.<span class="property">imageId</span> +</span><br><span class="line"> size +</span><br><span class="line"> <span class="string">'.jpg'</span></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h5 id="注意-1"><a href="#注意-1" class="headerlink" title="注意"></a>注意</h5><p><strong>一个组件可能会随着时间的推移收到不同的 props。</strong> Props 并不总是静态的!Props 反映了组件在任何时间点的数据,并不仅仅是在开始时。</p><p>然而,props 是 <a href="https://en.wikipedia.org/wiki/Immutable_object">不可变的</a>(一个计算机科学术语,意思是“不可改变”)。当一个组件需要改变它的 props(例如,响应用户交互或新数据)时,它不得不“请求”它的父组件传递 <strong>不同的 props</strong> —— 一个新对象!它的旧 props 将被丢弃,最终 JavaScript 引擎将回收它们占用的内存。<strong>不要尝试“更改 props”。</strong> </p><h4 id="条件渲染-1"><a href="#条件渲染-1" class="headerlink" title="条件渲染"></a>条件渲染</h4><p>组件中会存在很多根据条件渲染的内容。在React中可以使用js中的<code>if</code> 语句、<code>&&</code> 和 <code>? :</code> 运算符来选择性地渲染 JSX。</p><h5 id="如何根据条件返回JSX-1"><a href="#如何根据条件返回JSX-1" class="headerlink" title="如何根据条件返回JSX"></a>如何根据条件返回JSX</h5><p>根据组件Prop传入的属性值来动态展示内容</p><ul><li>使用 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/if...else">if/else 语句</a> 去判断</li></ul><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//其中isPacked是组件接受的Prop</span></span><br><span class="line"><span class="comment">// 用来动态显示 不同li内容的展示</span></span><br><span class="line"><span class="comment">// 可以选择 return 的组件为null 那么他将不会显示任何内容</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Item</span>(<span class="params">{ name, isPacked }</span>) {</span><br><span class="line"> <span class="keyword">if</span> (isPacked) {</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span>{name} ✔<span class="tag"></<span class="name">li</span>></span></span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span>{name}<span class="tag"></<span class="name">li</span>></span></span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">PackingList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">section</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">h1</span>></span>Sally Ride 的行李清单<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{true}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"宇航服"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{true}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"带金箔的头盔"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{false}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"Tam 的照片"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">section</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li><p>选择性包含JSX</p><p>一般的不同条件会return不同的HTML片段,此刻可以选择包含JSX的书写,一般使用以下运算符:</p><h6 id="1、三目运算符(-)-1"><a href="#1、三目运算符(-)-1" class="headerlink" title="1、三目运算符(? :)"></a>1、三目运算符(<code>? :</code>)</h6><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (isPacked) {</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span>{name} ✔<span class="tag"></<span class="name">li</span>></span></span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span>{name}<span class="tag"></<span class="name">li</span>></span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 以上代码使用三目运算符修改为</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span></span></span><br><span class="line"><span class="language-xml"> {isPacked ? name + ' ✔' : name}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line">);</span><br></pre></td></tr></tbody></table></figure><p>**<u><em>注意:</em></u>**简单的内容展示适合使用三目这种运算符来展示,如果展示的逻辑较为复杂,可选择提取自组件来渲染。</p><h6 id="2、与运算符(-amp-amp-)-1"><a href="#2、与运算符(-amp-amp-)-1" class="headerlink" title="2、与运算符(&&)"></a>2、与运算符(<code>&&</code>)</h6><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 当 JavaScript && 表达式 的左侧(我们的条件)为 true 时,它则返回其右侧的值(在我们的例子里是勾选符号)。但条件的结果是 false,则整个表达式会变成 false。在 JSX 里,React 会将 false 视为一个“空值”,就像 null 或者 undefined,这样 React 就不会在这里进行任何渲染</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span></span></span><br><span class="line"><span class="language-xml"> {name} {isPacked && '✔'}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line">);</span><br></pre></td></tr></tbody></table></figure><p><u><em>注意:</em></u><strong>切勿将数字放在 <code>&&</code> 左侧.</strong></p><p>JavaScript 会<u><em><strong>自动将左侧的值转换成布尔类型</strong></em></u>以判断条件成立与否。然而,如果左侧是 <code>0</code>,整个表达式将变成左侧的值(<code>0</code>),React 此时则会渲染 <code>0</code> 而不是不进行渲染。</p><p>例如,一个常见的错误是 <code>messageCount && <p>New messages</p></code>。其原本是想当 <code>messageCount</code> 为 0 的时候不进行渲染,但实际上却渲染了 <code>0</code>。</p><p>为了更正,可以将左侧的值改成布尔类型:<code>messageCount > 0 && <p>New messages</p></code>。</p></li><li><p>选择性赋值JSX</p><h6 id="1、可以选择将需要渲染的JSX表达式文本赋值给变量-1"><a href="#1、可以选择将需要渲染的JSX表达式文本赋值给变量-1" class="headerlink" title="1、可以选择将需要渲染的JSX表达式文本赋值给变量"></a>1、可以选择将需要渲染的JSX表达式文本赋值给变量</h6><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 其中 渲染的JSX文本 itemContent赋值变量</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Item</span>(<span class="params">{ name, isPacked }</span>) {</span><br><span class="line"> <span class="keyword">let</span> itemContent = name;</span><br><span class="line"> <span class="keyword">if</span> (isPacked) {</span><br><span class="line"> itemContent = name + <span class="string">" ✔"</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span></span></span><br><span class="line"><span class="language-xml"> {itemContent}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">PackingList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">section</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">h1</span>></span>Sally Ride 的行李清单<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{true}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"宇航服"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{true}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"带金箔的头盔"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{false}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"Tam 的照片"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">section</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h6 id="2、对于任意的JSX也是适用的-1"><a href="#2、对于任意的JSX也是适用的-1" class="headerlink" title="2、对于任意的JSX也是适用的"></a>2、对于任意的JSX也是适用的</h6><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 对于JSX itemContent 提取为变量 在return时便可直接使用变量 迭代修改时容易扩展</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Item</span>(<span class="params">{ name, isPacked }</span>) {</span><br><span class="line"> <span class="keyword">let</span> itemContent = name;</span><br><span class="line"> <span class="keyword">if</span> (isPacked) {</span><br><span class="line"> itemContent = (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">del</span>></span></span></span><br><span class="line"><span class="language-xml"> {name + " ✔"}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">del</span>></span></span></span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span></span></span><br><span class="line"><span class="language-xml"> {itemContent}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">PackingList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">section</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">h1</span>></span>Sally Ride 的行李清单<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{true}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"宇航服"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{true}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"带金箔的头盔"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{false}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"Tam 的照片"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">section</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h4 id="渲染列表-1"><a href="#渲染列表-1" class="headerlink" title="渲染列表"></a>渲染列表</h4><p>一般来说,我们经常通过<strong>JavaScript数组方法</strong>来操作数组中的数据,从而将数组中的数据渲染为多个类似的组件。</p><h5 id="遍历渲染-1"><a href="#遍历渲染-1" class="headerlink" title="遍历渲染"></a>遍历渲染</h5><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 首先我们拥有一个数组数据。</span></span><br><span class="line"><span class="keyword">const</span> people = [</span><br><span class="line"> <span class="string">'凯瑟琳·约翰逊: 数学家'</span>,</span><br><span class="line"> <span class="string">'马里奥·莫利纳: 化学家'</span>,</span><br><span class="line"> <span class="string">'穆罕默德·阿卜杜勒·萨拉姆: 物理学家'</span>,</span><br><span class="line"> <span class="string">'珀西·莱温·朱利亚: 化学家'</span>,</span><br><span class="line"> <span class="string">'苏布拉马尼扬·钱德拉塞卡: 天体物理学家'</span>,</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 我们可以使用数组的遍历方法map这个数组来遍历元素</span></span><br><span class="line"><span class="keyword">const</span> listItem = people.<span class="title function_">map</span>(<span class="function"><span class="params">person</span>=></span><span class="language-xml"><span class="tag"><<span class="name">li</span>></span>{person}<span class="tag"></<span class="name">li</span>></span></span>)</span><br><span class="line"> </span><br><span class="line"><span class="comment">// 用ul将listItem包围</span></span><br><span class="line"><span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">ul</span>></span>{listItem}<span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 最终代码</span></span><br><span class="line"><span class="keyword">const</span> people = [</span><br><span class="line"> <span class="string">'凯瑟琳·约翰逊: 数学家'</span>,</span><br><span class="line"> <span class="string">'马里奥·莫利纳: 化学家'</span>,</span><br><span class="line"> <span class="string">'穆罕默德·阿卜杜勒·萨拉姆: 物理学家'</span>,</span><br><span class="line"> <span class="string">'珀西·莱温·朱利亚: 化学家'</span>,</span><br><span class="line"> <span class="string">'苏布拉马尼扬·钱德拉塞卡: 天体物理学家'</span>,</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">List</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">const</span> listItems = people.<span class="title function_">map</span>(<span class="function"><span class="params">person</span> =></span></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">li</span>></span>{person}<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"> );</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">ul</span>></span>{listItems}<span class="tag"></<span class="name">ul</span>></span></span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p><em><strong><u>注意:</u>这样会导致没有唯一key报错。</strong></em></p><h5 id="过滤渲染-1"><a href="#过滤渲染-1" class="headerlink" title="过滤渲染"></a>过滤渲染</h5><p>我们有个需求需要将职业为化学家的元素渲染出来</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 数组</span></span><br><span class="line"><span class="keyword">const</span> people = [</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">id</span>: <span class="number">0</span>,</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'凯瑟琳·约翰逊'</span>,</span><br><span class="line"> <span class="attr">profession</span>: <span class="string">'数学家'</span>,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">id</span>: <span class="number">1</span>,</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'马里奥·莫利纳'</span>,</span><br><span class="line"> <span class="attr">profession</span>: <span class="string">'化学家'</span>,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">id</span>: <span class="number">2</span>,</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'穆罕默德·阿卜杜勒·萨拉姆'</span>,</span><br><span class="line"> <span class="attr">profession</span>: <span class="string">'物理学家'</span>,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'珀西·莱温·朱利亚'</span>,</span><br><span class="line"> <span class="attr">profession</span>: <span class="string">'化学家'</span>,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'苏布拉马尼扬·钱德拉塞卡'</span>,</span><br><span class="line"> <span class="attr">profession</span>: <span class="string">'天体物理学家'</span>,</span><br><span class="line"> },</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 采用数组filter方法进行过滤</span></span><br><span class="line"><span class="keyword">const</span> liItem = people.<span class="title function_">filter</span>(<span class="function"><span class="params">person</span>=></span>person.<span class="property">profession</span>===<span class="string">'化学家'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 用ul将liIte包围</span></span><br><span class="line"><span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">ul</span>></span>{liItem}<span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 完整代码</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">List</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">const</span> listItems = people.<span class="title function_">map</span>(<span class="function"><span class="params">person</span> =></span></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">li</span>></span>{person}<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"> );</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">ul</span>></span>{listItems}<span class="tag"></<span class="name">ul</span>></span></span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h5 id="用key能保证渲染顺序-1"><a href="#用key能保证渲染顺序-1" class="headerlink" title="用key能保证渲染顺序"></a>用key能保证渲染顺序</h5><p><em><strong><u>直接放在 <code>map()</code> 方法里的 JSX 元素一般都需要指定 <code>key</code> 值!</u></strong></em></p><p>这些key会告诉React,每个组件对应的数组数据那一项,去进行匹配。</p><p>这些将在数组项进行移动、插入或者删除等操作是非常重要的。合适的key将保证React正常的更新DMO树。</p><p><strong>key值在渲染之前,要存在数据里面key必须存在。</strong></p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line">// app.js</span><br><span class="line">import { people } from './data.js';</span><br><span class="line">import { getImageUrl } from './utils.js';</span><br><span class="line"></span><br><span class="line">export default function List() {</span><br><span class="line"> const listItems = people.map(person =></span><br><span class="line"> <li key={person.id}></span><br><span class="line"> <img</span><br><span class="line"> src={getImageUrl(person)}</span><br><span class="line"> alt={person.name}</span><br><span class="line"> /></span><br><span class="line"> <p></span><br><span class="line"> <b>{person.name}</b></span><br><span class="line"> {' ' + person.profession + ' '}</span><br><span class="line"> 因{person.accomplishment}而闻名世界</span><br><span class="line"> </p></span><br><span class="line"> </li></span><br><span class="line"> );</span><br><span class="line"> return <ul>{listItems}</ul>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// data.js</span><br><span class="line">export const people = [</span><br><span class="line"> {</span><br><span class="line"> id: 0, // 在 JSX 中作为 key 使用</span><br><span class="line"> name: '凯瑟琳·约翰逊',</span><br><span class="line"> profession: '数学家',</span><br><span class="line"> accomplishment: '太空飞行相关数值的核算',</span><br><span class="line"> imageId: 'MK3eW3A',</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> id: 1, // 在 JSX 中作为 key 使用</span><br><span class="line"> name: '马里奥·莫利纳',</span><br><span class="line"> profession: '化学家',</span><br><span class="line"> accomplishment: '北极臭氧空洞的发现',</span><br><span class="line"> imageId: 'mynHUSa',</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> id: 2, // 在 JSX 中作为 key 使用</span><br><span class="line"> name: '穆罕默德·阿卜杜勒·萨拉姆',</span><br><span class="line"> profession: '物理学家',</span><br><span class="line"> accomplishment: '关于基本粒子间弱相互作用和电磁相互作用的统一理论',</span><br><span class="line"> imageId: 'bE7W1ji',</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> id: 3, // 在 JSX 中作为 key 使用</span><br><span class="line"> name: '珀西·莱温·朱利亚',</span><br><span class="line"> profession: '化学家',</span><br><span class="line"> accomplishment: '开创性的可的松药物、类固醇和避孕药',</span><br><span class="line"> imageId: 'IOjWm71',</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> id: 4, // 在 JSX 中作为 key 使用</span><br><span class="line"> name: '苏布拉马尼扬·钱德拉塞卡',</span><br><span class="line"> profession: '天体物理学家',</span><br><span class="line"> accomplishment: '白矮星质量计算',</span><br><span class="line"> imageId: 'lrWQx8l',</span><br><span class="line"> },</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line">// util.js</span><br><span class="line">export function getImageUrl(person) {</span><br><span class="line"> return (</span><br><span class="line"> 'https://i.imgur.com/' +</span><br><span class="line"> person.imageId +</span><br><span class="line"> 's.jpg'</span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p><strong><u><em>如何在列表项显示多个DOM节点??</em></u></strong></p><p>我需要将每个列表多渲染其他DOM节点。使用<code>Fragment</code>的语法简写<code><></></code>无法接受key值。写法存在要不使用</p><div>包裹,或者使用明确的<code><Fragment></code>。<p></p><p>Fragment标签不会出现在DOM上,代码会转成最终的<code><h1></code>、<code><p></code>、<code><h1></code>、<code><p></code>…… 的列表。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">import { Fragment } from 'react';</span><br><span class="line"></span><br><span class="line">// ...</span><br><span class="line"></span><br><span class="line">const listItems = people.map(person =></span><br><span class="line"> <Fragment key={person.id}></span><br><span class="line"> <h1>{person.name}</h1></span><br><span class="line"> <p>{person.bio}</p></span><br><span class="line"> </Fragment></span><br><span class="line">);</span><br></pre></td></tr></tbody></table></figure><h5 id="如何设置key-1"><a href="#如何设置key-1" class="headerlink" title="如何设置key"></a>如何设置key</h5><h6 id="不同来源数据对应不同的key值获取方式:-1"><a href="#不同来源数据对应不同的key值获取方式:-1" class="headerlink" title="不同来源数据对应不同的key值获取方式:"></a>不同来源数据对应不同的key值获取方式:</h6><ul><li><strong>来自数据库的数据</strong>:数据存在唯一的主键,可以直接使用。</li><li><strong>本地产生的数据</strong>:可以使用自增计数器或者uuid的库生成唯一的key。</li></ul><h6 id="key值需要满足的条件-1"><a href="#key值需要满足的条件-1" class="headerlink" title="key值需要满足的条件"></a>key值需要满足的条件</h6><ul><li><strong>key在兄弟节点必须是唯一的。</strong>不需要全局唯一,不同数组可以使用相同的key。</li><li><strong>key值不可改变</strong>。一定不可以在渲染时候的动态生成key。</li></ul><h6 id="为什么需要key??-1"><a href="#为什么需要key??-1" class="headerlink" title="为什么需要key??"></a>为什么需要key??</h6><p>React的key作用,其实都是<strong>可以从众多兄弟元素中能够标识出某一项(JSX节点)</strong>。这个提供的key提供的信息不止是这个元素所在的位置。即使元素的位置在渲染工程中发现了改变,它对应的key能够让React在整个生命周期一直可以使用。</p><p><strong>注意:</strong></p><ul><li>不可以直接使用数组的索引作为key值来使用。如果没有显式指定key,React会默认讲索引作为key。当数组项的顺序在插入、删除或者重新排序等操作中发生改变。此时会出现一些难以排查的bug。</li><li>不要使用随机数去动态生成key,这样会导致每次渲染key不一样,React这样会认为你在频繁修改DOM,将会导致所有的组件和DOM元素重新渲染。会导致渲染问题,还会导致用户输入丢失。一定要保证key值的稳定性。</li><li>组件不会将key当作props的一部分。key值只会对React起到提示作用。</li></ul><h4 id="保持组件纯粹-1"><a href="#保持组件纯粹-1" class="headerlink" title="保持组件纯粹"></a>保持组件纯粹</h4><h5 id="纯函数-1"><a href="#纯函数-1" class="headerlink" title="纯函数"></a>纯函数</h5><p>纯函数具有以下特征:</p><ul><li><strong>只负责自己的任务</strong>。他不会更改在该函数调用前就已经存在的对象和变量。</li><li><strong>输入相同,则输出相同。</strong>给定相同的输入,纯函数总是返回相同的结果。</li></ul><p>React围绕纯函数的概念进行设计。<strong>React假设你编写的所有组件都是纯函数。</strong>也就是说对于相同的输入,你所编写的React组件将会总是返回相同的JSX。</p><p>就像数学公式一样。你传入的值相同,你将会永远得到相同的结果。</p><p>再比如,可以将组件比作食谱。如果你遵循正常的菜谱(也就是相同的规律或者规则),在烹饪过程中不引进新食材,那么每次得到的菜肴都是相同的。这个菜肴就是React组件渲染返回的JSX。</p><h5 id="副作用:(不符合)预期的结果-1"><a href="#副作用:(不符合)预期的结果-1" class="headerlink" title="副作用:(不符合)预期的结果"></a>副作用:(不符合)预期的结果</h5><p>React的渲染过程自始至终都是纯粹的。组件就应该只<strong>返回</strong>他们的JSX,而不是<strong>改变</strong>,在渲染前,就存在的任何对象或者变量,都会是他们变得不纯粹。</p><p>例如:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">let guest = 0;</span><br><span class="line"></span><br><span class="line">function Cup() {</span><br><span class="line"> // Bad: changing a preexisting variable!</span><br><span class="line"> guest = guest + 1;</span><br><span class="line"> return <h2>Tea cup for guest #{guest}</h2>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">export default function TeaSet() {</span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <Cup /></span><br><span class="line"> <Cup /></span><br><span class="line"> <Cup /></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>以上组件,在读取外部<code>guest</code>的变量,这将导致React组件每次返回的JSX都是不同的。如果这时候,其他组件也在读取这个变量的话,他们也会产生不同的JSX。</p><p>这时候就可以这样修改:</p><p>我们将变量作为<code>Props</code>(输入),输入到我们的React组件中。现在对于<code>Props</code>输入的变量是一样的,这时候React组件返回的JSX都是一样的。<strong>这时候React组件返回的JSX只依赖这个<code>guest</code>prop。</strong></p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">function Cup({ guest }) {</span><br><span class="line"> return <h2>Tea cup for guest #{guest}</h2>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">export default function TeaSet() {</span><br><span class="line"> return (</span><br><span class="line"> <></span><br><span class="line"> <Cup guest={1} /></span><br><span class="line"> <Cup guest={2} /></span><br><span class="line"> <Cup guest={3} /></span><br><span class="line"> </></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>一般来说,你不应该期望你的组件以特定的顺序去渲染。你要注意,其实每个组件都应该是独立的,同样地,每个组件都应该去“独立思考”去考虑自己的输入,而不是在渲染过程中试图去与其他组件协调,或者依赖于其他组件。</p><h5 id="使用严格模式检测不纯的计算-1"><a href="#使用严格模式检测不纯的计算-1" class="headerlink" title="使用严格模式检测不纯的计算"></a>使用严格模式检测不纯的计算</h5><p>当用户输入<u><em>改变</em></u>某些内容时,你应该<strong>设置状态</strong>,而不是直接写入变量。当组件渲染时,你永远不应该改变预先存在的变量或者对象。</p><p>React提供了“严格模式”,在严格模式下开发,他将会调用每个组件函数俩次。<strong>通过重复调用组件函数,严格模式有助找到违反这些规则的组件。</strong></p><p><strong>纯函数仅仅执行计算,因此调用它们两次不会改变任何东西</strong> — 就像两次调用 <code>double(2)</code> 并不会改变返回值,两次求解 y = 2x 不会改变 y 的值一样。相同的输入,总是返回相同的输出。</p><p>严格模式在生产环境下不生效,因此它不会降低应用程序的速度。如需引入严格模式,你可以用 <code><React.StrictMode></code> 包裹根组件。一些框架会默认这样做。</p><h5 id="局部mutation:组件的小秘密-1"><a href="#局部mutation:组件的小秘密-1" class="headerlink" title="局部mutation:组件的小秘密"></a>局部mutation:组件的小秘密</h5><p>以上示例的问题是在渲染过程中,组件改变了<strong>预先存在</strong>的变量的值。</p><p>这种现象我们称之为<strong>突变(mutation)</strong>。纯函数不会改变函数作用域以外的变量、或在函数调用前创建的对象,这会使函数变得不纯粹!</p><p>但是,你完全可以在渲染时更改你**<u><em>刚刚</em></u>**创建的变量和对象。</p><p>看下面示例:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">function Cup({ guest }) {</span><br><span class="line"> return <h2>Tea cup for guest #{guest}</h2>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">export default function TeaGathering() {</span><br><span class="line"> let cups = [];</span><br><span class="line"> for (let i = 1; i <= 12; i++) {</span><br><span class="line"> cups.push(<Cup key={i} guest={i} />);</span><br><span class="line"> }</span><br><span class="line"> return cups;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>上面<code>cups</code>变量是在<code>TeaGathering</code>函数内部创建的,函数外部不知道发生了什么。这就被称为 <strong>“局部 mutation”</strong> — 如同藏在组件里的小秘密。</p><h5 id="哪些地方可能引发副作用-1"><a href="#哪些地方可能引发副作用-1" class="headerlink" title="哪些地方可能引发副作用"></a>哪些地方可能引发副作用</h5><p>函数式编程很大程度依赖纯函数,但在<strong>某些事物</strong>在特定情况下不得不发生改变。这些变动包含更新屏幕、启动动画、更改数据等,他们被称为<strong>副作用</strong>。这些都是“额外”发生的事情,与渲染过程无关。</p><p>在React中,副作用通常属于<strong>事件处理程序</strong>。事件处理程序是React在你执行某些操作(如单击按钮)时运行的函数。即使事件处理程序是在组件<strong>内部</strong>定义的,他们也不会在渲染期间运行!**<u><em>因此事件处理程序无需事纯函数。</em></u>**</p><p>无法为副作用找到合适的事件处理程序,你还可以调用组件中的<code>useEffect</code>方法将其附加到返回的JSX中,这会将告诉React在渲染结束后执行他。<strong>然而,这种方法应该是最后的手段。</strong></p><h6 id="为什么React如此侧重于纯函数??-1"><a href="#为什么React如此侧重于纯函数??-1" class="headerlink" title="为什么React如此侧重于纯函数??"></a>为什么React如此侧重于纯函数??</h6><p>纯函数的编写需要遵循一些习惯和规程。</p><ul><li>你的组件在不同环境运行。这样针对相同的输入,都会返回相同的结果。</li><li>可以为那些未更改的组件来跳过渲染,可以提高性能。因为纯函数总是返回相同的结果,所以可以安全的缓存他们。</li><li>如果在渲染深层组件树的过程中,数据发生了变化,React可以重新开始渲染,不会浪费时间完成过时的渲染。纯粹性使得它随时可以安全的停止计算。</li></ul><h4 id="将UI视为树-1"><a href="#将UI视为树-1" class="headerlink" title="将UI视为树"></a>将UI视为树</h4><p>树是项目和UI之间的关系模型,通常使用树结构来表示UI。例如,浏览器使用树结构来构建HTM(DOM)与CSS(CSSOM)。移动平台也使用树来表示试图层次机构。</p><p>与浏览器和移动平台一样,React 还使用树结构来管理和建模 React 应用程序中组件之间的关系。这些树是有用的工具,用于理解数据如何在 React 应用程序中流动以及如何优化呈现和应用程序大小。</p><h5 id="渲染树-1"><a href="#渲染树-1" class="headerlink" title="渲染树"></a>渲染树</h5><p>组件的主要特性是能够根据其他组件组合而成。组件是能够嵌套的,其中一个组件的父组件也可能是其他组件的自组件。</p><p>React在渲染的时候,会在渲染树中建模这种层级关系。这棵树是由节点组成,每个节点代表一个组件。</p><p>在React渲染树中,根节点是应用程序的跟组件。这种情况下,根组件是<code>App</code>,他是React渲染的第一个组件。树的每个箭头从父组件指向子组件。</p><p>渲染树表示 React 应用程序的单个渲染过程。在 <a href="https://react.docschina.org/learn/conditional-rendering">条件渲染</a> 中,父组件可以根据传递的数据渲染不同的子组件。</p><p>存在条件渲染的时候,渲染过程的渲染树可能都不同,但是这些树有助于识别React应用程序中的顶级和叶子组件。顶级组件是距离根组件最近的组件,他会影响其下所有的组件渲染性能,通常包含最多复杂性。叶子组件位于树的底部,没有子组件,通常会频繁重新渲染。</p><p>识别这些组件类别有助于应用程序的数据流和性能。</p><h5 id="模块依赖树-1"><a href="#模块依赖树-1" class="headerlink" title="模块依赖树"></a>模块依赖树</h5><p>在React应用程序中,可以用树来建模的另一个关系是应用程序的模块依赖关系。当拆分组件和逻辑到不同的文件时,就创建了JavaScript模块,在这些模块中可以导出组件、函数或常量。</p><p>模块依赖树中的每个节点都是一个模块,每个分支代表该模块中的 <code>import</code> 语句。</p><p>以之前的 Inspirations 应用程序为例,可以构建一个模块依赖树,简称依赖树。</p><p>树的根节点是根模块,也称入口文件。它通常包含根组件的模块。</p><p>与同一应用程序的渲染树相比,存在相似的结构,但也存在一些显著的差异:</p><ul><li>构成树的节点代表模块,而不是组件。</li><li>非组件模块。树中也有体现,渲染树仅封装组件。</li><li>组件嵌套中,父组件接受JSX作为<code>children props</code>,子组件渲染出来,但不导入该模块。</li></ul><p>依赖树对于确定React应用程序所需要的模块是很有用的。在生产环境构建React应用时,通常会有构建步骤,该步骤将捆绑所有有必要的JavaScript以供客户端使用。负责操作的工具成为<code>bundler(捆绑器)</code>,并且bunder将使用依赖树来确定应该包含哪些模块。</p><p>随着应用程序的增长,捆绑包大小通常也会增加。大型捆绑包大小对于客户端来说下载和运行成本高昂,并延迟 UI 绘制的时间。了解应用程序的依赖树可能有助于调试这些问题</p><h3 id="什么是JSX-1"><a href="#什么是JSX-1" class="headerlink" title="什么是JSX"></a>什么是JSX</h3><p>JSX 是 JavaScript 语法扩展,可以让在 JavaScript 文件中书写类似 HTML 的标签。JSX 看起来和 HTML 很像,但它的语法更加严格并且可以动态展示信息。</p><h3 id="为什么出现JSX-1"><a href="#为什么出现JSX-1" class="headerlink" title="为什么出现JSX"></a>为什么出现JSX</h3><p>随着Web交互性越来越强,逻辑开始决定页面的内容。也就是可以说JavaScript负责HTML内容。因此,在React中,渲染逻辑和标签存在同一个组件文件中。</p><h3 id="使用JSX的优势-1"><a href="#使用JSX的优势-1" class="headerlink" title="使用JSX的优势"></a>使用JSX的优势</h3><p>例如将一个按钮的渲染逻辑和标签放在一起,可以确保他们在编辑的时候保持同步。反之,也可以说彼此无关的细节是隔离的。</p><h3 id="JSX与HTML的区别-1"><a href="#JSX与HTML的区别-1" class="headerlink" title="JSX与HTML的区别"></a>JSX与HTML的区别</h3><p>每个 React 组件都是一个 JavaScript 函数,它会返回一些标签,React 会将这些标签渲染到浏览器上。React 组件使用一种被称为 JSX 的语法扩展来描述这些标签。JSX对于HTML来说,语法更加严格并且可以动态的展示信息。</p><p><strong>JSX and React 是相互独立的东西。通常配合使用,也可以单独使用它们中的任意一个,JSX 是一种语法扩展,而 React 则是一个 JavaScript 的库。</strong> </p><h3 id="HTML转换为JSX-1"><a href="#HTML转换为JSX-1" class="headerlink" title="HTML转换为JSX"></a>HTML转换为JSX</h3><p>现在存在一个html标签</p><figure class="highlight html"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">h1</span>></span>海蒂·拉玛的待办事项<span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"><span class="tag"><<span class="name">img</span> </span></span><br><span class="line"><span class="tag"> <span class="attr">src</span>=<span class="string">"https://i.imgur.com/yXOvdOSs.jpg"</span> </span></span><br><span class="line"><span class="tag"> <span class="attr">alt</span>=<span class="string">"Hedy Lamarr"</span> </span></span><br><span class="line"><span class="tag"> <span class="attr">class</span>=<span class="string">"photo"</span></span></span><br><span class="line"><span class="tag">></span></span><br><span class="line"><span class="tag"><<span class="name">ul</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>发明一种新式交通信号灯</span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>排练一个电影场景</span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>改进频谱技术</span><br><span class="line"><span class="tag"></<span class="name">ul</span>></span></span><br></pre></td></tr></tbody></table></figure><p>此时需要将它转化为React组件,可以这么做,是的没错。 采用 *** <> 或者其他标签*** 包裹。</p><figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">TodoList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">h1</span>></span>海蒂·拉玛的待办事项<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">img</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">src</span>=<span class="string">"https://i.imgur.com/yXOvdOSs.jpg"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">alt</span>=<span class="string">"Hedy Lamarr"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">className</span>=<span class="string">"photo"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>发明一种新式交通信号灯<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>排练一个电影场景<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>改进频谱技术<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></></span></span></span><br></pre></td></tr></tbody></table></figure><h3 id="JSX语法规则-2"><a href="#JSX语法规则-2" class="headerlink" title="JSX语法规则"></a>JSX语法规则</h3><ul><li><p>只能返回一个根元素</p><ol><li>就是return()中必须只有一个根元素</li><li>不想在标签中增加一个额外的 <div>,可以用 <> 和 </> 元素来代替</div></li></ol></li><li><p>标签必须闭合</p></li><li><p>使用驼峰式命名法给大部分属性命名!</p><p><em><strong><u>JSX 最终会被转化为 JavaScript,而 JSX 中的属性也会变成 JavaScript 对象中的键值对。在组件中,经常会遇到需要用变量的方式读取这些属性的时候。但 JavaScript 对变量的命名有限制。例如,变量名称不能包含 - 符号或者像 class 这样的保留字。</u></strong></em></p><p><u><em><strong>JSX 虽然看起来很像 HTML,但在底层其实被转化为了 JavaScript 对象,你不能在一个函数中返回多个对象,除非用一个数组把他们包装起来。这就是为什么多个 JSX 标签必须要用一个父元素或者 Fragment 来包裹。</strong></em></u></p></li></ul><h4 id="JSX中通过大括号使用javaScript-2"><a href="#JSX中通过大括号使用javaScript-2" class="headerlink" title="JSX中通过大括号使用javaScript"></a>JSX中通过大括号使用javaScript</h4><p>JSX可以在JavaScript中编写类似HTML的标签,保证渲染逻辑和内容绑定。在需要在标签中添加Js逻辑或者引用标签的动态属性的时候可以在JSX的大括号里面编写Js。</p><h4 id="JSX使用引号字符串-2"><a href="#JSX使用引号字符串-2" class="headerlink" title="JSX使用引号字符串"></a>JSX使用引号字符串</h4><ul><li><p>当你想将字符串属性传递给JSX时,放在<em><strong>单引号</strong></em>或者<em><strong>双引号</strong></em>中</p></li><li><p>当你想动态传值时,可以使用{ 和 } 替代 “ 和 “</p></li></ul><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 字符串</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Avatar</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">img</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">className</span>=<span class="string">"avatar"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">src</span>=<span class="string">"https://i.imgur.com/7vQD0fPs.jpg"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">alt</span>=<span class="string">"Gregorio Y. Zara"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 变量</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Avatar</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">const</span> avatar = <span class="string">'https://i.imgur.com/7vQD0fPs.jpg'</span>;</span><br><span class="line"> <span class="keyword">const</span> description = <span class="string">'Gregorio Y. Zara'</span>;</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">img</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">className</span>=<span class="string">"avatar"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">src</span>=<span class="string">{avatar}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">alt</span>=<span class="string">{description}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p><u><strong>注意</strong></u>:当使用src={ } 这种写法会去读取花括号中js中这个变量的值。</p><h4 id="标签插值-2"><a href="#标签插值-2" class="headerlink" title="标签插值"></a>标签插值</h4><ul><li><p>JSX中允许在标签中插入大括号<code>{}</code>中使用变量</p></li><li><p>JSX中允许在标签中插入大括号<code>{}</code>中使用函数表达式</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 变量</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">TodoList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">const</span> name = <span class="string">'Gregorio Y. Zara'</span>;</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">h1</span>></span>{name}'s To Do List<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 函数表达式</span></span><br><span class="line"><span class="keyword">const</span> today = <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">formatDate</span>(<span class="params">date</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Intl</span>.<span class="title class_">DateTimeFormat</span>(</span><br><span class="line"> <span class="string">'zh-CN'</span>,</span><br><span class="line"> { <span class="attr">weekday</span>: <span class="string">'long'</span> }</span><br><span class="line"> ).<span class="title function_">format</span>(date);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">TodoList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">h1</span>></span>To Do List for {formatDate(today)}<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h4 id="大括号的使用场景-2"><a href="#大括号的使用场景-2" class="headerlink" title="大括号的使用场景"></a>大括号的使用场景</h4><p>主要场景是字符串、数字、变量、和js表达式。</p><ul><li>用作JSX标签*<u><strong>内部的文本</strong></u>*:<code><h1>{name}'s To Do List</h1></code></li><li>标签***=*<strong>后面紧跟的</strong><u><em>属性</em></u>**:<code>src={avatar}</code> 会读取 <code>avatar</code> 变量,但是!!!<u><em>对于<code>src="{avatar}"只会传一个字符串{avatar}</code></em></u></li></ul><h4 id="双大括号的使用场景-2"><a href="#双大括号的使用场景-2" class="headerlink" title="双大括号的使用场景"></a>双大括号的使用场景</h4><p>双大括号其实是<code>{}</code>传递对象的传递方式。</p><ul><li>对象也用大括号表示,例如 <code>{ name: "Hedy Lamarr", inventions: 5 }</code>。因此,为了能在 JSX 中传递,必须用另一对额外的大括号包裹对象:<code>person={{ name: "Hedy Lamarr", inventions: 5 }}</code>。</li><li>也可使用嵌套对象,在jsx大括号中使用。</li></ul><p><u><em><strong>注意:</strong></em></u>内联 <code>style</code> 属性 使用驼峰命名法编写。例如,HTML <code><ul style="background-color: black"></code> 在你的组件里应该写成 <code><ul style={{ backgroundColor: 'black' }}></code>。</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 对象传递</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">TodoList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">ul</span> <span class="attr">style</span>=<span class="string">{{</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">backgroundColor:</span> '<span class="attr">black</span>',</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">color:</span> '<span class="attr">pink</span>'</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> }}></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Improve the videophone<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Prepare aeronautics lectures<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Work on the alcohol-fuelled engine<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 嵌套对象传递</span></span><br><span class="line"><span class="keyword">const</span> person = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'Gregorio Y. Zara'</span>,</span><br><span class="line"> <span class="attr">theme</span>: {</span><br><span class="line"> <span class="attr">backgroundColor</span>: <span class="string">'black'</span>,</span><br><span class="line"> <span class="attr">color</span>: <span class="string">'pink'</span></span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">TodoList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">{person.theme}</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">h1</span>></span>{person.name}'s Todos<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">img</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">className</span>=<span class="string">"avatar"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">src</span>=<span class="string">"https://i.imgur.com/7vQD0fPs.jpg"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">alt</span>=<span class="string">"Gregorio Y. Zara"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Improve the videophone<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Prepare aeronautics lectures<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Work on the alcohol-fuelled engine<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="Props组件传递-2"><a href="#Props组件传递-2" class="headerlink" title="Props组件传递"></a>Props组件传递</h4><p>React组件使用<em>Props</em>互相通信。每个父组件都可以提供props给他的自组件传递信息。可以通过Props传递认识JavaScript值,包括对象、数组、和函数。</p><p>Props传递类似HTML预定义的属性。像组件传递的时候可以传递任意的props。传递的props可以通过不同的方式进行渲染。</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 其中person就是props</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">其中的花括号也就是props传递对象而已</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1、传递</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Profile</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">Avatar</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">person</span>=<span class="string">{{</span> <span class="attr">name:</span> '<span class="attr">Lin</span> <span class="attr">Lanying</span>', <span class="attr">imageId:</span> '<span class="attr">1bX5QH6</span>' }}</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">size</span>=<span class="string">{100}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2、使用/读取</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">1.以下这种方式是采用解构,将person的props解构为单独的props传递</span></span><br><span class="line"><span class="comment">2.prop可以存在默认值,例如下面的prop size</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Avatar</span>(<span class="params">{ person, size=<span class="number">100</span> }</span>) {</span><br><span class="line"> <span class="comment">// 在这里 person 和 size 是可访问的</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>Props的使用,必须要关注你的父子组件。需要考虑好父组件需要向自组件传的props,而子组件需要考虑传递的props通过什么方式去进行渲染。事实上,**<u><em>就可以总结出来,React函数组件就是接受一个参数,那就是Props对象。</em></u>**</p><h5 id="Props使用妙计-2"><a href="#Props使用妙计-2" class="headerlink" title="Props使用妙计"></a>Props使用妙计</h5><ul><li><p>prop可以指定默认值</p><p>如果渲染时不存在size的prop,那么size将被赋值100进行渲染</p><p>其实就是size的prop属性不存在或者值undefined时会生效</p><p><u><em><strong>但是,如果传递<code>size={null}</code> 或 <code>size={0}</code>,默认值将 不 被使用。</strong></em></u></p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Avatar</span>(<span class="params">{ person, size = <span class="number">100</span> }</span>) {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p>可以使用JSX展开语法传递Props</p><p>会存在需要传递pros很多,需要声明prop传递</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Profile</span>(<span class="params">{ person, size, isSepia, thickBorder }</span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">className</span>=<span class="string">"card"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Avatar</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">person</span>=<span class="string">{person}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">size</span>=<span class="string">{size}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isSepia</span>=<span class="string">{isSepia}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">thickBorder</span>=<span class="string">{thickBorder}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"> );</span><br></pre></td></tr></tbody></table></figure><p>如果存在将所有的props传递给子组件可以采用简洁展开语法,这种写法,将父组件需要传递的props全部传递。<strong>如果滥用,那么建议拆分组件。</strong></p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Profile</span>(<span class="params">props</span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">className</span>=<span class="string">"card"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Avatar</span> {<span class="attr">...props</span>} /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p>将JSX作为子组件传递</p><p>类似HTML标签嵌套,以下例子在父组件card中将children组件作为prop传递,类似vue插槽</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// app.js</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">Avatar</span> <span class="keyword">from</span> <span class="string">'./Avatar.js'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Card</span>(<span class="params">{ children }</span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">className</span>=<span class="string">"card"</span>></span></span></span><br><span class="line"><span class="language-xml"> {children}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Profile</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">Card</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Avatar</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">size</span>=<span class="string">{100}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">person</span>=<span class="string">{{</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name:</span> '<span class="attr">Katsuko</span> <span class="attr">Saruhashi</span>',</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">imageId:</span> '<span class="attr">YfeOqp2</span>'</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> }}</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">Card</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Avatar.js</span></span><br><span class="line"><span class="keyword">import</span> { getImageUrl } <span class="keyword">from</span> <span class="string">'./utils.js'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Avatar</span>(<span class="params">{ person, size }</span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">img</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">className</span>=<span class="string">"avatar"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">src</span>=<span class="string">{getImageUrl(person)}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">alt</span>=<span class="string">{person.name}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">width</span>=<span class="string">{size}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">height</span>=<span class="string">{size}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// utils.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">getImageUrl</span>(<span class="params">person, size = <span class="string">'s'</span></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="string">'https://i.imgur.com/'</span> +</span><br><span class="line"> person.<span class="property">imageId</span> +</span><br><span class="line"> size +</span><br><span class="line"> <span class="string">'.jpg'</span></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h5 id="注意-2"><a href="#注意-2" class="headerlink" title="注意"></a>注意</h5><p><strong>一个组件可能会随着时间的推移收到不同的 props。</strong> Props 并不总是静态的!Props 反映了组件在任何时间点的数据,并不仅仅是在开始时。</p><p>然而,props 是 <a href="https://en.wikipedia.org/wiki/Immutable_object">不可变的</a>(一个计算机科学术语,意思是“不可改变”)。当一个组件需要改变它的 props(例如,响应用户交互或新数据)时,它不得不“请求”它的父组件传递 <strong>不同的 props</strong> —— 一个新对象!它的旧 props 将被丢弃,最终 JavaScript 引擎将回收它们占用的内存。<strong>不要尝试“更改 props”。</strong> </p><h4 id="条件渲染-2"><a href="#条件渲染-2" class="headerlink" title="条件渲染"></a>条件渲染</h4><p>组件中会存在很多根据条件渲染的内容。在React中可以使用js中的<code>if</code> 语句、<code>&&</code> 和 <code>? :</code> 运算符来选择性地渲染 JSX。</p><h5 id="如何根据条件返回JSX-2"><a href="#如何根据条件返回JSX-2" class="headerlink" title="如何根据条件返回JSX"></a>如何根据条件返回JSX</h5><p>根据组件Prop传入的属性值来动态展示内容</p><ul><li>使用 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/if...else">if/else 语句</a> 去判断</li></ul><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//其中isPacked是组件接受的Prop</span></span><br><span class="line"><span class="comment">// 用来动态显示 不同li内容的展示</span></span><br><span class="line"><span class="comment">// 可以选择 return 的组件为null 那么他将不会显示任何内容</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Item</span>(<span class="params">{ name, isPacked }</span>) {</span><br><span class="line"> <span class="keyword">if</span> (isPacked) {</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span>{name} ✔<span class="tag"></<span class="name">li</span>></span></span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span>{name}<span class="tag"></<span class="name">li</span>></span></span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">PackingList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">section</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">h1</span>></span>Sally Ride 的行李清单<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{true}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"宇航服"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{true}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"带金箔的头盔"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{false}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"Tam 的照片"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">section</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li><p>选择性包含JSX</p><p>一般的不同条件会return不同的HTML片段,此刻可以选择包含JSX的书写,一般使用以下运算符:</p><h6 id="1、三目运算符(-)-2"><a href="#1、三目运算符(-)-2" class="headerlink" title="1、三目运算符(? :)"></a>1、三目运算符(<code>? :</code>)</h6><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (isPacked) {</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span>{name} ✔<span class="tag"></<span class="name">li</span>></span></span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span>{name}<span class="tag"></<span class="name">li</span>></span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 以上代码使用三目运算符修改为</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span></span></span><br><span class="line"><span class="language-xml"> {isPacked ? name + ' ✔' : name}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line">);</span><br></pre></td></tr></tbody></table></figure><p>**<u><em>注意:</em></u>**简单的内容展示适合使用三目这种运算符来展示,如果展示的逻辑较为复杂,可选择提取自组件来渲染。</p><h6 id="2、与运算符(-amp-amp-)-2"><a href="#2、与运算符(-amp-amp-)-2" class="headerlink" title="2、与运算符(&&)"></a>2、与运算符(<code>&&</code>)</h6><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 当 JavaScript && 表达式 的左侧(我们的条件)为 true 时,它则返回其右侧的值(在我们的例子里是勾选符号)。但条件的结果是 false,则整个表达式会变成 false。在 JSX 里,React 会将 false 视为一个“空值”,就像 null 或者 undefined,这样 React 就不会在这里进行任何渲染</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span></span></span><br><span class="line"><span class="language-xml"> {name} {isPacked && '✔'}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line">);</span><br></pre></td></tr></tbody></table></figure><p><u><em>注意:</em></u><strong>切勿将数字放在 <code>&&</code> 左侧.</strong></p><p>JavaScript 会<u><em><strong>自动将左侧的值转换成布尔类型</strong></em></u>以判断条件成立与否。然而,如果左侧是 <code>0</code>,整个表达式将变成左侧的值(<code>0</code>),React 此时则会渲染 <code>0</code> 而不是不进行渲染。</p><p>例如,一个常见的错误是 <code>messageCount && <p>New messages</p></code>。其原本是想当 <code>messageCount</code> 为 0 的时候不进行渲染,但实际上却渲染了 <code>0</code>。</p><p>为了更正,可以将左侧的值改成布尔类型:<code>messageCount > 0 && <p>New messages</p></code>。</p></li><li><p>选择性赋值JSX</p><h6 id="1、可以选择将需要渲染的JSX表达式文本赋值给变量-2"><a href="#1、可以选择将需要渲染的JSX表达式文本赋值给变量-2" class="headerlink" title="1、可以选择将需要渲染的JSX表达式文本赋值给变量"></a>1、可以选择将需要渲染的JSX表达式文本赋值给变量</h6><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 其中 渲染的JSX文本 itemContent赋值变量</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Item</span>(<span class="params">{ name, isPacked }</span>) {</span><br><span class="line"> <span class="keyword">let</span> itemContent = name;</span><br><span class="line"> <span class="keyword">if</span> (isPacked) {</span><br><span class="line"> itemContent = name + <span class="string">" ✔"</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span></span></span><br><span class="line"><span class="language-xml"> {itemContent}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">PackingList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">section</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">h1</span>></span>Sally Ride 的行李清单<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{true}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"宇航服"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{true}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"带金箔的头盔"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{false}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"Tam 的照片"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">section</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h6 id="2、对于任意的JSX也是适用的-2"><a href="#2、对于任意的JSX也是适用的-2" class="headerlink" title="2、对于任意的JSX也是适用的"></a>2、对于任意的JSX也是适用的</h6><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 对于JSX itemContent 提取为变量 在return时便可直接使用变量 迭代修改时容易扩展</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Item</span>(<span class="params">{ name, isPacked }</span>) {</span><br><span class="line"> <span class="keyword">let</span> itemContent = name;</span><br><span class="line"> <span class="keyword">if</span> (isPacked) {</span><br><span class="line"> itemContent = (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">del</span>></span></span></span><br><span class="line"><span class="language-xml"> {name + " ✔"}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">del</span>></span></span></span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">className</span>=<span class="string">"item"</span>></span></span></span><br><span class="line"><span class="language-xml"> {itemContent}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">PackingList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">section</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">h1</span>></span>Sally Ride 的行李清单<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{true}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"宇航服"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{true}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"带金箔的头盔"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Item</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isPacked</span>=<span class="string">{false}</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name</span>=<span class="string">"Tam 的照片"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">section</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h4 id="渲染列表-2"><a href="#渲染列表-2" class="headerlink" title="渲染列表"></a>渲染列表</h4><p>一般来说,我们经常通过<strong>JavaScript数组方法</strong>来操作数组中的数据,从而将数组中的数据渲染为多个类似的组件。</p><h5 id="遍历渲染-2"><a href="#遍历渲染-2" class="headerlink" title="遍历渲染"></a>遍历渲染</h5><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 首先我们拥有一个数组数据。</span></span><br><span class="line"><span class="keyword">const</span> people = [</span><br><span class="line"> <span class="string">'凯瑟琳·约翰逊: 数学家'</span>,</span><br><span class="line"> <span class="string">'马里奥·莫利纳: 化学家'</span>,</span><br><span class="line"> <span class="string">'穆罕默德·阿卜杜勒·萨拉姆: 物理学家'</span>,</span><br><span class="line"> <span class="string">'珀西·莱温·朱利亚: 化学家'</span>,</span><br><span class="line"> <span class="string">'苏布拉马尼扬·钱德拉塞卡: 天体物理学家'</span>,</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 我们可以使用数组的遍历方法map这个数组来遍历元素</span></span><br><span class="line"><span class="keyword">const</span> listItem = people.<span class="title function_">map</span>(<span class="function"><span class="params">person</span>=></span><span class="language-xml"><span class="tag"><<span class="name">li</span>></span>{person}<span class="tag"></<span class="name">li</span>></span></span>)</span><br><span class="line"> </span><br><span class="line"><span class="comment">// 用ul将listItem包围</span></span><br><span class="line"><span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">ul</span>></span>{listItem}<span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 最终代码</span></span><br><span class="line"><span class="keyword">const</span> people = [</span><br><span class="line"> <span class="string">'凯瑟琳·约翰逊: 数学家'</span>,</span><br><span class="line"> <span class="string">'马里奥·莫利纳: 化学家'</span>,</span><br><span class="line"> <span class="string">'穆罕默德·阿卜杜勒·萨拉姆: 物理学家'</span>,</span><br><span class="line"> <span class="string">'珀西·莱温·朱利亚: 化学家'</span>,</span><br><span class="line"> <span class="string">'苏布拉马尼扬·钱德拉塞卡: 天体物理学家'</span>,</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">List</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">const</span> listItems = people.<span class="title function_">map</span>(<span class="function"><span class="params">person</span> =></span></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">li</span>></span>{person}<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"> );</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">ul</span>></span>{listItems}<span class="tag"></<span class="name">ul</span>></span></span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p><em><strong><u>注意:</u>这样会导致没有唯一key报错。</strong></em></p><h5 id="过滤渲染-2"><a href="#过滤渲染-2" class="headerlink" title="过滤渲染"></a>过滤渲染</h5><p>我们有个需求需要将职业为化学家的元素渲染出来</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 数组</span></span><br><span class="line"><span class="keyword">const</span> people = [</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">id</span>: <span class="number">0</span>,</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'凯瑟琳·约翰逊'</span>,</span><br><span class="line"> <span class="attr">profession</span>: <span class="string">'数学家'</span>,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">id</span>: <span class="number">1</span>,</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'马里奥·莫利纳'</span>,</span><br><span class="line"> <span class="attr">profession</span>: <span class="string">'化学家'</span>,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">id</span>: <span class="number">2</span>,</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'穆罕默德·阿卜杜勒·萨拉姆'</span>,</span><br><span class="line"> <span class="attr">profession</span>: <span class="string">'物理学家'</span>,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'珀西·莱温·朱利亚'</span>,</span><br><span class="line"> <span class="attr">profession</span>: <span class="string">'化学家'</span>,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'苏布拉马尼扬·钱德拉塞卡'</span>,</span><br><span class="line"> <span class="attr">profession</span>: <span class="string">'天体物理学家'</span>,</span><br><span class="line"> },</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 采用数组filter方法进行过滤</span></span><br><span class="line"><span class="keyword">const</span> liItem = people.<span class="title function_">filter</span>(<span class="function"><span class="params">person</span>=></span>person.<span class="property">profession</span>===<span class="string">'化学家'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 用ul将liIte包围</span></span><br><span class="line"><span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">ul</span>></span>{liItem}<span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 完整代码</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">List</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">const</span> listItems = people.<span class="title function_">map</span>(<span class="function"><span class="params">person</span> =></span></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">li</span>></span>{person}<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"> );</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">ul</span>></span>{listItems}<span class="tag"></<span class="name">ul</span>></span></span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h5 id="用key能保证渲染顺序-2"><a href="#用key能保证渲染顺序-2" class="headerlink" title="用key能保证渲染顺序"></a>用key能保证渲染顺序</h5><p><em><strong><u>直接放在 <code>map()</code> 方法里的 JSX 元素一般都需要指定 <code>key</code> 值!</u></strong></em></p><p>这些key会告诉React,每个组件对应的数组数据那一项,去进行匹配。</p><p>这些将在数组项进行移动、插入或者删除等操作是非常重要的。合适的key将保证React正常的更新DMO树。</p><p><strong>key值在渲染之前,要存在数据里面key必须存在。</strong></p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line">// app.js</span><br><span class="line">import { people } from './data.js';</span><br><span class="line">import { getImageUrl } from './utils.js';</span><br><span class="line"></span><br><span class="line">export default function List() {</span><br><span class="line"> const listItems = people.map(person =></span><br><span class="line"> <li key={person.id}></span><br><span class="line"> <img</span><br><span class="line"> src={getImageUrl(person)}</span><br><span class="line"> alt={person.name}</span><br><span class="line"> /></span><br><span class="line"> <p></span><br><span class="line"> <b>{person.name}</b></span><br><span class="line"> {' ' + person.profession + ' '}</span><br><span class="line"> 因{person.accomplishment}而闻名世界</span><br><span class="line"> </p></span><br><span class="line"> </li></span><br><span class="line"> );</span><br><span class="line"> return <ul>{listItems}</ul>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// data.js</span><br><span class="line">export const people = [</span><br><span class="line"> {</span><br><span class="line"> id: 0, // 在 JSX 中作为 key 使用</span><br><span class="line"> name: '凯瑟琳·约翰逊',</span><br><span class="line"> profession: '数学家',</span><br><span class="line"> accomplishment: '太空飞行相关数值的核算',</span><br><span class="line"> imageId: 'MK3eW3A',</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> id: 1, // 在 JSX 中作为 key 使用</span><br><span class="line"> name: '马里奥·莫利纳',</span><br><span class="line"> profession: '化学家',</span><br><span class="line"> accomplishment: '北极臭氧空洞的发现',</span><br><span class="line"> imageId: 'mynHUSa',</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> id: 2, // 在 JSX 中作为 key 使用</span><br><span class="line"> name: '穆罕默德·阿卜杜勒·萨拉姆',</span><br><span class="line"> profession: '物理学家',</span><br><span class="line"> accomplishment: '关于基本粒子间弱相互作用和电磁相互作用的统一理论',</span><br><span class="line"> imageId: 'bE7W1ji',</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> id: 3, // 在 JSX 中作为 key 使用</span><br><span class="line"> name: '珀西·莱温·朱利亚',</span><br><span class="line"> profession: '化学家',</span><br><span class="line"> accomplishment: '开创性的可的松药物、类固醇和避孕药',</span><br><span class="line"> imageId: 'IOjWm71',</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> id: 4, // 在 JSX 中作为 key 使用</span><br><span class="line"> name: '苏布拉马尼扬·钱德拉塞卡',</span><br><span class="line"> profession: '天体物理学家',</span><br><span class="line"> accomplishment: '白矮星质量计算',</span><br><span class="line"> imageId: 'lrWQx8l',</span><br><span class="line"> },</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line">// util.js</span><br><span class="line">export function getImageUrl(person) {</span><br><span class="line"> return (</span><br><span class="line"> 'https://i.imgur.com/' +</span><br><span class="line"> person.imageId +</span><br><span class="line"> 's.jpg'</span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p><strong><u><em>如何在列表项显示多个DOM节点??</em></u></strong></p><p>我需要将每个列表多渲染其他DOM节点。使用<code>Fragment</code>的语法简写<code><></></code>无法接受key值。写法存在要不使用</p><div>包裹,或者使用明确的<code><Fragment></code>。<p></p><p>Fragment标签不会出现在DOM上,代码会转成最终的<code><h1></code>、<code><p></code>、<code><h1></code>、<code><p></code>…… 的列表。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">import { Fragment } from 'react';</span><br><span class="line"></span><br><span class="line">// ...</span><br><span class="line"></span><br><span class="line">const listItems = people.map(person =></span><br><span class="line"> <Fragment key={person.id}></span><br><span class="line"> <h1>{person.name}</h1></span><br><span class="line"> <p>{person.bio}</p></span><br><span class="line"> </Fragment></span><br><span class="line">);</span><br></pre></td></tr></tbody></table></figure><h5 id="如何设置key-2"><a href="#如何设置key-2" class="headerlink" title="如何设置key"></a>如何设置key</h5><h6 id="不同来源数据对应不同的key值获取方式:-2"><a href="#不同来源数据对应不同的key值获取方式:-2" class="headerlink" title="不同来源数据对应不同的key值获取方式:"></a>不同来源数据对应不同的key值获取方式:</h6><ul><li><strong>来自数据库的数据</strong>:数据存在唯一的主键,可以直接使用。</li><li><strong>本地产生的数据</strong>:可以使用自增计数器或者uuid的库生成唯一的key。</li></ul><h6 id="key值需要满足的条件-2"><a href="#key值需要满足的条件-2" class="headerlink" title="key值需要满足的条件"></a>key值需要满足的条件</h6><ul><li><strong>key在兄弟节点必须是唯一的。</strong>不需要全局唯一,不同数组可以使用相同的key。</li><li><strong>key值不可改变</strong>。一定不可以在渲染时候的动态生成key。</li></ul><h6 id="为什么需要key??-2"><a href="#为什么需要key??-2" class="headerlink" title="为什么需要key??"></a>为什么需要key??</h6><p>React的key作用,其实都是<strong>可以从众多兄弟元素中能够标识出某一项(JSX节点)</strong>。这个提供的key提供的信息不止是这个元素所在的位置。即使元素的位置在渲染工程中发现了改变,它对应的key能够让React在整个生命周期一直可以使用。</p><p><strong>注意:</strong></p><ul><li>不可以直接使用数组的索引作为key值来使用。如果没有显式指定key,React会默认讲索引作为key。当数组项的顺序在插入、删除或者重新排序等操作中发生改变。此时会出现一些难以排查的bug。</li><li>不要使用随机数去动态生成key,这样会导致每次渲染key不一样,React这样会认为你在频繁修改DOM,将会导致所有的组件和DOM元素重新渲染。会导致渲染问题,还会导致用户输入丢失。一定要保证key值的稳定性。</li><li>组件不会将key当作props的一部分。key值只会对React起到提示作用。</li></ul><h3 id="什么是JSX-2"><a href="#什么是JSX-2" class="headerlink" title="什么是JSX"></a>什么是JSX</h3><p>JSX 是 JavaScript 语法扩展,可以让在 JavaScript 文件中书写类似 HTML 的标签。JSX 看起来和 HTML 很像,但它的语法更加严格并且可以动态展示信息。</p><h3 id="为什么出现JSX-2"><a href="#为什么出现JSX-2" class="headerlink" title="为什么出现JSX"></a>为什么出现JSX</h3><p>随着Web交互性越来越强,逻辑开始决定页面的内容。也就是可以说JavaScript负责HTML内容。因此,在React中,渲染逻辑和标签存在同一个组件文件中。</p><h3 id="使用JSX的优势-2"><a href="#使用JSX的优势-2" class="headerlink" title="使用JSX的优势"></a>使用JSX的优势</h3><p>例如将一个按钮的渲染逻辑和标签放在一起,可以确保他们在编辑的时候保持同步。反之,也可以说彼此无关的细节是隔离的。</p><h3 id="JSX与HTML的区别-2"><a href="#JSX与HTML的区别-2" class="headerlink" title="JSX与HTML的区别"></a>JSX与HTML的区别</h3><p>每个 React 组件都是一个 JavaScript 函数,它会返回一些标签,React 会将这些标签渲染到浏览器上。React 组件使用一种被称为 JSX 的语法扩展来描述这些标签。JSX对于HTML来说,语法更加严格并且可以动态的展示信息。</p><p>***JSX and React 是相互独立的东西。通常配合使用,也可以单独使用它们中的任意一个,JSX 是一种语法扩展,而 React 则是一个 JavaScript 的库。 *** </p><h3 id="HTML转换为JSX-2"><a href="#HTML转换为JSX-2" class="headerlink" title="HTML转换为JSX"></a>HTML转换为JSX</h3><p>现在存在一个html标签</p><figure class="highlight html"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">h1</span>></span>海蒂·拉玛的待办事项<span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"><span class="tag"><<span class="name">img</span> </span></span><br><span class="line"><span class="tag"> <span class="attr">src</span>=<span class="string">"https://i.imgur.com/yXOvdOSs.jpg"</span> </span></span><br><span class="line"><span class="tag"> <span class="attr">alt</span>=<span class="string">"Hedy Lamarr"</span> </span></span><br><span class="line"><span class="tag"> <span class="attr">class</span>=<span class="string">"photo"</span></span></span><br><span class="line"><span class="tag">></span></span><br><span class="line"><span class="tag"><<span class="name">ul</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>发明一种新式交通信号灯</span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>排练一个电影场景</span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>改进频谱技术</span><br><span class="line"><span class="tag"></<span class="name">ul</span>></span></span><br></pre></td></tr></tbody></table></figure><p>此时需要将它转化为React组件,可以这么做,是的没错。 采用 *** <> 或者其他标签*** 包裹。</p><figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">TodoList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">h1</span>></span>海蒂·拉玛的待办事项<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">img</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">src</span>=<span class="string">"https://i.imgur.com/yXOvdOSs.jpg"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">alt</span>=<span class="string">"Hedy Lamarr"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">className</span>=<span class="string">"photo"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>发明一种新式交通信号灯<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>排练一个电影场景<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>改进频谱技术<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></></span></span></span><br></pre></td></tr></tbody></table></figure><h3 id="JSX语法规则-3"><a href="#JSX语法规则-3" class="headerlink" title="JSX语法规则"></a>JSX语法规则</h3><ul><li><p>只能返回一个根元素</p><ol><li>就是return()中必须只有一个根元素</li><li>不想在标签中增加一个额外的 <div>,可以用 <> 和 </> 元素来代替</div></li></ol><p>***JSX 虽然看起来很像 HTML,但在底层其实被转化为了 JavaScript 对象,你不能在一个函数中返回多个对象,除非用一个数组把他们包装起来。这就是为什么多个 JSX 标签必须要用一个父元素或者 Fragment 来包裹。 ***</p></li><li><p>标签必须闭合</p></li><li><p>使用驼峰式命名法给大部分属性命名!</p><p>JSX 最终会被转化为 JavaScript,而 JSX 中的属性也会变成 JavaScript 对象中的键值对。在组件中,经常会遇到需要用变量的方式读取这些属性的时候。但 JavaScript 对变量的命名有限制。例如,变量名称不能包含 - 符号或者像 class 这样的保留字。</p></li></ul><h4 id="JSX中通过大括号使用javaScript-3"><a href="#JSX中通过大括号使用javaScript-3" class="headerlink" title="JSX中通过大括号使用javaScript"></a>JSX中通过大括号使用javaScript</h4><p>JSX可以在JavaScript中编写类似HTML的标签,保证渲染逻辑和内容绑定。在需要在标签中添加Js逻辑或者引用标签的动态属性的时候可以在JSX的大括号里面编写Js。</p><h4 id="JSX使用引号字符串-3"><a href="#JSX使用引号字符串-3" class="headerlink" title="JSX使用引号字符串"></a>JSX使用引号字符串</h4><ul><li><p>当你想将字符串属性传递给JSX时,放在<em><strong>单引号</strong></em>或者<em><strong>双引号</strong></em>中</p></li><li><p>当你想动态传值时,可以使用{ 和 } 替代 “ 和 “</p></li></ul><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 字符串</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Avatar</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">img</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">className</span>=<span class="string">"avatar"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">src</span>=<span class="string">"https://i.imgur.com/7vQD0fPs.jpg"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">alt</span>=<span class="string">"Gregorio Y. Zara"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 变量</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Avatar</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">const</span> avatar = <span class="string">'https://i.imgur.com/7vQD0fPs.jpg'</span>;</span><br><span class="line"> <span class="keyword">const</span> description = <span class="string">'Gregorio Y. Zara'</span>;</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">img</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">className</span>=<span class="string">"avatar"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">src</span>=<span class="string">{avatar}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">alt</span>=<span class="string">{description}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p><u><strong>注意</strong></u>:当使用src={ } 这种写法会去读取花括号中js中这个变量的值。</p><h4 id="标签插值-3"><a href="#标签插值-3" class="headerlink" title="标签插值"></a>标签插值</h4><ul><li><p>JSX中允许在标签中插入大括号<code>{}</code>中使用变量</p></li><li><p>JSX中允许在标签中插入大括号<code>{}</code>中使用函数表达式</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 变量</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">TodoList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">const</span> name = <span class="string">'Gregorio Y. Zara'</span>;</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">h1</span>></span>{name}'s To Do List<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 函数表达式</span></span><br><span class="line"><span class="keyword">const</span> today = <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">formatDate</span>(<span class="params">date</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Intl</span>.<span class="title class_">DateTimeFormat</span>(</span><br><span class="line"> <span class="string">'zh-CN'</span>,</span><br><span class="line"> { <span class="attr">weekday</span>: <span class="string">'long'</span> }</span><br><span class="line"> ).<span class="title function_">format</span>(date);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">TodoList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">h1</span>></span>To Do List for {formatDate(today)}<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h4 id="大括号的使用场景-3"><a href="#大括号的使用场景-3" class="headerlink" title="大括号的使用场景"></a>大括号的使用场景</h4><p>主要场景是字符串、数字、变量、和js表达式。</p><ul><li>用作JSX标签*<u><strong>内部的文本</strong></u>*:<code><h1>{name}'s To Do List</h1></code></li><li>标签***=*<strong>后面紧跟的</strong><u><em>属性</em></u>**:<code>src={avatar}</code> 会读取 <code>avatar</code> 变量,但是!!!<u><em>对于<code>src="{avatar}"只会传一个字符串{avatar}</code></em></u></li></ul><h4 id="双大括号的使用场景-3"><a href="#双大括号的使用场景-3" class="headerlink" title="双大括号的使用场景"></a>双大括号的使用场景</h4><p>双大括号其实是<code>{}</code>传递对象的传递方式。</p><ul><li>对象也用大括号表示,例如 <code>{ name: "Hedy Lamarr", inventions: 5 }</code>。因此,为了能在 JSX 中传递,必须用另一对额外的大括号包裹对象:<code>person={{ name: "Hedy Lamarr", inventions: 5 }}</code>。</li><li>也可使用嵌套对象,在jsx大括号中使用。</li></ul><p><u><em><strong>注意:</strong></em></u>内联 <code>style</code> 属性 使用驼峰命名法编写。例如,HTML <code><ul style="background-color: black"></code> 在你的组件里应该写成 <code><ul style={{ backgroundColor: 'black' }}></code>。</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 对象传递</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">TodoList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">ul</span> <span class="attr">style</span>=<span class="string">{{</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">backgroundColor:</span> '<span class="attr">black</span>',</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">color:</span> '<span class="attr">pink</span>'</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> }}></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Improve the videophone<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Prepare aeronautics lectures<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Work on the alcohol-fuelled engine<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 嵌套对象传递</span></span><br><span class="line"><span class="keyword">const</span> person = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'Gregorio Y. Zara'</span>,</span><br><span class="line"> <span class="attr">theme</span>: {</span><br><span class="line"> <span class="attr">backgroundColor</span>: <span class="string">'black'</span>,</span><br><span class="line"> <span class="attr">color</span>: <span class="string">'pink'</span></span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">TodoList</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">{person.theme}</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">h1</span>></span>{person.name}'s Todos<span class="tag"></<span class="name">h1</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">img</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">className</span>=<span class="string">"avatar"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">src</span>=<span class="string">"https://i.imgur.com/7vQD0fPs.jpg"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">alt</span>=<span class="string">"Gregorio Y. Zara"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Improve the videophone<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Prepare aeronautics lectures<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span>></span>Work on the alcohol-fuelled engine<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="Props组件传递-3"><a href="#Props组件传递-3" class="headerlink" title="Props组件传递"></a>Props组件传递</h4><p>React组件使用<em>Props</em>互相通信。每个父组件都可以提供props给他的自组件传递信息。可以通过Props传递认识JavaScript值,包括对象、数组、和函数。</p><p>Props传递类似HTML预定义的属性。像组件传递的时候可以传递任意的props。传递的props可以通过不同的方式进行渲染。</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 其中person就是props</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">其中的花括号也就是props传递对象而已</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1、传递</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Profile</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">Avatar</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">person</span>=<span class="string">{{</span> <span class="attr">name:</span> '<span class="attr">Lin</span> <span class="attr">Lanying</span>', <span class="attr">imageId:</span> '<span class="attr">1bX5QH6</span>' }}</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">size</span>=<span class="string">{100}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2、使用/读取</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">1.以下这种方式是采用解构,将person的props解构为单独的props传递</span></span><br><span class="line"><span class="comment">2.prop可以存在默认值,例如下面的prop size</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Avatar</span>(<span class="params">{ person, size=<span class="number">100</span> }</span>) {</span><br><span class="line"> <span class="comment">// 在这里 person 和 size 是可访问的</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>Props的使用,必须要关注你的父子组件。需要考虑好父组件需要向自组件传的props,而子组件需要考虑传递的props通过什么方式去进行渲染。事实上,**<u><em>就可以总结出来,React函数组件就是接受一个参数,那就是Props对象。</em></u>**</p><h5 id="Props使用妙计-3"><a href="#Props使用妙计-3" class="headerlink" title="Props使用妙计"></a>Props使用妙计</h5><ul><li><p>prop可以指定默认值</p><p>如果渲染时不存在size的prop,那么size将被赋值100进行渲染</p><p>其实就是size的prop属性不存在或者值undefined时会生效</p><p><u><em><strong>但是,如果传递<code>size={null}</code> 或 <code>size={0}</code>,默认值将 不 被使用。</strong></em></u></p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Avatar</span>(<span class="params">{ person, size = <span class="number">100</span> }</span>) {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p>可以使用JSX展开语法传递Props</p><p>会存在需要传递pros很多,需要声明prop传递</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Profile</span>(<span class="params">{ person, size, isSepia, thickBorder }</span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">className</span>=<span class="string">"card"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Avatar</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">person</span>=<span class="string">{person}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">size</span>=<span class="string">{size}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">isSepia</span>=<span class="string">{isSepia}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">thickBorder</span>=<span class="string">{thickBorder}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"> );</span><br></pre></td></tr></tbody></table></figure><p>如果存在将所有的props传递给子组件可以采用简洁展开语法,这种写法,将父组件需要传递的props全部传递。<strong>如果滥用,那么建议拆分组件。</strong></p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Profile</span>(<span class="params">props</span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">className</span>=<span class="string">"card"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Avatar</span> {<span class="attr">...props</span>} /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p>将JSX作为子组件传递</p><p>类似HTML标签嵌套,以下例子在父组件card中将children组件作为prop传递,类似vue插槽</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// app.js</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">Avatar</span> <span class="keyword">from</span> <span class="string">'./Avatar.js'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Card</span>(<span class="params">{ children }</span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">className</span>=<span class="string">"card"</span>></span></span></span><br><span class="line"><span class="language-xml"> {children}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Profile</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">Card</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">Avatar</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">size</span>=<span class="string">{100}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">person</span>=<span class="string">{{</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">name:</span> '<span class="attr">Katsuko</span> <span class="attr">Saruhashi</span>',</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">imageId:</span> '<span class="attr">YfeOqp2</span>'</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> }}</span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">Card</span>></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Avatar.js</span></span><br><span class="line"><span class="keyword">import</span> { getImageUrl } <span class="keyword">from</span> <span class="string">'./utils.js'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">Avatar</span>(<span class="params">{ person, size }</span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">img</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">className</span>=<span class="string">"avatar"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">src</span>=<span class="string">{getImageUrl(person)}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">alt</span>=<span class="string">{person.name}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">width</span>=<span class="string">{size}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">height</span>=<span class="string">{size}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"> );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// utils.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">getImageUrl</span>(<span class="params">person, size = <span class="string">'s'</span></span>) {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="string">'https://i.imgur.com/'</span> +</span><br><span class="line"> person.<span class="property">imageId</span> +</span><br><span class="line"> size +</span><br><span class="line"> <span class="string">'.jpg'</span></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h5 id="注意-3"><a href="#注意-3" class="headerlink" title="注意"></a>注意</h5><p><strong>一个组件可能会随着时间的推移收到不同的 props。</strong> Props 并不总是静态的!Props 反映了组件在任何时间点的数据,并不仅仅是在开始时。</p><p>然而,props 是 <a href="https://en.wikipedia.org/wiki/Immutable_object">不可变的</a>(一个计算机科学术语,意思是“不可改变”)。当一个组件需要改变它的 props(例如,响应用户交互或新数据)时,它不得不“请求”它的父组件传递 <strong>不同的 props</strong> —— 一个新对象!它的旧 props 将被丢弃,最终 JavaScript 引擎将回收它们占用的内存。<strong>不要尝试“更改 props”。</strong> </p></div></div></div>]]></content>
<summary type="html"><hr>
<h1 id="React"><a href="#React" class="headerlink" title="React"></a>React</h1><p>React应用就是被组件的独立UI片段构建,React组件本质就是可以添加任意标签的JavaScript函数。</p></summary>
<category term="前端框架" scheme="https://ayozoo.github.io/categories/%E5%89%8D%E7%AB%AF%E6%A1%86%E6%9E%B6/"/>
<category term="react" scheme="https://ayozoo.github.io/tags/react/"/>
</entry>
<entry>
<title>http vs websocket</title>
<link href="https://ayozoo.github.io/2023/08/27/%20websocket/"/>
<id>https://ayozoo.github.io/2023/08/27/%20websocket/</id>
<published>2023-08-27T13:22:45.000Z</published>
<updated>2023-09-26T15:17:06.157Z</updated>
<content type="html"><![CDATA[<h1 id="通信"><a href="#通信" class="headerlink" title="通信"></a>通信</h1><pre><code>串行通信中,数据通常是在俩个终端之间进行传送,根据数据流的传输方向分为以下三种基本传送方式:单工、半双工和全双工。</code></pre><span id="more"></span><h1 id="基本区别为:"><a href="#基本区别为:" class="headerlink" title="基本区别为:"></a>基本区别为:</h1><ul><li><code>单工</code>:单工通信只有一根数据线,通信只在一个方向上进行,这种方式的应用实例有:监视器、打印机、电视机等。</li><li><code>半双工</code>:半双工通信也只有一根数据线,它也单工的区别是这根数据线既可作发送又可作发接收,虽然数据可在两个方向上传送,但通信双方不能同时收发数据。<strong>http协议采用的就是这个通信方式</strong></li><li><code>全双工</code>: 数据的发送和接收用两根不同的数据线,通信双方在同一时刻都能进行发送和接收,这一工作方式称为全双工通信。在这种方式下,通信双方都有发送器和接收器,发送和接收可同时进行,没有时间延迟。<strong>websocket采用的就是这个通信方式</strong></li></ul><h1 id="http"><a href="#http" class="headerlink" title="http"></a>http</h1><ul><li>http协议主要关注的是 客户端——>服务器(获取资源)</li></ul><p>特点:无状态协议;<br> 每个请求都是独立的;<br> 请求应答模式,服务器无法主动给客户端推送消息(单工,<em>半双工</em>,全双工)<br> http受浏览器同源策略的影响</p><h1 id="websocket"><a href="#websocket" class="headerlink" title="websocket"></a>websocket</h1><p>双向通信(全双工协议)每次不需要重新建立连接,可以一直相互通信</p><h3 id="不使用websocket-以前的双向通信的实现方式"><a href="#不使用websocket-以前的双向通信的实现方式" class="headerlink" title="不使用websocket 以前的双向通信的实现方式"></a>不使用websocket 以前的双向通信的实现方式</h3><p>Comet,主要是为了是实现服务端可以像客户端桶送数据,为了继绝实时性比较高的情况。</p><ul><li>1.轮询(客户端定期向服务端发送请求采用方式,前端setInterval定时器发送请求)<br>轮询会在的问题:<ul><li>轮询方式会存在<strong>竞速问题</strong>,无法保证请求的先后顺序,可能会存在多个请求返回的结果同时修改资源。</li><li>频繁的网络请求 会导致服务器负荷增加 同时频繁的客户端发请求也会影响客户端性能问题</li><li>http 发送的时候 会增加http报文(headers、鉴权、内容类型) 会出现额外的数据消耗</li><li>实时性比较低 定时轮询前端定时轮询(定时器发请求)无法处理即时处理的请求<br>轮询的优点</li><li>容易实现</li><li>不适合实时性比较高的,低并发</li></ul><hr></li><li>2.长轮询(前端接口递归调用)<br> 想解决短轮询的缺点(想将实时性更强)<br>长轮询存在的问题<ul><li>实时性强了,同时也造成了更多的网络请求</li><li>链接堆积问题,链接需要在服务端中保持打开,占有服务器资源(前端需要大量数据从服务端访问,会一直从服务端获取)<br>优点</li><li>实时性强了,但是要求服务端的并发能力要强</li></ul><hr></li><li>3.iframe流(使用ifream存在的沙箱模式)<br>存在的问题<ul><li>单通信(服务端直接推送客户端消息)<br>优点</li><li>具有实时性,且不需要客户端和服务端频繁发请求</li></ul><hr></li><li><h2 id="4-sse-EventSource(html提供的,单项通信,客户端可以监控服务端推送的事件。只能推送文本消息,适合小数据)存在的问题-单项传输,客户端无法给服务端传递数据"><a href="#4-sse-EventSource(html提供的,单项通信,客户端可以监控服务端推送的事件。只能推送文本消息,适合小数据)存在的问题-单项传输,客户端无法给服务端传递数据" class="headerlink" title="4.sse EventSource(html提供的,单项通信,客户端可以监控服务端推送的事件。只能推送文本消息,适合小数据)存在的问题- 单项传输,客户端无法给服务端传递数据"></a>4.sse EventSource(html提供的,<em>单项通信</em>,客户端可以监控服务端推送的事件。只能推送文本消息,适合小数据)<br><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 服务端代码</span></span><br><span class="line"> app.<span class="title function_">get</span>(<span class="string">'/clock'</span>,<span class="keyword">function</span>(<span class="params">res,req</span>){</span><br><span class="line"> <span class="comment">// 这里表明服务器传递的是时间流</span></span><br><span class="line"> res.<span class="title function_">setHeader</span>(<span class="string">'Content-Type'</span>,<span class="string">'text/event-stream'</span>);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">setInterval</span>(<span class="function">()=></span>{</span><br><span class="line"> <span class="comment">// 和http协议一样,按照行的方式传输</span></span><br><span class="line"> <span class="comment">// Content-Type:xxx</span></span><br><span class="line"> <span class="comment">// Authorization:xxx</span></span><br><span class="line"> res.<span class="title function_">write</span>(<span class="string">`data:hello\n\n`</span>)</span><br><span class="line"> },<span class="number">1000</span>)</span><br><span class="line"> })</span><br><span class="line"> </span><br><span class="line"><span class="comment">// 客户端代码 script中</span></span><br><span class="line"><span class="keyword">const</span> eventsource = <span class="keyword">new</span> <span class="title class_">EventSource</span>(<span class="string">'xxx接口地址'</span>)</span><br><span class="line"></span><br><span class="line">eventsource.<span class="property">onopen</span> = <span class="keyword">function</span>(<span class="params"></span>){</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Connection opened'</span>);</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 发送消息</span></span><br><span class="line">eventsource.<span class="property">onmessage</span> = <span class="keyword">function</span>(<span class="params">e</span>){</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(e.<span class="property">data</span>)</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><br>存在的问题<br>- 单项传输,客户端无法给服务端传递数据</h2></li><li>5.webSocket(h5提供的api)<br>优点<ul><li><p>双向通信</p></li><li><p>持久连接</p></li><li><p>发送的消息增加帧是非常小的</p></li><li><p>支持多种数据格式</p></li><li><p>天生支持跨域</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 客户端代码</span></span><br><span class="line"><span class="keyword">const</span> ws = <span class="keyword">new</span> <span class="title class_">Websocket</span>(<span class="string">'ws://loacalhost:3000'</span>)</span><br><span class="line"></span><br><span class="line">ws.<span class="property">onopen</span> = <span class="keyword">function</span>(<span class="params"></span>){</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Content opened'</span>)</span><br><span class="line"></span><br><span class="line"> ws.<span class="title function_">send</span>(<span class="string">'hello Serve'</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">ws.<span class="property">onmessage</span> = <span class="keyword">function</span>(<span class="params">e</span>){</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'服务器响应数据:'</span>+ e.<span class="property">data</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 服务端代码</span></span><br><span class="line"><span class="keyword">import</span> express <span class="keyword">from</span> <span class="string">'express'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> http <span class="keyword">from</span> <span class="string">'http'</span><span class="comment">// webSocket首先基于http协议</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> { <span class="title class_">WebSocket</span> } <span class="keyword">from</span> <span class="string">'ws'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> wsServer = <span class="keyword">new</span> <span class="title class_">WebSocket</span>({ server })</span><br><span class="line"></span><br><span class="line">wsServer.<span class="title function_">on</span>(<span class="string">'connection'</span>,<span class="function">(<span class="params">ws</span>)=></span>{</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Connection opend'</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 给客户端发送消息</span></span><br><span class="line"> ws.<span class="title function_">send</span>(<span class="string">'hellow client'</span>)</span><br><span class="line"></span><br><span class="line"> ws.<span class="title function_">on</span>(<span class="string">'message'</span>,<span class="function">(<span class="params">message</span>)=></span>{</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'客户端发送的数据:'</span> + message)</span><br><span class="line"> })</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">server.<span class="title function_">listen</span>(<span class="number">3000</span>)</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p><em><strong>拓展:</strong></em><br> 协议的表示方式?</p><p>以http为例子<br>就是要了解http各种header的使用<br>怎么实现握手的,数据长什么样子,怎么通信</p><p>查看网络得知:<br>webSocket协议</p></li><li><p>请求行显示:请求方式 <em><strong>GET ws://localhost:3000</strong></em> 协议版本 <em><strong>HTTP/1.1</strong></em></p></li><li><p>Connection : Upgrade</p></li><li><p>Upgrade: websocket 升级的协议是什么</p></li><li><p>Sec-Websocket-Version: 13 协议的版本</p></li><li><p>生成<em>Sec-Websocket-Key</em>:用于生成唯一的,保证安全的websocket连接 防止恶意连接 可以用于握手</p></li><li><p>Sec-Websocket-Accept 是根据key算出来的 表示握手成功</p><p>通过wireshark工具可以抓包,了解<br>会生成key->·GBUN9IA5TYXPYgQehlxEUw== 握手的时候创建一个随机的key<br>accept-> TpUkC2LowejLbA6ZRgwSL8Rk4FI= 服务端要响应一个值</p><p>每一次的key都不一样,采用以下方法创建安全的握手连接</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 加密库</span></span><br><span class="line"><span class="keyword">import</span> crypto <span class="keyword">from</span> <span class="string">'crypto'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> number = <span class="string">'258EAFA5-E914-47DA-95CA-C5ABODC85B11const'</span>; <span class="keyword">const</span> websocketKey =<span class="string">'GBIN9IA5TYXPYgQehlxEUw=='</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 采用hah算法生成更新生成摘要输出base64格式 响应给客户端</span></span><br><span class="line"><span class="keyword">const</span> websocketAccept = crypto.<span class="title function_">createHash</span>( <span class="string">'sha1'</span> ).<span class="title function_">update</span>(websocketKey + number).<span class="title function_">digest</span>( <span class="string">'base64'</span> )</span><br></pre></td></tr></tbody></table></figure><p>完整的握手过程</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 服务端代码</span></span><br><span class="line"><span class="keyword">import</span> net <span class="keyword">from</span> <span class="string">'net'</span><span class="comment">// 可以接受原始的消息</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 每个人连接都会产生一个socket</span></span><br><span class="line"><span class="keyword">const</span> server = net.<span class="title function_">createServer</span>(<span class="keyword">function</span>(<span class="params">socket</span>){</span><br><span class="line"> <span class="comment">// 客户端发消息 先握手</span></span><br><span class="line"> socket.<span class="title function_">once</span>(<span class="string">'data'</span>,<span class="keyword">function</span>(<span class="params">data</span>){</span><br><span class="line"> <span class="comment">// 发送的报文 data</span></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * data包含</span></span><br><span class="line"><span class="comment"> GET / HTIP/1.1 *请求行*</span></span><br><span class="line"><span class="comment"> Host: localhost:3000</span></span><br><span class="line"><span class="comment"> Connection: UpgradePragma: no-cacheCache Control: no-cacheUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac 0S X 10 15 7) AppleWebKit/537.36 (KHTMLlike Gecko) Chrome/115.0.0.0 Safari/537.36Upgrade: websocket</span></span><br><span class="line"><span class="comment"> 0rigin: http://127.0.0.1:5500</span></span><br><span class="line"><span class="comment"> Sec WebSocket-Version: 13</span></span><br><span class="line"><span class="comment"> Accept-Encoding: gzip,deflate,brAccept-Language: zh-CN, zh;g=0.9Sec-WebSocket-Key: LFD4X3DrVLhObMnKL0b5K0</span></span><br><span class="line"><span class="comment"> Sec-WebSocket-Extensions: permessage-deflate; client max window bits</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> data = data.<span class="title function_">toString</span>()</span><br><span class="line"> <span class="comment">// 说明要升级成websocket协议 再报文中读取是否已经是websocket协议</span></span><br><span class="line"> <span class="keyword">if</span>(data.<span class="title function_">match</span>(<span class="regexp">/Upgrade:websocket/</span>)){</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 在抓包工具中读取到 key用来解析 ***在报文中都是字符串存在的***</span></span><br><span class="line"><span class="comment"> Host; localhost:3000\r\n</span></span><br><span class="line"><span class="comment"> Connection: Upgrade rn</span></span><br><span class="line"><span class="comment"> Pragma: no-cache rin</span></span><br><span class="line"><span class="comment"> Cache Control: nocache\rin</span></span><br><span class="line"><span class="comment"> User-Agent; Mozilla/5.0 (Macintosh; Intel Mac 05 X 10 15 7) AppleWebkit/537,36 (KHTML, like Gecko</span></span><br><span class="line"><span class="comment"> Upgrade: websocket r\n</span></span><br><span class="line"><span class="comment"> 0rigin: http://127.0.0.1:5500\r\n</span></span><br><span class="line"><span class="comment"> Sec-WebSocket-Version: 13r'nAccept-Encoding: gzip, deflate, bririnAccept-Language: zh-CN zh;a=0.9\r\n</span></span><br><span class="line"><span class="comment"> Sec-WebSocket-Key:P2P2F9kEf/wg18RkzXM8eA==\rin</span></span><br><span class="line"><span class="comment"> Sec-WebSocket-Extensions: permessage-deflate; client max window bits'rinrin</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">let</span> rows = data.<span class="title function_">split</span>(<span class="string">'\r\n'</span>)</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(rows)</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * rows 打印得出</span></span><br><span class="line"><span class="comment"> GETHTTP/1.1Host: localhost:3000'Connection: Uggrade'Pragma; no-cache'Cache-Control; no-cache'"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac 0S X 10 15 7) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/115.0.0.0 Safari/537.36''Uparade: websocket'</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">server.<span class="title function_">listen</span>(<span class="number">3000</span>,<span class="keyword">function</span>(<span class="params"></span>){</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'server start port 3000'</span>)</span><br><span class="line">})</span><br></pre></td></tr></tbody></table></figure></li></ul></li></ul>]]></content>
<summary type="html"><h1 id="通信"><a href="#通信" class="headerlink" title="通信"></a>通信</h1><pre><code>串行通信中,数据通常是在俩个终端之间进行传送,根据数据流的传输方向分为以下三种基本传送方式:单工、半双工和全双工。
</code></pre></summary>
</entry>
<entry>
<title>webpack、vite的自动导包的"爱恨情仇"</title>
<link href="https://ayozoo.github.io/2022/10/19/%20webpack%E3%80%81vite%E7%9A%84%E8%87%AA%E5%8A%A8%E5%AF%BC%E5%8C%85%E7%9A%84-%E7%88%B1%E6%81%A8%E6%83%85%E4%BB%87/"/>
<id>https://ayozoo.github.io/2022/10/19/%20webpack%E3%80%81vite%E7%9A%84%E8%87%AA%E5%8A%A8%E5%AF%BC%E5%8C%85%E7%9A%84-%E7%88%B1%E6%81%A8%E6%83%85%E4%BB%87/</id>
<published>2022-10-19T13:21:48.000Z</published>
<updated>2023-09-26T15:14:58.020Z</updated>
<content type="html"><![CDATA[<h1 id="webpack、vite的自动导包的”爱恨情仇”"><a href="#webpack、vite的自动导包的”爱恨情仇”" class="headerlink" title="webpack、vite的自动导包的”爱恨情仇”"></a>webpack、vite的自动导包的”爱恨情仇”</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>最近在开发Vue3+Vite的统一基座工程,遇到了模块自动导入的不同构建工具的引入方式,特此记录。</p><span id="more"></span><p>在项目中,为了避免频繁导入。大家都会在对应,例如项目使用Webpack 打包工具的。在其项目<em><strong>store</strong></em>文件下其中的index.js 文件中使用webpack提供的api<code>require.context</code>的方法实现自动导入。</p><h2 id="ES-模块规范"><a href="#ES-模块规范" class="headerlink" title="ES 模块规范"></a>ES 模块规范</h2><p>在实现自动导入模块前,我们先了解下<code>ES 模块规范</code>。</p><p>所谓的<code>ES模块规范</code>,即 JavaScript 的标准模块系统,它允许您使用 <code>import</code> 和 <code>export</code> 关键字来导入和导出模块。这是现代 JavaScript 中推荐使用的模块化方式。</p><p>在<code>ES模块规范</code>中,提供了<code>import.meta.glob</code>功能。它允许在项目运行中动态匹配特定模式的模块。例如以下代码就可以动态匹配到<code>module</code>模块下的所有<code>js</code>文件。</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span>.<span class="property">meta</span>.<span class="title function_">glob</span>(<span class="string">'./module/*.js'</span>)</span><br></pre></td></tr></tbody></table></figure><h2 id="话不多说,上干货"><a href="#话不多说,上干货" class="headerlink" title="话不多说,上干货"></a>话不多说,上干货</h2><p>例如,你的项目<em><strong>sotre</strong></em>文件目录为</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">- store</span><br><span class="line"> - module</span><br><span class="line"> - module1.js</span><br><span class="line"> - module2.js</span><br><span class="line"> - ...</span><br><span class="line"> - moduleN.js</span><br><span class="line"> - main.js</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>其中<code>module</code>下的<code>moduleX</code>文件你的<code>main.js</code>为你的<em><strong>sotre</strong></em>入口文件,</p><h3 id="webpack中的自动导入的实现方式"><a href="#webpack中的自动导入的实现方式" class="headerlink" title="webpack中的自动导入的实现方式"></a>webpack中的自动导入的实现方式</h3><p>在<code>main.js</code>入口文件中你可以使用webpack提供<code>require.context</code>的方法实现<code>module</code>文件目录下的模块自动导入。</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> files = <span class="built_in">require</span>.<span class="title function_">context</span>(<span class="string">'./model'</span>, <span class="literal">false</span>, <span class="regexp">/\.js$/</span>)</span><br><span class="line"><span class="keyword">const</span> modules = {}</span><br><span class="line">files.<span class="title function_">keys</span>().<span class="title function_">forEach</span>(<span class="function">(<span class="params">key</span>) =></span> {</span><br><span class="line">modules[key.<span class="title function_">replace</span>(<span class="regexp">/(\.\/|\.js)/g</span>, <span class="string">''</span>)] = <span class="title function_">files</span>(key).<span class="property">default</span></span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> modules</span><br></pre></td></tr></tbody></table></figure><p>其中<code>files.keys()</code>是<code>require.context</code>方法返回的一个函数,它会返回一个包含所有匹配模块路径的数组。</p><p><code>require.context</code>是Webpack提供的一个方法,它允许你在构建时动态地导入模块。该方法接收三个参数:</p><ol><li><code>directory</code>: 表示要搜索的目录路径。</li><li><code>useSubdirectories</code>: 表示是否搜索子目录。</li><li><code>regExp</code>: 表示匹配文件的正则表达式。</li></ol><p>在上面代码中,<code>files</code>就是通过<code>require.context</code>动态导入了<code>./model</code>目录下所有的<code>.js</code>文件,并使用正则表达式<code>/\.js$/</code>来匹配文件。这样,<code>files.keys()</code>返回一个包含所有匹配模块路径的数组。例如控制台打印<code>files.keys()</code>可获得以下数组</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> <span class="string">"./module1.js"</span>,</span><br><span class="line"> <span class="string">"./module2.js"</span>,</span><br><span class="line"> <span class="string">"./。。。.js"</span>,</span><br><span class="line"> <span class="string">"./moduleN.js"</span></span><br><span class="line">]</span><br></pre></td></tr></tbody></table></figure><p>接着,我们可以使用<code>.forEach()</code>遍历这个数组,对每个匹配的模块进行处理,提取模块名,并将模块添加到<code>modules</code>对象中。这样,你就得到了一个以模块名为键、模块对象为值的<code>modules</code>对象,它包含了所有从<code>./model</code>目录中动态导入的模块。</p><h3 id="vite自动导入的实现方式"><a href="#vite自动导入的实现方式" class="headerlink" title="vite自动导入的实现方式"></a>vite自动导入的实现方式</h3><p>在<code>main.js</code>入口文件中你可以使用<code>ES模块规范</code>提供的<code>import.meta.glob</code>方法来获取特定模块的匹配模式,来实现自动导入。</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// main.js</span></span><br><span class="line"><span class="keyword">const</span> modules = {};</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">importAllModules</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">const</span> files = <span class="keyword">import</span>.<span class="property">meta</span>.<span class="title function_">glob</span>(<span class="string">'./module/*.js'</span>);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">const</span> path <span class="keyword">in</span> files) {</span><br><span class="line"> <span class="keyword">const</span> key = path.<span class="title function_">replace</span>(<span class="regexp">/\.\/module\/|\.js/g</span>, <span class="string">''</span>);</span><br><span class="line"> <span class="keyword">const</span> <span class="variable language_">module</span> = <span class="keyword">await</span> files[path]();</span><br><span class="line"> modules[key] = <span class="variable language_">module</span>.<span class="property">default</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">importAllModules().<span class="title function_">then</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(modules); <span class="comment">// 所有模块已导入并组合成对象</span></span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="comment">// 现在可以导出组合后的 modules 对象供其他地方使用</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> modules;</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>在这种情况下,使用异步操作是因为模块导入是一个异步操作。<code>import.meta.glob()</code>方法会返回一个对象,该对象的键是匹配到的文件路径,值是一个函数,调用该函数将异步导入对应的模块。因此,我们需要使用异步操作来等待模块导入完成,然后再将其添加到<code>modules</code>对象中。</p><p>如果我们不使用异步操作,而是直接将模块导入的结果添加到<code>module</code>对象中,那么由于模块导入是异步的,<code>modules</code>对象可能在模块导入完成之前被导出,导致<code>modules</code>对象不完整或为空。使用异步操作可以确保在所有模块导入完成后再导出<code>modules</code>对象,保证其包含所有模块导出的内容。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><h3 id="为什么都是打包工具,会有不同的自动导入方式呢?"><a href="#为什么都是打包工具,会有不同的自动导入方式呢?" class="headerlink" title="为什么都是打包工具,会有不同的自动导入方式呢?"></a>为什么都是打包工具,会有不同的自动导入方式呢?</h3><p>在使用 Webpack 的情况下,与使用 Vite 或其他原生支持 ES 模块的项目相比,自动导入模块的实现会有一些区别。主要区别在于 Webpack 不支持 <code>import.meta.glob</code>,因为它是 ES 模块的一个特殊功能,而 Webpack 是一个打包工具,不完全符合 ES 模块的规范。</p><h3 id="那这里就浅浅对比下俩种打包工具的差异吧"><a href="#那这里就浅浅对比下俩种打包工具的差异吧" class="headerlink" title="那这里就浅浅对比下俩种打包工具的差异吧"></a>那这里就浅浅对比下俩种打包工具的差异吧</h3><h4 id="webpack"><a href="#webpack" class="headerlink" title="webpack"></a>webpack</h4><ul><li>成熟度高。Webpack是一个成熟且应用相当广泛的打包构建工具,具有强大的生态系统和社区支持。</li><li>打包速度较慢。Webpack打包速度在大型项目打包速度比较慢,从入口文件开始,基于代码中的import、export、require构建依赖树,将所有的模块打包到一个或者几个少数文件中。因此,项目规模庞大的话,启动和热更新更慢。每次代码变更构建都需要生成新的Bundle文件。</li><li>配置复杂。官网配置很多,需要处理不同的Loader和Plugin来管理不同的资源文件。</li><li>插件系统丰富。Webpack具有强大的插件系统,允许开发者根据需求扩展定义。</li><li>Tree Shaking。Webpack通过使用UglifyJS等工具进行Tree Shaking,消除未使用的代码</li><li>热模块替换(HMR)。Webpack支持热模块替换,但在某些情况下需要手动配置。</li></ul><h4 id="Vite"><a href="#Vite" class="headerlink" title="Vite"></a>Vite</h4><ul><li>新兴技术。Vite是一个相较新的构建工具,旨在提供更快的开发体验和构建速度。</li><li>打包速度极快。Vite在开发环境下具有极快的启动和热更新速度,因为它采用了原生ES模块的方式,并且将依赖项保持为独立的文件,而不是打包到一个大文件中。</li><li>配置简单。Vite的配置比Webpack简单,尤其是对于常见的项目结构,大部分任务都无需额外配置。</li><li>热模块替换(HMR)。Vite对热模块替换的支持非常好,在开发过程中几乎不需要手动配置即可实现HMR。</li><li>Tree Shaking。Vite使用Rollup进行Tree Shaking,这使得未使用的代码更容易被消除。</li><li>插件系统还不够完善。Vite的插件系统仍在发展中,目前没有Webpack那么丰富的插件支持。</li></ul><p>总之,Webpack更加适合与大型、复杂项目的构建工作,拥有成熟和完善的生态系统和社区。Vite更适合用于一些热更新快速的程序。</p>]]></content>
<summary type="html"><h1 id="webpack、vite的自动导包的”爱恨情仇”"><a href="#webpack、vite的自动导包的”爱恨情仇”" class="headerlink" title="webpack、vite的自动导包的”爱恨情仇”"></a>webpack、vite的自动导包的”爱恨情仇”</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>最近在开发Vue3+Vite的统一基座工程,遇到了模块自动导入的不同构建工具的引入方式,特此记录。</p></summary>
<category term="前端构建工具" scheme="https://ayozoo.github.io/tags/%E5%89%8D%E7%AB%AF%E6%9E%84%E5%BB%BA%E5%B7%A5%E5%85%B7/"/>
</entry>
<entry>
<title>vue3+vite环境变量踩雷</title>
<link href="https://ayozoo.github.io/2022/07/18/%20vue3-vite%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F%E8%B8%A9%E9%9B%B7/"/>
<id>https://ayozoo.github.io/2022/07/18/%20vue3-vite%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F%E8%B8%A9%E9%9B%B7/</id>
<published>2022-07-18T12:12:44.000Z</published>
<updated>2023-09-26T15:14:44.580Z</updated>
<content type="html"><![CDATA[<h1 id="vue3-vite-环境变量踩坑"><a href="#vue3-vite-环境变量踩坑" class="headerlink" title="vue3 + vite 环境变量踩坑"></a>vue3 + vite 环境变量踩坑</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>众所周知,项目搭建过程中,存在环境的区分。一般项目中都会存在<em><strong>env</strong></em>的俩个配置环境变量的配置文件。…</p><span id="more"></span><p>例如以下生产环境、开发环境的环境变量文件:</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//.env.development 生产环境变量文件</span></span><br><span class="line"># 开发环境配置</span><br><span class="line"><span class="variable constant_">ENV</span> = <span class="string">'development'</span></span><br><span class="line"></span><br><span class="line"># 开发环境</span><br><span class="line"><span class="variable constant_">VUE_APP_BASE_API</span> = <span class="string">'/dev-api'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// .env.production</span></span><br><span class="line"># 生产环境配置</span><br><span class="line"><span class="variable constant_">ENV</span> = <span class="string">'production'</span></span><br><span class="line"></span><br><span class="line"># 生产环境</span><br><span class="line"><span class="variable constant_">VUE_APP_BASE_API</span> = <span class="string">'/web-api'</span></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>然后在package.json中配置对应环境启动命令用以启动不同环境</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"scripts"</span>: {</span><br><span class="line"> <span class="string">"dev"</span>: <span class="string">"vue-cli-service --mode development"</span>,</span><br><span class="line"> <span class="string">"build"</span>: <span class="string">"vue-cli-service build --mode production"</span>,</span><br><span class="line"> },</span><br></pre></td></tr></tbody></table></figure><h2 id="什么是环境变量?"><a href="#什么是环境变量?" class="headerlink" title="什么是环境变量?"></a>什么是环境变量?</h2><p>根据代码运行环境变化而变化的变量就是所谓的环境变量。</p><p>在生产环境和开发环境中最多用以区分接口公共<em><strong>BASE_URL_API</strong></em>路径来请求不同接口。</p><p>环境变量一般在全局可以访问得到。在vue项目中,常见的环境变量访问方式是以下方式进行获取的:</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// webpack.config.js</span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = {</span><br><span class="line"> <span class="attr">mode</span>: process.<span class="property">env</span>.<span class="property">NODE_ENV</span> === <span class="string">'production'</span> ? <span class="string">'production'</span> : <span class="string">'development'</span></span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>其中的<u><em><strong>process.env.NODE_ENV</strong></em></u>就是环境变量,他是Node.js提供的API,用以返回获取当前<u><strong>Shall</strong></u>(操作系统和运行环境)所有的环境变量。</p><h2 id="vue2-webPack-与-vue3-vite项目搭建获取环境变量的区别:"><a href="#vue2-webPack-与-vue3-vite项目搭建获取环境变量的区别:" class="headerlink" title="vue2+webPack 与 vue3+vite项目搭建获取环境变量的区别:"></a>vue2+webPack 与 vue3+vite项目搭建获取环境变量的区别:</h2><h3 id="webpack、vite相似之处"><a href="#webpack、vite相似之处" class="headerlink" title="webpack、vite相似之处"></a>webpack、vite相似之处</h3><p>配置都是按照前言所述,创建对应的环境变量的<u><em><strong>env</strong></em></u>文件,配置<u><em><strong>package.json</strong></em></u>文件中对应环境的启动命令。</p><h3 id="webpack中的环境变量"><a href="#webpack中的环境变量" class="headerlink" title="webpack中的环境变量"></a>webpack中的环境变量</h3><p>在vue2项目中,webpack做了处理,使得浏览器可以直接识别获取到node环境的process.env变量。</p><ul><li><p>创建方式:配置在项目中env环境变量配置文件中,webpack环境变量可随意命名</p></li><li><p>获取方式:可以通过***<u>process.env*</u>**获取</p></li></ul><p>所以在页面中可以获取相应的环境变量:</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//这里就是获取启动或者打包的命令获取对应的配置文件中的变量 </span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'当前环境是:'</span>,process.<span class="property">env</span>.<span class="property">NODE_ENV</span>)</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h3 id="vite中的环境变量"><a href="#vite中的环境变量" class="headerlink" title="vite中的环境变量"></a>vite中的环境变量</h3><ul><li><p>创建方式:vite中环境创建存在俩种,都是在项目中env环境变量配置文件中配置:</p><ol><li><p>创建以<u><strong>VITE_</strong></u>开头的环境变量</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"># 开发环境配置</span><br><span class="line"><span class="variable constant_">NODE_ENV</span> =<span class="string">'development'</span></span><br><span class="line"></span><br><span class="line"># 开发环境</span><br><span class="line"><span class="variable constant_">VITE_APP_BASE_API</span> = <span class="string">'/dev-api'</span></span><br><span class="line"></span><br><span class="line"><span class="variable constant_">VITE_APP_URL</span> = <span class="string">"http://193.1.1.107:8042"</span></span><br><span class="line"></span><br><span class="line"><span class="variable constant_">VITE_APP_BASE_PORT</span> = <span class="string">"80"</span></span><br></pre></td></tr></tbody></table></figure></li><li><p>更换<u><strong>VITE_</strong></u>前缀</p><p>以 envPrefix 开头的环境变量会通过 import.meta.env 暴露在你的客户端源码中。参考官网:</p><p>[共享配置]: <a href="https://vitejs.cn/vite3-cn/config/shared-options.html#envdir">https://vitejs.cn/vite3-cn/config/shared-options.html#envdir</a>“VIte官方文档”</p><p>以 envPrefix 开头的环境变量会通过 import.meta.env 暴露在你的客户端源码中。</p><ul><li><p><strong>类型:</strong> string | string[]</p></li><li><p><strong>默认:</strong> VITE_</p></li><li><p><strong>安全注意事项</strong></p><p><strong>envPrefix</strong> 不应被设置为空字符串 <strong>‘ ‘</strong> ,这将暴露你所有的环境变量,导致敏感信息的意外泄漏。 检测到配置为 <strong>‘ ‘</strong> 时 Vite 将会抛出错误.</p></li></ul></li></ol></li><li><p>获取方式:</p><p>Vite是在 <strong>import.meta.env</strong>对象上暴露当前Shall的环境变量。</p><p>以下是获取对象中常用的内在变量:</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 应用运行的模式。</span></span><br><span class="line"><span class="keyword">import</span>.<span class="property">meta</span>.<span class="property">env</span>.<span class="property">MODE</span>: {string} </span><br><span class="line"><span class="comment">// 部署应用时的基本 URL。他由base配置项决定。</span></span><br><span class="line"><span class="keyword">import</span>. meta.<span class="property">env</span>.<span class="property">BASE_URL</span>: {string} </span><br><span class="line"><span class="comment">// 应用是否运行在生产环境。</span></span><br><span class="line"><span class="keyword">import</span>. meta.<span class="property">env</span>.<span class="property">PROD</span>: {boolean}</span><br><span class="line"><span class="comment">// 应用是否运行在开发环境 (永远与 import.meta.env.PROD相反)。</span></span><br><span class="line"><span class="keyword">import</span>. meta.<span class="property">env</span>.<span class="property">DEV</span>: {boolean} </span><br><span class="line"><span class="comment">// 应用是否运行在 </span></span><br><span class="line"><span class="keyword">import</span>.<span class="property">meta</span>.<span class="property">env</span>.<span class="property">SSR</span>: {boolean} server 上。</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>注意:以上的变量存在于运行环境中,对应的vite.config中获取不到。</p><p>在 Vite 的配置文件 vite.config.js 中,默认是不加载 .env 文件中的环境变量的。因为正常情况下,只有在评估完 Vite 配置之后,才知道哪些文件需要被加载,比如之前我们提到的<code>root</code>和<code>envDir</code>都会影响到加载的行为。不管如何,如果想要在配置中加载环境变量,可以使用 <code>loadEnv</code> 去加载环境变量。</p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> { defineConfig, loadEnv } <span class="keyword">from</span> <span class="string">"vite"</span>;</span><br><span class="line"><span class="keyword">import</span> vue <span class="keyword">from</span> <span class="string">"@vitejs/plugin-vue"</span>;</span><br><span class="line"><span class="keyword">import</span> path <span class="keyword">from</span> <span class="string">"path"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// https://vitejs.dev/config/</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title function_">defineConfig</span>(<span class="function">(<span class="params">{ mode }</span>) =></span> {</span><br><span class="line"> <span class="keyword">const</span> env = <span class="title function_">loadEnv</span>(mode, process.<span class="title function_">cwd</span>(), <span class="string">""</span>);</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(env.<span class="property">VITE_APP_BASE_API</span>);</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">resolve</span> = (<span class="params">dir</span>) => path.<span class="title function_">resolve</span>(__dirname, dir);</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> <span class="attr">base</span>: <span class="string">"./"</span>,</span><br><span class="line"> <span class="attr">publicPath</span>: <span class="title function_">resolve</span>(<span class="string">"./static"</span>),</span><br><span class="line"> <span class="attr">assetsPublicPath</span>: <span class="string">"./"</span>,</span><br><span class="line"> <span class="attr">plugins</span>: [<span class="title function_">vue</span>()],</span><br><span class="line"> <span class="attr">extensions</span>: [<span class="string">".js"</span>, <span class="string">".ts"</span>, <span class="string">".jsx"</span>, <span class="string">".tsx"</span>, <span class="string">".json"</span>, <span class="string">".vue"</span>],</span><br><span class="line"> <span class="attr">resolve</span>: {</span><br><span class="line"> <span class="attr">alias</span>: {</span><br><span class="line"> <span class="string">"@"</span>: <span class="title function_">resolve</span>(<span class="string">"src"</span>),</span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line"> <span class="comment">// 本地运行配置,及反向代理配置</span></span><br><span class="line"> <span class="attr">server</span>: {</span><br><span class="line"> <span class="attr">host</span>: <span class="string">"127.0.0.1"</span>,</span><br><span class="line"> <span class="attr">port</span>: <span class="number">80</span>,</span><br><span class="line"> <span class="attr">open</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">strictPort</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">https</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">cors</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">proxy</span>: {</span><br><span class="line"> <span class="comment">// 代理规则直接放在 server 对象下,而非嵌套的 server 对象中</span></span><br><span class="line"> [env.<span class="property">VITE_APP_BASE_API</span>]: {</span><br><span class="line"> <span class="attr">target</span>: <span class="string">"http://172.1.1.210:8080"</span>,</span><br><span class="line"> <span class="attr">changeOrigin</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">rewrite</span>: <span class="function">(<span class="params">path</span>) =></span> path.<span class="title function_">replace</span>(<span class="keyword">new</span> <span class="title class_">RegExp</span>(<span class="string">"^"</span> + env.<span class="property">VITE_APP_BASE_API</span>), <span class="string">"/web-api"</span>), <span class="comment">// 替换 /dev-api 为 target 接口地址</span></span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line"> </span><br><span class="line"> };</span><br><span class="line">});</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>注意,mode必须解构后传入loadEnv mode变量中</p></li></ul>]]></content>
<summary type="html"><h1 id="vue3-vite-环境变量踩坑"><a href="#vue3-vite-环境变量踩坑" class="headerlink" title="vue3 + vite 环境变量踩坑"></a>vue3 + vite 环境变量踩坑</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>众所周知,项目搭建过程中,存在环境的区分。一般项目中都会存在<em><strong>env</strong></em>的俩个配置环境变量的配置文件。…</p></summary>
<category term="前端构建工具" scheme="https://ayozoo.github.io/tags/%E5%89%8D%E7%AB%AF%E6%9E%84%E5%BB%BA%E5%B7%A5%E5%85%B7/"/>
</entry>
</feed>