袁俊强 發表於 2023-2-28 09:29:00

React学习笔记(二)—— JSX、组件与生命周期

<h1>一、JSX</h1>
<h2>1.1、什么是JSX?</h2>
<p>JSX = JavaScript XML,这是React官方发明的一种JS语法(糖)</p>
<p>概念:JSX是 JavaScript XML(HTML)的缩写,表示在 JS 代码中书写 HTML 结构</p>
<p>设想如下变量声明:</p>
<div class="gatsby-highlight" data-language="jsx">
<pre class="gatsby-code-jsx"><code class="gatsby-code-jsx"><span class="token keyword">const element <span class="token operator">= <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;h1<span class="token punctuation">&gt;<span class="token plain-text">Hello, world!<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/h1<span class="token punctuation">&gt;<span class="token punctuation">;</span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>这个有趣的标签语法既不是字符串也不是 HTML。</p>
<p>它被称为 JSX,是一个 JavaScript 的语法扩展。我们建议在 React 中配合使用 JSX,JSX 可以很好地描述 UI 应该呈现出它应有交互的本质形式。JSX 可能会使人联想到模板语言,但它具有 JavaScript 的全部功能。</p>
<p>JSX 可以生成 React “元素”。</p>
<h2 id="why-jsx">1.2、为什么使用 JSX?</h2>
<p>React 认为渲染逻辑本质上与其他 UI 逻辑内在耦合,比如,在 UI 中需要绑定处理事件、在某些时刻状态发生变化时需要通知到 UI,以及需要在 UI 中展示准备好的数据。</p>
<p>React 并没有采用将<em>标记与逻辑分离到不同文件</em>这种人为的分离方式,而是通过将二者共同存放在称之为“组件”的松散耦合单元之中,来实现<em>关注点分离</em>。我们将在后面章节中深入学习组件。如果你还没有适应在 JS 中使用标记语言,这个会议讨论应该可以说服你。</p>
<p>React&nbsp;不强制要求使用 JSX,但是大多数人发现,在 JavaScript 代码中将 JSX 和 UI 放在一起时,会在视觉上有辅助作用。它还可以使 React 显示更多有用的错误和警告消息。</p>
<p>浏览器默认是不支持JSX的,所以jsx语法必须使用@babel/preset-react进行编译,编译的结果React.createElement()这种Api的代码。</p>
<p>示例:&lt;div&gt;Hello&lt;/div&gt; ==&gt;@babel/preset-react==&gt; React.createElement('div',{},'Hello')</p>
<p>在React开发中,JSX语法是可选的(也就是你可以不使用JSX)。如果不使用JSX语法,React组件代码将变得特别麻烦(难以维护)。所以几乎所有React开发都用的是JSX语法。</p>
<p>JSX是Javascript的一种语法拓展</p>
<p>JSX是JavaScript XML简写,表示在JavaScript中编写XML格式代码(也就是HTML格式)</p>
<p>优势:</p>
<ul>
<ul>
<li>声明式语法更加直观</li>
<li>与HTML结构相同</li>
<li>降低了学习成本、提升开发效率</li>
</ul>
</ul>
<p>注意:JSX 并不是标准的 JS 语法,是 JS 的语法扩展,浏览器默认是不识别的,脚手架中内置的&nbsp;@babel/plugin-transform-react-jsx&nbsp;包,用来解析该语法</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230207151811929-672364097.png" alt="" loading="lazy"></p>
<h2>1.3、JSX中使用js表达式</h2>
<p><code>目标任务:</code>&nbsp;能够在JSX中使用表达式</p>
<p><strong>语法</strong></p>
<p><code>{ JS 表达式 }</code></p>
<pre class="highlighter-hljs"><code class="language-jsx highlighter-hljs hljs language-javascript"><span class="hljs-keyword">const name = <span class="hljs-string">'张三'

&lt;h1&gt;你好,我叫{name}&lt;<span class="hljs-regexp">/h1&gt;   /<span class="hljs-regexp">/    &lt;h1&gt;你好,我叫</span></span></span></span></code><code class="language-jsx highlighter-hljs hljs language-javascript">张三</code><code class="language-jsx highlighter-hljs hljs language-javascript"><span class="hljs-keyword"><span class="hljs-string"><span class="hljs-regexp"><span class="hljs-regexp">&lt;/h1&gt; </span></span></span></span></code></pre>
<p><strong>可以使用的表达式</strong></p>
<ol>
<li>字符串、数值、布尔值、null、undefined、object( [] / {} )</li>
<li>1 + 2、'abc'.split('')、['a', 'b'].join('-')</li>
<li>内置函数,自定义函数</li>
</ol>
<p><strong>特别注意</strong></p>
<p>​ if 语句/ switch-case 语句/ 变量声明语句,这些叫做语句,不是表达式,不能出现在&nbsp;<code>{}</code>&nbsp;中。</p>
<p>可以使用?:与&amp;&amp;替代if的功能</p>
<p>在下面的示例中,我们将调用 JavaScript 函数&nbsp;<code class="gatsby-code-text">formatName(user)</code>&nbsp;的结果,并将结果嵌入到&nbsp;<code class="gatsby-code-text">&lt;h1&gt;</code>&nbsp;元素中。</p>
<div class="gatsby-highlight has-highlighted-lines" data-language="jsx">
<pre class="gatsby-code-jsx"><code class="gatsby-code-jsx"><span class="token keyword">function <span class="token function">formatName<span class="token punctuation">(<span class="token parameter">user<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">return user<span class="token punctuation">.firstName <span class="token operator">+ <span class="token string">' ' <span class="token operator">+ user<span class="token punctuation">.lastName<span class="token punctuation">;
<span class="token punctuation">}

<span class="token keyword">const user <span class="token operator">= <span class="token punctuation">{
<span class="token literal-property property">firstName<span class="token operator">: <span class="token string">'Harper'<span class="token punctuation">,
<span class="token literal-property property">lastName<span class="token operator">: <span class="token string">'Perez'
<span class="token punctuation">}<span class="token punctuation">;

<span class="token keyword">const element <span class="token operator">= <span class="token punctuation">(
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;h1<span class="token punctuation">&gt;<span class="token plain-text">
<span class="gatsby-highlight-code-line"><span class="token plain-text">    Hello, <span class="token punctuation">{<span class="token function">formatName<span class="token punctuation">(user<span class="token punctuation">)<span class="token punctuation">}<span class="token plain-text">!<span class="token plain-text"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/h1<span class="token punctuation">&gt;
<span class="token punctuation">)<span class="token punctuation">;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>为了便于阅读,我们会将 JSX 拆分为多行。同时,我们建议将内容包裹在括号中,虽然这样做不是强制要求的,但是这可以避免遇到自动插入分号陷阱。</p>
<h2 id="3-jsx列表渲染">1.4. JSX列表渲染</h2>
<h3>1.4.1、map函数</h3>
<blockquote>
<p><code>map()</code>方法定义在JavaScript的<code>Array</code>中,它返回一个新的数组,数组中的元素为原始数组调用函数处理后的值。<br>注意:</p>
















</blockquote>
<ul>
<ul>
<li><code>map()</code>不会对空数组进行检测</li>
<li><code>map()</code>不会改变原始数组</li>
















</ul>















</ul>
<div class="cnblogs_code">
<pre>array.map(<span style="color: rgba(0, 0, 255, 1)">function</span>(currentValue, index, arr), thisValue)</pre>
</div>
<p>参数说明:</p>
<ul>
<li><code>function(currentValue, index, arr)</code>:必须。为一个函数,数组中的每个元素都会执行这个函数。其中函数参数:</li>
</ul>
<blockquote><ol>
<li><code>currentValue</code>:必须。当前元素的的值。</li>
<li><code>index</code>:可选。当前元素的索引。</li>
<li><code>arr</code>:可选。当前元素属于的数组对象。</li>
</ol></blockquote>
<ul>
<li><code>thisValue</code>:可选。对象作为该执行回调时使用,传递给函数,用作"<code>this</code>"的值。</li>
</ul>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">返回由原数组中每个元素的平方组成的新数组:</span>
<span style="color: rgba(0, 0, 0, 1)">
let array </span>= ;

let newArray </span>= array.map((item) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> item *<span style="color: rgba(0, 0, 0, 1)"> item;
})

console.log(newArray)</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> </span></pre>
</div>
<p>&nbsp;</p>
<h3>1.4.2、JSX列表渲染</h3>
<p>JSX 表达式必须具有一个父元素。没有父元素时请使用&lt;&gt;&lt;/&gt;</p>
<p><code>目标任务:</code>&nbsp;能够在JSX中实现列表渲染</p>
<blockquote>
<p>页面的构建离不开重复的列表结构,比如歌曲列表,商品列表等,我们知道vue中用的是v-for,react这边如何实现呢?</p>
</blockquote>
<p>实现:使用数组的<code>map</code>&nbsp;方法</p>
<p>案例:</p>
<pre class="highlighter-hljs"><code class="language-jsx highlighter-hljs hljs language-javascript"><span class="hljs-comment">// 列表
<span class="hljs-keyword">const songs = [
{ <span class="hljs-attr">id: <span class="hljs-number">1, <span class="hljs-attr">name: <span class="hljs-string">'痴心绝对' },
{ <span class="hljs-attr">id: <span class="hljs-number">2, <span class="hljs-attr">name: <span class="hljs-string">'像我这样的人' },
{ <span class="hljs-attr">id: <span class="hljs-number">3, <span class="hljs-attr">name: <span class="hljs-string">'南山南' }
]

<span class="hljs-keyword">function <span class="hljs-title function_">App(<span class="hljs-params">) {
<span class="hljs-keyword">return (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div <span class="hljs-attr">className=<span class="hljs-string">"App"&gt;
      <span class="hljs-tag">&lt;<span class="hljs-name">ul&gt;
      {
          songs.map(item =&gt; <span class="hljs-tag">&lt;<span class="hljs-name">li&gt;{item.name}<span class="hljs-tag">&lt;/<span class="hljs-name">li&gt;)
      }
      <span class="hljs-tag">&lt;/<span class="hljs-name">ul&gt;
    <span class="hljs-tag">&lt;/<span class="hljs-name">div&gt;
)
}

<span class="hljs-keyword">export <span class="hljs-keyword">default <span class="hljs-title class_">App
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>注意点:需要为遍历项添加&nbsp;<code>key</code>&nbsp;属性</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230207162625898-863390799.png" alt="" loading="lazy"></p>
<ol>
<li>key 在 HTML 结构中是看不到的,是 React 内部用来进行性能优化时使用</li>
<li>key 在当前列表中要唯一的字符串或者数值(String/Number)</li>
<li>如果列表中有像 id 这种的唯一值,就用 id 来作为 key 值</li>
<li>如果列表中没有像 id 这种的唯一值,就可以使用 index(下标)来作为 key 值</li>
</ol>
<h2>1.5、JSX条件渲染</h2>
<p><code>目标任务:</code>&nbsp;能够在JSX中实现条件渲染</p>
<p>作用:根据是否满足条件生成HTML结构,比如Loading效果</p>
<p>实现:可以使用&nbsp;<code>三元运算符</code>&nbsp;或&nbsp;<code>逻辑与(&amp;&amp;)运算符</code></p>
<p>案例:</p>
<pre class="highlighter-hljs"><code class="language-jsx highlighter-hljs hljs language-javascript"><span class="hljs-comment">// 来个布尔值
<span class="hljs-keyword">const flag = <span class="hljs-literal">true
<span class="hljs-keyword">function <span class="hljs-title function_">App(<span class="hljs-params">) {
<span class="hljs-keyword">return (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div <span class="hljs-attr">className=<span class="hljs-string">"App"&gt;
      {/* 条件渲染字符串 */}
      {flag ? 'react真有趣' : 'vue真有趣'}
      {/* 条件渲染标签/组件 */}
      {flag ? <span class="hljs-tag">&lt;<span class="hljs-name">span&gt;this is span<span class="hljs-tag">&lt;/<span class="hljs-name">span&gt; : null}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div&gt;
)
}
<span class="hljs-keyword">export <span class="hljs-keyword">default <span class="hljs-title class_">App</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h2 id="5-jsx样式处理">1.6. JSX样式处理</h2>
<p><code>目标任务:</code>&nbsp;能够在JSX中实现css样式处理</p>
<ul>
<li>
<p>行内样式 - style</p>
<div>
<div>import ReactDOM from "react-dom/client";</div>
<br>
<div>//1、创建根节点</div>
<div>let root = ReactDOM.createRoot(document.getElementById("root"));</div>

























                        <br>
<div>let songs = ["好汉歌", "南山南", "滴答"];</div>

























                        <br>
<div>//3、将Vnode渲染到根结点上</div>
<div>root.render(</div>
<div>&nbsp; &lt;div&gt;</div>
<div>&nbsp; &nbsp; &lt;h2 style={{ color: "red", backgroundColor: "yellow" }}&gt;歌曲列表&lt;/h2&gt;</div>
<div>&nbsp; &nbsp; &lt;ul&gt;</div>
<div>&nbsp; &nbsp; &nbsp; {songs.map((item) =&gt; (</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &lt;li key={item}&gt;{item}&lt;/li&gt;</div>
<div>&nbsp; &nbsp; &nbsp; ))}</div>
<div>&nbsp; &nbsp; &lt;/ul&gt;</div>
<div>&nbsp; &lt;/div&gt;</div>
<div>);</div>

























                </div>
























        </li>
<li>
<p>行内样式 - style - 更优写法</p>
<pre class="highlighter-hljs"><code class="language-jsx highlighter-hljs hljs language-javascript"><span class="hljs-keyword">const styleObj = {
    <span class="hljs-attr">color:red
}

<span class="hljs-keyword">function <span class="hljs-title function_">App(<span class="hljs-params">) {
<span class="hljs-keyword">return (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div <span class="hljs-attr">className=<span class="hljs-string">"App"&gt;
      <span class="hljs-tag">&lt;<span class="hljs-name">div <span class="hljs-attr">style=<span class="hljs-string">{ <span class="hljs-attr">styleObj }&gt;this is a div<span class="hljs-tag">&lt;/<span class="hljs-name">div&gt;
    <span class="hljs-tag">&lt;/<span class="hljs-name">div&gt;
)
}

<span class="hljs-keyword">export <span class="hljs-keyword">default <span class="hljs-title class_">App
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</li>
<li>
<p>类名 - className(推荐)</p>
<p><code>app.css</code></p>
<pre class="highlighter-hljs"><code class="language-css highlighter-hljs hljs"><span class="hljs-selector-class">.title {
<span class="hljs-attribute">font-size: <span class="hljs-number">30px;
<span class="hljs-attribute">color: blue;
}
</span></span></span></span></code></pre>
<p><code>app.js</code></p>
<pre class="highlighter-hljs"><code class="language-jsx highlighter-hljs hljs language-javascript"><span class="hljs-keyword">import <span class="hljs-string">'./app.css'

<span class="hljs-keyword">function <span class="hljs-title function_">App(<span class="hljs-params">) {
<span class="hljs-keyword">return (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div <span class="hljs-attr">className=<span class="hljs-string">"App"&gt;
      <span class="hljs-tag">&lt;<span class="hljs-name">div <span class="hljs-attr">className=<span class="hljs-string">'title'&gt;this is a div<span class="hljs-tag">&lt;/<span class="hljs-name">div&gt;
    <span class="hljs-tag">&lt;/<span class="hljs-name">div&gt;
)
}
<span class="hljs-keyword">export <span class="hljs-keyword">default <span class="hljs-title class_">App
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</li>
<li>
<p>类名 - className - 动态类名控制</p>
<pre class="highlighter-hljs"><code class="language-jsx highlighter-hljs hljs language-javascript"><span class="hljs-keyword">import <span class="hljs-string">'./app.css'
<span class="hljs-keyword">const showTitle = <span class="hljs-literal">true
<span class="hljs-keyword">function <span class="hljs-title function_">App(<span class="hljs-params">) {
<span class="hljs-keyword">return (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div <span class="hljs-attr">className=<span class="hljs-string">"App"&gt;
      <span class="hljs-tag">&lt;<span class="hljs-name">div <span class="hljs-attr">className=<span class="hljs-string">{ <span class="hljs-attr">showTitle ? '<span class="hljs-attr">title' <span class="hljs-attr">: ''}&gt;this is a div<span class="hljs-tag">&lt;/<span class="hljs-name">div&gt;
    <span class="hljs-tag">&lt;/<span class="hljs-name">div&gt;
)
}
<span class="hljs-keyword">export <span class="hljs-keyword">default <span class="hljs-title class_">App</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</li>
</ul>
<h2>1.7、注意事项</h2>
<ol>
<li>JSX必须有一个根节点,如果没有根节点,可以使用<code>&lt;&gt;&lt;/&gt;</code>(幽灵节点)替代</li>
<li>所有标签必须形成闭合,成对闭合或者自闭合都可以</li>
<li>JSX中的语法更加贴近JS语法,属性名采用驼峰命名法&nbsp;<code>class -&gt; className</code>&nbsp;<code>for -&gt; htmlFor</code></li>
<li>JSX支持多行(换行),如果需要换行,需使用<code>()</code>&nbsp;包裹,防止bug出现</li>
</ol>
<h2 id="jsx-is-an-expression-too">1.8、JSX 也是一个表达式</h2>
<p>在编译之后,JSX 表达式会被转为普通 JavaScript 函数调用,并且对其取值后得到 JavaScript 对象。</p>
<p>也就是说,你可以在&nbsp;<code class="gatsby-code-text">if</code>&nbsp;语句和&nbsp;<code class="gatsby-code-text">for</code>&nbsp;循环的代码块中使用 JSX,将 JSX 赋值给变量,把 JSX 当作参数传入,以及从函数中返回 JSX:</p>
<div class="gatsby-highlight has-highlighted-lines" data-language="jsx">
<pre class="gatsby-code-jsx"><code class="gatsby-code-jsx"><span class="token keyword">function <span class="token function">getGreeting<span class="token punctuation">(<span class="token parameter">user<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">if <span class="token punctuation">(user<span class="token punctuation">) <span class="token punctuation">{
<span class="gatsby-highlight-code-line">    <span class="token keyword">return <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;h1<span class="token punctuation">&gt;<span class="token plain-text">Hello, <span class="token punctuation">{<span class="token function">formatName<span class="token punctuation">(user<span class="token punctuation">)<span class="token punctuation">}<span class="token plain-text">!<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/h1<span class="token punctuation">&gt;<span class="token punctuation">;<span class="token punctuation">}
<span class="gatsby-highlight-code-line"><span class="token keyword">return <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;h1<span class="token punctuation">&gt;<span class="token plain-text">Hello, Stranger.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/h1<span class="token punctuation">&gt;<span class="token punctuation">;<span class="token punctuation">}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<h2 id="specifying-attributes-with-jsx">1.9、JSX 中指定属性</h2>
<p>你可以通过使用引号,来将属性值指定为字符串字面量:</p>
<div class="gatsby-highlight" data-language="jsx">
<pre class="gatsby-code-jsx"><code class="gatsby-code-jsx"><span class="token keyword">const element <span class="token operator">= <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;a <span class="token attr-name">href<span class="token attr-value"><span class="token punctuation attr-equals">=<span class="token punctuation">"https://www.reactjs.org<span class="token punctuation">"<span class="token punctuation">&gt;<span class="token plain-text"> link <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/a<span class="token punctuation">&gt;<span class="token punctuation">;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>也可以使用大括号,来在属性值中插入一个 JavaScript 表达式:</p>
<div class="gatsby-highlight" data-language="jsx">
<pre class="gatsby-code-jsx"><code class="gatsby-code-jsx"><span class="token keyword">const element <span class="token operator">= <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;img <span class="token attr-name">src<span class="token script language-javascript"><span class="token script-punctuation punctuation">=<span class="token punctuation">{user<span class="token punctuation">.avatarUrl<span class="token punctuation">}<span class="token punctuation">&gt;<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/img<span class="token punctuation">&gt;<span class="token punctuation">;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>在属性中嵌入 JavaScript 表达式时,不要在大括号外面加上引号。你应该仅使用引号(对于字符串值)或大括号(对于表达式)中的一个,对于同一属性不能同时使用这两种符号。</p>
<blockquote>
<p>警告:</p>
<p>因为 JSX 语法上更接近 JavaScript 而不是 HTML,所以 React DOM 使用&nbsp;<code class="gatsby-code-text">camelCase</code>(小驼峰命名)来定义属性的名称,而不使用 HTML 属性名称的命名约定。</p>
<p>例如,JSX 里的&nbsp;<code class="gatsby-code-text">class</code>&nbsp;变成了&nbsp;<code class="gatsby-code-text">className</code>,而&nbsp;<code class="gatsby-code-text">tabindex</code>&nbsp;则变为&nbsp;<code class="gatsby-code-text">tabIndex</code>。</p>
</blockquote>
<p>属性也可以是一个箭头函数:</p>
<div class="cnblogs_code">
<pre>import ReactDOM from "react-dom/client"<span style="color: rgba(0, 0, 0, 1)">;
import React from </span>"react"<span style="color: rgba(0, 0, 0, 1)">;
import </span>"./App.css"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">1、创建根节点</span>
let root = ReactDOM.createRoot(document.getElementById("root"<span style="color: rgba(0, 0, 0, 1)">));

let counter </span>= 0<span style="color: rgba(0, 0, 0, 1)">;
const element </span>=<span style="color: rgba(0, 0, 0, 1)"> (
</span>&lt;div&gt;
    &lt;h2&gt;计算器&lt;/h2&gt;
    &lt;<span style="color: rgba(0, 0, 0, 1)">button
      onClick</span>={() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      counter</span>++<span style="color: rgba(0, 0, 0, 1)">;
      console.log(counter);
      }}
    </span>&gt;<span style="color: rgba(0, 0, 0, 1)">
      点击加1
    </span>&lt;/button&gt;
&lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">3、将Vnode渲染到根结点上</span>
root.render(element);</pre>
</div>
<p>也可以是一个普通函数:</p>
<div class="cnblogs_code">
<pre>import ReactDOM from "react-dom/client"<span style="color: rgba(0, 0, 0, 1)">;
import React from </span>"react"<span style="color: rgba(0, 0, 0, 1)">;
import </span>"./App.css"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">1、创建根节点</span>
let root = ReactDOM.createRoot(document.getElementById("root"<span style="color: rgba(0, 0, 0, 1)">));

let counter </span>= 0<span style="color: rgba(0, 0, 0, 1)">;
const element </span>=<span style="color: rgba(0, 0, 0, 1)"> (
</span>&lt;div&gt;
    &lt;h2&gt;计算器&lt;/h2&gt;
    &lt;button onClick={increment}&gt;点击加1&lt;/button&gt;
&lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> increment() {
counter</span>++<span style="color: rgba(0, 0, 0, 1)">;
console.log(counter);
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">3、将Vnode渲染到根结点上</span>
root.render(element);</pre>
</div>
<h2 id="specifying-children-with-jsx">1.10、使用 JSX 指定子元素</h2>
<p>假如一个标签里面没有内容,你可以使用&nbsp;<code class="gatsby-code-text">/&gt;</code>&nbsp;来闭合标签,就像 XML 语法一样:</p>
<div class="gatsby-highlight" data-language="jsx">
<pre class="gatsby-code-jsx"><code class="gatsby-code-jsx"><span class="token keyword">const element <span class="token operator">= <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;img <span class="token attr-name">src<span class="token script language-javascript"><span class="token script-punctuation punctuation">=<span class="token punctuation">{user<span class="token punctuation">.avatarUrl<span class="token punctuation">} <span class="token punctuation">/&gt;<span class="token punctuation">;</span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>JSX 标签里能够包含很多子元素:</p>
<div class="gatsby-highlight" data-language="jsx">
<pre class="gatsby-code-jsx"><code class="gatsby-code-jsx"><span class="token keyword">const element <span class="token operator">= <span class="token punctuation">(
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;div<span class="token punctuation">&gt;<span class="token plain-text">
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;h1<span class="token punctuation">&gt;<span class="token plain-text">Hello!<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/h1<span class="token punctuation">&gt;<span class="token plain-text">
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;h2<span class="token punctuation">&gt;<span class="token plain-text">Good to see you here.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/h2<span class="token punctuation">&gt;<span class="token plain-text">
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/div<span class="token punctuation">&gt;
<span class="token punctuation">)<span class="token punctuation">;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<h2 id="jsx-prevents-injection-attacks">1.11、JSX 防止注入攻击</h2>
<p>你可以安全地在 JSX 当中插入用户输入内容:</p>
<div class="gatsby-highlight" data-language="jsx">
<pre class="gatsby-code-jsx"><code class="gatsby-code-jsx"><span class="token keyword">const title <span class="token operator">= response<span class="token punctuation">.potentiallyMaliciousInput<span class="token punctuation">;
<span class="token comment">// 直接使用是安全的:
<span class="token keyword">const element <span class="token operator">= <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;h1<span class="token punctuation">&gt;<span class="token punctuation">{title<span class="token punctuation">}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/h1<span class="token punctuation">&gt;<span class="token punctuation">;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>React DOM 在渲染所有输入内容之前,默认会进行转义。它可以确保在你的应用中,永远不会注入那些并非自己明确编写的内容。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止&nbsp;XSS(cross-site-scripting, 跨站脚本)攻击。</p>
<h2 id="jsx-represents-objects">1.12、JSX 表示对象</h2>
<p>Babel 会把 JSX 转译成一个名为&nbsp;<code class="gatsby-code-text">React.createElement()</code>&nbsp;函数调用。</p>
<p>以下两种示例代码完全等效:</p>
<div class="gatsby-highlight" data-language="jsx">
<pre class="gatsby-code-jsx"><code class="gatsby-code-jsx"><span class="token keyword">const element <span class="token operator">= <span class="token punctuation">(
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;h1 <span class="token attr-name">className<span class="token attr-value"><span class="token punctuation attr-equals">=<span class="token punctuation">"greeting<span class="token punctuation">"<span class="token punctuation">&gt;<span class="token plain-text">
    Hello, world!
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/h1<span class="token punctuation">&gt;
<span class="token punctuation">)<span class="token punctuation">;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<div class="gatsby-highlight" data-language="jsx">
<pre class="gatsby-code-jsx"><code class="gatsby-code-jsx"><span class="token keyword">const element <span class="token operator">= React<span class="token punctuation">.<span class="token function">createElement<span class="token punctuation">(
<span class="token string">'h1'<span class="token punctuation">,
<span class="token punctuation">{<span class="token literal-property property">className<span class="token operator">: <span class="token string">'greeting'<span class="token punctuation">}<span class="token punctuation">,
<span class="token string">'Hello, world!'
<span class="token punctuation">)<span class="token punctuation">;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p><code class="gatsby-code-text">React.createElement()</code>&nbsp;会预先执行一些检查,以帮助你编写无错代码,但实际上它创建了一个这样的对象:</p>
<div class="gatsby-highlight" data-language="jsx">
<pre class="gatsby-code-jsx"><code class="gatsby-code-jsx"><span class="token comment">// 注意:这是简化过的结构
<span class="token keyword">const element <span class="token operator">= <span class="token punctuation">{
<span class="token literal-property property">type<span class="token operator">: <span class="token string">'h1'<span class="token punctuation">,
<span class="token literal-property property">props<span class="token operator">: <span class="token punctuation">{
    <span class="token literal-property property">className<span class="token operator">: <span class="token string">'greeting'<span class="token punctuation">,
    <span class="token literal-property property">children<span class="token operator">: <span class="token string">'Hello, world!'
<span class="token punctuation">}
<span class="token punctuation">}<span class="token punctuation">;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>这些对象被称为 “React 元素”。它们描述了你希望在屏幕上看到的内容。React 通过读取这些对象,然后使用它们来构建 DOM 以及保持随时更新。</p>
<div class="cnblogs_code">
<pre>import ReactDOM from "react-dom/client"<span style="color: rgba(0, 0, 0, 1)">;
import React from </span>"react"<span style="color: rgba(0, 0, 0, 1)">;
import </span>"./App.css"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">1、创建根节点</span>
let root = ReactDOM.createRoot(document.getElementById("root"<span style="color: rgba(0, 0, 0, 1)">));

const element </span>=<span style="color: rgba(0, 0, 0, 1)"> React.createElement(
</span>"h1"<span style="color: rgba(0, 0, 0, 1)">,
{ className: </span>"blueBg"<span style="color: rgba(0, 0, 0, 1)"> },
</span>"Hello, world!"<span style="color: rgba(0, 0, 0, 1)">
);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">3、将Vnode渲染到根结点上</span>
root.render(element);</pre>
</div>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230208111022977-968213242.png" alt="" loading="lazy"></p>
<h1>二、组件 Component</h1>
<div>
<div>如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展,但如果,我们将一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了。如果我们将一个个功能块拆分后,就可以像搭建积木一下来搭建我们的项目。</div>
<div>&nbsp;</div>
<div><img src="https://img2022.cnblogs.com/blog/63651/202211/63651-20221101160540109-1097535676.png" alt="" width="697" height="456" class="medium-zoom-image" loading="lazy"></div>
</div>
<div>&nbsp;</div>
<h2>2.1、SPA</h2>
<p>SPA指的是Single Page Application,就是只有一张Web页面的应用。单页应用程序 (SPA) 是加载单个HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序。 浏览器一开始会加载必需的HTML、CSS和JavaScript,所有的操作都在这张页面上完成,都由JavaScript来控制。因此,对单页应用来说模块化的开发和设计显得相当重要。</p>
<p>单页Web应用,顾名思义,就是只有一张Web页面的应用。浏览器一开始会加载必需的HTML、CSS和JavaScript,之后所有的操作都在这张页面上完成,这一切都由JavaScript来控制。因此,单页Web应用会包含大量的JavaScript代码,复杂度可想而知,模块化开发和设计的重要性不言而喻。</p>
<p>速度:更好的用户体验,让用户在web app感受native app的速度和流畅</p>
<p>MVVM:经典MVVM开发模式,前后端各负其责</p>
<p>ajax:重前端,业务逻辑全部在本地操作,数据都需要通过AJAX同步、提交</p>
<p>路由:在URL中采用#号来作为当前视图的地址,改变#号后的参数,页面并不会重载</p>
<p><strong>优点:</strong></p>
<p>1.分离前后端关注点,前端负责View,后端负责Model,各司其职;<br>2.服务器只接口提供数据,不用展示逻辑和页面合成,提高性能;<br>3.同一套后端程序代码,不用修改兼容Web界面、手机;<br>4.用户体验好、快,内容的改变不需要重新加载整个页面<br>5.可以缓存较多数据,减少服务器压力<br>6.单页应用像网络一样,几乎随处可以访问—不像大多数的桌面应用,用户可以通过任务网络连接和适当的浏览器访问单页应用。如今,这一名单包括智能手机、平板电脑、电视、笔记本电脑和台式计算机。
</p>
<p><strong>缺点:</strong></p>
<p>1.SEO问题<br>2.刚开始的时候加载可能慢很多<br>3.用户操作需要写逻辑,前进、后退等<br>4.页面复杂度提高很多,复杂逻辑难度成倍</p>
<h2 id="什么是组件?">2.2、什么是组件?</h2>
<p>组件允许你将 UI 拆分为独立可复用的代码片段,并对每个片段进行独立构思。</p>
<p>组件,从概念上类似于 JavaScript 函数。它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素。</p>
<p>组件它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。仔细想想,几乎任意类型的应用界面都可以抽象为一个组件树:</p>
<p><img src="https://img2018.cnblogs.com/blog/63651/201901/63651-20190102081544274-1397029476.png" alt="" class="medium-zoom-image"></p>
<p>组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际应用中,组件常常被组织成层层嵌套的树状结构:</p>
<p><img src="https://cn.vuejs.org/assets/components.7fbb3771.png" alt="组件树" class="medium-zoom-image"></p>
<h2>2.3、组件定义</h2>
<p>组件是 React的核心慨念,定 React应用程序的基石。组件将应用的UI拆分成独立的、可复用的模块,React 用任厅止定田一个一个组件搭建而成的。</p>
<p>定义一个组件有两种方式,便用ES 6 class(类组件)和使用函数(函数组件)。我们先介绍使用class定义组件方式。</p>
<h3>2.3.1、使用class定义组件</h3>
<p>使用class定义组件需要满足两个条件:</p>
<p>(1)class继承自 React.Component。</p>
<p>(2) class内部必须定义 render方法,render方法返回代表该组件UI的React元素。</p>
<p>使用create-react-app新建一个简易BBS项目,在这个项目中定义一个组件PostList,用于展示BBS 的帖子列表。</p>
<p>PostList的定义如下:</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

class PostList extends Component {
render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div&gt;
      &lt;h2&gt;帖子列表:&lt;/h2&gt;
      &lt;ul&gt;
          &lt;li&gt;男子吃霸王餐还教育老板和气生财&lt;/li&gt;
          &lt;li&gt;莫斯科险遭无人机炸弹攻击&lt;/li&gt;
          &lt;li&gt;武汉官方把房地产归为困难行业&lt;/li&gt;
      &lt;/ul&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> PostList;</pre>
</div>
<p>index.js</p>
<div class="cnblogs_code">
<pre>import ReactDOM from "react-dom/client"<span style="color: rgba(0, 0, 0, 1)">;
import React from </span>"react"<span style="color: rgba(0, 0, 0, 1)">;
import PostList from </span>"./PostList"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">1、创建根节点</span>
let root = ReactDOM.createRoot(document.getElementById("root"<span style="color: rgba(0, 0, 0, 1)">));

let vNode </span>= &lt;PostList /&gt;;

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">3、将Vnode渲染到根结点上</span>
root.render(vNode);</pre>
</div>
<p>运行效果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230209093443986-1841711301.png" alt="" width="328" height="183" loading="lazy"></p>
<div class="cnblogs_code">
<pre>type MyProps =<span style="color: rgba(0, 0, 0, 1)"> { ... };

type MyState </span>=<span style="color: rgba(0, 0, 0, 1)"> { value: string };

class App extends React.Component</span>&lt;MyProps, MyState&gt; { ... }</pre>
</div>
<p><img src="https://img2024.cnblogs.com/blog/63651/202401/63651-20240103113404968-1226396651.png" alt="" width="611" height="108" loading="lazy"></p>
<h3>2.3.2、使用函数定义组件</h3>
<p>定义组件最简单的方式就是编写 JavaScript 函数:</p>
<div class="gatsby-highlight" data-language="jsx">
<pre class="gatsby-code-jsx"><code class="gatsby-code-jsx"><span class="token keyword">function <span class="token function">Welcome<span class="token punctuation">(<span class="token parameter">props<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">return <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;h1<span class="token punctuation">&gt;<span class="token plain-text">Hello, <span class="token punctuation">{props<span class="token punctuation">.name<span class="token punctuation">}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/h1<span class="token punctuation">&gt;<span class="token punctuation">;
<span class="token punctuation">}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>该函数是一个有效的 React 组件,因为它接收唯一带有数据的 “props”(代表属性)对象与并返回一个 React 元素。这类组件被称为“函数组件”,因为它本质上就是 JavaScript 函数。</p>
<p>index.js内容如下:</p>
<div class="cnblogs_code">
<pre>import ReactDOM from "react-dom/client"<span style="color: rgba(0, 0, 0, 1)">;
import React from </span>"react"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">1、创建根节点</span>
let root = ReactDOM.createRoot(document.getElementById("root"<span style="color: rgba(0, 0, 0, 1)">));

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">2、定义函数组件</span>
<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Welcome(props) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;h2&gt;Hello {props.name}!&lt;/h2&gt;;
<span style="color: rgba(0, 0, 0, 1)">}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">3、使用组件</span>
let vNode = &lt;Welcome name="zhangguo" /&gt;;

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">4、将Vnode渲染到根结点上</span>
root.render(vNode);</pre>
</div>
<p>运行结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230209093947860-641714067.png" alt="" loading="lazy"></p>
<p>&nbsp;</p>
<p style="padding: 0; border: 0; font-size: 15px; vertical-align: baseline; box-sizing: border-box; color: rgba(29, 33, 41, 1); font-family: UbuntuMono, &quot;Varela Round&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, Helvetica, Arial, Menlo, Monaco, monospace, sans-serif; background-color: rgba(247, 248, 250, 1)"><strong>约定说明</strong></p>
<ol style="margin-top: 22px; margin-right: 0; margin-bottom: 22px; padding: 0 0 0 28px; border: 0; font-size: 15px; vertical-align: baseline; box-sizing: border-box; list-style: auto; color: rgba(29, 33, 41, 1); font-family: UbuntuMono, &quot;Varela Round&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, Helvetica, Arial, Menlo, Monaco, monospace, sans-serif; background-color: rgba(247, 248, 250, 1)">
<li style="margin-top: 0; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; border: 0; vertical-align: baseline; box-sizing: border-box; list-style-type: decimal">
<p style="padding: 0; border: 0; vertical-align: baseline; box-sizing: border-box">组件的名称<strong>必须首字母大写</strong>,react内部会根据这个来判断是组件还是普通的HTML标签</p>
</li>
<li style="margin-top: 0; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; border: 0; vertical-align: baseline; box-sizing: border-box; list-style-type: decimal">
<p style="padding: 0; border: 0; vertical-align: baseline; box-sizing: border-box">函数组件<strong>必须有返回值</strong>,表示该组件的 UI 结构;如果不需要渲染任何内容,则返回 null</p>
</li>
<li style="margin-top: 0; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; border: 0; vertical-align: baseline; box-sizing: border-box; list-style-type: decimal">
<p style="padding: 0; border: 0; vertical-align: baseline; box-sizing: border-box">组件就像 HTML 标签一样可以被渲染到页面中。组件表示的是一段结构内容,对于函数组件来说,渲染的内容是函数的<strong>返回值</strong>就是对应的内容</p>
</li>
<li style="margin-top: 0; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; border: 0; vertical-align: baseline; box-sizing: border-box; list-style-type: decimal">
<p style="padding: 0; border: 0; vertical-align: baseline; box-sizing: border-box">使用函数名称作为组件标签名称,可以成对出现也可以自闭合</p>
</li>
</ol>
<h2>2.4、组件的props</h2>
<p>2.3.1中的PostList 中的每一个帖子都使用一个标签直接包裹,但一个帖子不仅包含能子的标题,还会包含帖子的创建人、帖子创建时间等信息,这时候标签下的结构就会变得复杂。而且每一个帖子都需要重写一次这 个复杂的结构,PostList 的结构将会变成类似这样的形式:</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

class PostList extends Component {
render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div&gt;
      &lt;h2&gt;帖子列表:&lt;/h2&gt;
      &lt;ul&gt;
          &lt;li&gt;
            &lt;div&gt;男子吃霸王餐还教育老板和气生财&lt;/div&gt;
            &lt;div&gt;<span style="color: rgba(0, 0, 0, 1)">
            创建人:</span>&lt;span&gt;小明&lt;/span&gt;
            &lt;/div&gt;
            &lt;div&gt;<span style="color: rgba(0, 0, 0, 1)">
            创建时间:</span>&lt;span&gt;2023-02-03 18:19:22&lt;/span&gt;
            &lt;/div&gt;
          &lt;/li&gt;
          &lt;li&gt;
            &lt;div&gt;莫斯科险遭无人机炸弹攻击&lt;/div&gt;
            &lt;div&gt;<span style="color: rgba(0, 0, 0, 1)">
            创建人:</span>&lt;span&gt;小军&lt;/span&gt;
            &lt;/div&gt;
            &lt;div&gt;<span style="color: rgba(0, 0, 0, 1)">
            创建时间:</span>&lt;span&gt;2023-01-22 21:22:35&lt;/span&gt;
            &lt;/div&gt;
          &lt;/li&gt;
          &lt;li&gt;
            &lt;div&gt;武汉官方把房地产归为困难行业&lt;/div&gt;
            &lt;div&gt;<span style="color: rgba(0, 0, 0, 1)">
            创建人:</span>&lt;span&gt;小华&lt;/span&gt;
            &lt;/div&gt;
            &lt;div&gt;<span style="color: rgba(0, 0, 0, 1)">
            创建时间:</span>&lt;span&gt;2022-12-22 15:14:56&lt;/span&gt;
            &lt;/div&gt;
          &lt;/li&gt;
      &lt;/ul&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> PostList;</pre>
</div>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230209112413190-1325980234.png" alt="" width="342" height="330" loading="lazy"></p>
<p>但是,帖子列表的数括依然存在于 PostList中,如何将数据传递给每个 PostItem 组件呢?这时候就需要用到组件的props属性。组件的 props用于把父组件中的数据或方法传递给子组件,供子组件使用。</p>
<p>props是一个简单结构的对象,它包含的属性正是由组件作为JSX标签使用时的属性组成。</p>
<p>例如下面是一个使用User组</p>
<div class="cnblogs_code">
<pre>&lt;User name='React' age='4' address=' America'&gt;</pre>
</div>
<p>此时User组件的 props结构如下:</p>
<div class="cnblogs_code">
<pre>props =<span style="color: rgba(0, 0, 0, 1)">{
name: </span>'React'<span style="color: rgba(0, 0, 0, 1)">,
age: </span>'4'<span style="color: rgba(0, 0, 0, 1)">,
address: </span>'America'<span style="color: rgba(0, 0, 0, 1)">
}</span></pre>
</div>
<p>利用props定义PostItem组件,PostItem.js如下所示:</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

class PostItem extends Component {
render() {
    const { title, author, date } </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props;
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;li&gt;
      &lt;div&gt;{title}&lt;/div&gt;
      &lt;div&gt;<span style="color: rgba(0, 0, 0, 1)">
          创建人:</span>&lt;span&gt;{author}&lt;/span&gt;
      &lt;/div&gt;
      &lt;div&gt;<span style="color: rgba(0, 0, 0, 1)">
          创建时间:</span>&lt;span&gt;{date}&lt;/span&gt;
      &lt;/div&gt;
      &lt;/li&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> PostItem;</pre>
</div>
<p>src/PostList.js</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;
import PostItem from </span>"./PostItem"<span style="color: rgba(0, 0, 0, 1)">;

const data </span>=<span style="color: rgba(0, 0, 0, 1)"> [
{
    title: </span>"男子吃霸王餐还教育老板和气生财"<span style="color: rgba(0, 0, 0, 1)">,
    author: </span>"小明"<span style="color: rgba(0, 0, 0, 1)">,
    date: </span>"2023-02-03 18:19:22"<span style="color: rgba(0, 0, 0, 1)">,
},
{
    title: </span>"莫斯科险遭无人机炸弹攻击"<span style="color: rgba(0, 0, 0, 1)">,
    author: </span>"小军"<span style="color: rgba(0, 0, 0, 1)">,
    date: </span>"2023-01-22 21:22:35"<span style="color: rgba(0, 0, 0, 1)">,
},
{
    title: </span>"武汉官方把房地产归为困难行业"<span style="color: rgba(0, 0, 0, 1)">,
    author: </span>"小华"<span style="color: rgba(0, 0, 0, 1)">,
    date: </span>"2022-12-22 15:14:56"<span style="color: rgba(0, 0, 0, 1)">,
},
];

class PostList extends Component {
render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div&gt;
      &lt;h2&gt;帖子列表:&lt;/h2&gt;
      &lt;ul&gt;<span style="color: rgba(0, 0, 0, 1)">
          {data.map((item) </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> (
            </span>&lt;<span style="color: rgba(0, 0, 0, 1)">PostItem
            title</span>=<span style="color: rgba(0, 0, 0, 1)">{item.title}
            author</span>=<span style="color: rgba(0, 0, 0, 1)">{item.author}
            date</span>=<span style="color: rgba(0, 0, 0, 1)">{item.date}
            </span>/&gt;
<span style="color: rgba(0, 0, 0, 1)">          ))}
      </span>&lt;/ul&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> PostList;</pre>
</div>
<p>index.js</p>
<div class="cnblogs_code">
<pre>import ReactDOM from "react-dom/client"<span style="color: rgba(0, 0, 0, 1)">;
import React from </span>"react"<span style="color: rgba(0, 0, 0, 1)">;
import PostList from </span>"./PostList"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">1、创建根节点</span>
let root = ReactDOM.createRoot(document.getElementById("root"<span style="color: rgba(0, 0, 0, 1)">));

let vNode </span>= &lt;PostList /&gt;;

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">3、将Vnode渲染到根结点上</span>
root.render(vNode);</pre>
</div>
<p>运行结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230221114625066-89770765.png" alt="" width="341" height="298" loading="lazy"></p>
<h2>2.5、组件的state</h2>
<p>组件的 state是组件内部的状态,state的变化最终将反映到组件UI的上。我们在组件的构造方法constructor中通过this.state定义组件的初始状态,并通过调用 this.setState 方法改变组件状态(也是改变组件状态的唯一方式),进而组件UI也会随之重新渲染。</p>
<p>示例:Counter.js</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class Counter extends Component {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">构造函数,用于初始化当前组件</span>
<span style="color: rgba(0, 0, 0, 1)">constructor(props) {
    super(props); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">初始化属性</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义当前组件的状态对象</span>
    <span style="color: rgba(0, 0, 255, 1)">this</span>.state =<span style="color: rgba(0, 0, 0, 1)"> {
      n: </span>100<span style="color: rgba(0, 0, 0, 1)">,
    };
    console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
}

clickHandler </span>= () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">this.state对象是只读的,不允许直接修改,使用setState修改</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">当调用setState时会更新UI,render()被调用</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">this.state.n++;</span>

    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">更新状态,设置新的状态对象</span>
    <span style="color: rgba(0, 0, 255, 1)">this</span>.setState({ n: <span style="color: rgba(0, 0, 255, 1)">this</span>.state.n + 1<span style="color: rgba(0, 0, 0, 1)"> });
};

render() {
    console.log(</span>"render"<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div&gt;
      &lt;h2&gt;计数器&lt;/h2&gt;
      &lt;<span style="color: rgba(0, 0, 0, 1)">button
          onClick</span>={() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
            </span><span style="color: rgba(0, 0, 255, 1)">this</span>.setState({ n: <span style="color: rgba(0, 0, 255, 1)">this</span>.state.n + 1<span style="color: rgba(0, 0, 0, 1)"> });
          }}
      </span>&gt;<span style="color: rgba(0, 0, 0, 1)">
          {</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.n}
      </span>&lt;/button&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}</span></pre>
</div>
<p>结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230302090753770-472270189.png" alt="" width="919" height="690" loading="lazy"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>下面来改造下BBS项目。我们为每一个帖子增加一个“点赞”按钮每点击一次,该帖子的点赞数增加1。点赞数是会发生变化的,它的变化也会影响到组件UI,因此我们将点赞数vote<br>作为Postltem的一个状态定义到它的state内。
</p>
<p>修改后的PostItem:</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

class PostItem extends Component {
constructor(props) {
    super(props); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">调用Component的构造方法,用于完成React组件的初始化工作</span>
    <span style="color: rgba(0, 0, 255, 1)">this</span>.state =<span style="color: rgba(0, 0, 0, 1)"> {
      vote: </span>0<span style="color: rgba(0, 0, 0, 1)">,
    };
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">处理点击逻辑</span>
<span style="color: rgba(0, 0, 0, 1)">handleClick() {
    let vote </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.vote;
    vote</span>++<span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({ vote: vote });
}

render() {
    const { title, author, date } </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props;
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;li&gt;
      &lt;div&gt;{title}&lt;/div&gt;
      &lt;div&gt;<span style="color: rgba(0, 0, 0, 1)">
          创建人:</span>&lt;span&gt;{author}&lt;/span&gt;
      &lt;/div&gt;
      &lt;div&gt;<span style="color: rgba(0, 0, 0, 1)">
          创建时间:</span>&lt;span&gt;{date}&lt;/span&gt;
      &lt;/div&gt;
      &lt;div&gt;
          &lt;<span style="color: rgba(0, 0, 0, 1)">button
            onClick</span>={() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
            </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.handleClick();
            }}
          </span>&gt;<span style="color: rgba(0, 0, 0, 1)">
            点赞 {</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.vote}
          </span>&lt;/button&gt;
      &lt;/div&gt;
      &lt;/li&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> PostItem;</pre>
</div>
<p>运行结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230221152203508-614910785.png" alt="" width="437" height="410" loading="lazy"></p>
<p>&nbsp;</p>
<p>React组件正是由props和state两种类型的数据驱动渲染出组件 UI。props是组件对外的接口,组件通过 props接收外部传入的数据(包括方法); state是组件对内的接口,组件内部状态的变化通过state反映。另外,props是只读的,你不能在组内部修改 props; state是可变的,组件状态的变化通过修改state来实现。</p>
<h3>2.5.1、Function.bind函数</h3>
<div>
<div id="stage-100022049" class="J-stage-100022049 doc-postil">
<div class="rno-markdown undefined rno-">
<p><strong><code>bind()</code></strong>方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。</p>
</div>
</div>
</div>
<div>
<div id="stage-100022050" class="J-stage-100022050 doc-postil">
<div>
<div class="rno-markdown undefined rno-">
<h4>语法</h4>
</div>
</div>
</div>
</div>
<div>
<div id="stage-100022051" class="J-stage-100022051 doc-postil markdown-code">
<div class="markdown-code-bd">
<div class="rno-markdown undefined rno-">
<div class="developer-code-block">
<pre class="prism-token token line-numbers language-javascript"><code class="language-javascript"><span class="token function">fun<span class="token punctuation">.<span class="token function">bind<span class="token punctuation">(thisArg<span class="token punctuation">[<span class="token punctuation">, arg1<span class="token punctuation">[<span class="token punctuation">, arg2<span class="token punctuation">[<span class="token punctuation">, <span class="token operator">...<span class="token punctuation">]<span class="token punctuation">]<span class="token punctuation">]<span class="token punctuation">)</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
</div>
</div>
</div>
</div>
<div>
<div id="stage-100022052" class="J-stage-100022052 doc-postil">
<div>
<div class="rno-markdown undefined rno-">
<h4>参数</h4>
</div>
</div>
</div>
</div>
<div>
<div id="stage-100022053" class="J-stage-100022053 doc-postil">
<div class="rno-markdown undefined rno-">
<p><code>thisArg</code>当绑定函数被调用时,该参数会作为原函数运行时的&nbsp;this&nbsp;指向。当使用<code>new</code>操作符调用绑定函数时,该参数无效。</p>
<p><code>arg1, arg2, ...</code>当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。</p>
</div>
</div>
</div>
<div>
<div id="stage-100022054" class="J-stage-100022054 doc-postil">
<div>
<div class="rno-markdown undefined rno-">
<h4>返回值</h4>
</div>
</div>
</div>
</div>
<div>
<div id="stage-100022055" class="J-stage-100022055 doc-postil">
<div class="rno-markdown undefined rno-">
<p>返回由指定的this值和初始化参数改造的原函数拷贝</p>
</div>
</div>
</div>
<div>
<div id="stage-100022056" class="J-stage-100022056 doc-postil">
<div>
<div class="rno-markdown undefined rno-">
<h4>描述</h4>
</div>
</div>
</div>
</div>
<div>
<div id="stage-100022057" class="J-stage-100022057 doc-postil">
<div>
<div class="rno-markdown undefined rno-">
<p>bind()&nbsp;函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的<code>call</code>属性)。当<strong>新函数</strong>被调用时&nbsp;this&nbsp;值绑定到&nbsp;bind()&nbsp;的第一个参数,该参数不能被重写。</p>
</div>
</div>
</div>
</div>
<div>
<div id="stage-100022058" class="J-stage-100022058 doc-postil">
<div>
<div class="rno-markdown undefined rno-">
<p>新函数具有下列内部属性:</p>
</div>
</div>
</div>
</div>
<div>
<div id="stage-100022059" class="J-stage-100022059 doc-postil">
<div>
<div class="rno-markdown undefined rno-">
<ul class="ul-level-0">
<li><strong></strong> - 封装的函数对象;</li>
</ul>
</div>
</div>
</div>
</div>
<div>
<div id="stage-100022060" class="J-stage-100022060 doc-postil">
<div>
<div class="rno-markdown undefined rno-">
<ul class="ul-level-0">
<li><strong></strong> - 在调用包装函数时作为<strong>此</strong>值传递的值。</li>
</ul>
</div>
</div>
</div>
</div>
<div>
<div id="stage-100022061" class="J-stage-100022061 doc-postil">
<div>
<div class="rno-markdown undefined rno-">
<ul class="ul-level-0">
<li><strong></strong> - 元素被用作对包装函数的调用的第一个参数的值列表。</li>
</ul>
</div>
</div>
</div>
</div>
<div>
<div id="stage-100022062" class="J-stage-100022062 doc-postil">
<div>
<div class="rno-markdown undefined rno-">
<ul class="ul-level-0">
<li><strong></strong> - 执行与此对象关联的代码。通过函数调用表达式调用。内部方法的参数是一个<strong>this</strong>值和一个包含通过调用表达式传递给函数的参数的列表。</li>
</ul>
</div>
</div>
</div>
</div>
<div>
<div id="stage-100022063" class="J-stage-100022063 doc-postil">
<div>
<div class="rno-markdown undefined rno-">
<p>当调用绑定函数时,它调用<strong></strong>上的内部方法<strong>,</strong>并使用以下参数<strong>Call(<em>boundThis</em>,</strong> <strong><em>args</em>)。</strong>其中,<strong><em>boundThis</em></strong>是<strong></strong>,<strong><em>args</em></strong>是<strong></strong>后跟函数调用传递的参数。</p>
</div>
</div>
</div>
</div>
<div>
<div id="stage-100022064" class="J-stage-100022064 doc-postil">
<div>
<div class="rno-markdown undefined rno-">
<p>绑定函数也可以使用<code>new</code>运算符来构造:这样做就好像目标函数已被构建一样。所提供的<code>this</code>值将被忽略,同时为仿真函数提供前置参数。</p>
</div>
</div>
</div>
</div>
<div>
<div id="stage-100022065" class="J-stage-100022065 doc-postil">
<div>
<div class="rno-markdown undefined rno-">
<h4>示例</h4>
</div>
</div>
</div>
</div>
<div>
<div id="stage-100022066" class="J-stage-100022066 doc-postil">
<div>
<div class="rno-markdown undefined rno-">
<h4>创建绑定函数</h4>
</div>
</div>
</div>
</div>
<div>
<div id="stage-100022067" class="J-stage-100022067 doc-postil">
<div class="rno-markdown undefined rno-">
<p><code>bind()</code>&nbsp;最简单的用法是创建一个函数,使这个函数不论怎么调用都有同样的&nbsp;<code>this</code>&nbsp;值。JavaScript新手经常犯的一个错误是将一个方法从对象中拿出来,然后再调用,希望方法中的&nbsp;<code>this</code>&nbsp;是原来的对象。(比如在回调中传入这个方法。)如果不做特殊处理的话,一般会丢失原来的对象。从原来的函数和原来的对象创建一个绑定函数,则能很漂亮地解决这个问题:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">this</span>.x = 9;    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> this refers to global "window" object here in the browser</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> module =<span style="color: rgba(0, 0, 0, 1)"> {
x: </span>81<span style="color: rgba(0, 0, 0, 1)">,
getX: </span><span style="color: rgba(0, 0, 255, 1)">function</span>() { <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.x; }
};

module.getX(); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 81</span>

<span style="color: rgba(0, 0, 255, 1)">var</span> retrieveX =<span style="color: rgba(0, 0, 0, 1)"> module.getX;
retrieveX();   
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> returns 9 - The function gets invoked at the global scope</span>

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Create a new function with 'this' bound to module</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> New programmers might confuse the</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> global var x with module's property x</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> boundGetX =<span style="color: rgba(0, 0, 0, 1)"> retrieveX.bind(module);
boundGetX(); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 81</span></pre>
</div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">&lt;!</span><span style="color: rgba(255, 0, 255, 1)">DOCTYPE html</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">html </span><span style="color: rgba(255, 0, 0, 1)">lang</span><span style="color: rgba(0, 0, 255, 1)">="en"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">head</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">meta </span><span style="color: rgba(255, 0, 0, 1)">charset</span><span style="color: rgba(0, 0, 255, 1)">="UTF-8"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">meta </span><span style="color: rgba(255, 0, 0, 1)">http-equiv</span><span style="color: rgba(0, 0, 255, 1)">="X-UA-Compatible"</span><span style="color: rgba(255, 0, 0, 1)"> content</span><span style="color: rgba(0, 0, 255, 1)">="IE=edge"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">meta </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="viewport"</span><span style="color: rgba(255, 0, 0, 1)"> content</span><span style="color: rgba(0, 0, 255, 1)">="width=device-width, initial-scale=1.0"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">title</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>Document<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">title</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">head</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">body</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">script</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">
      const obj1 </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> {
      x: </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">111</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">,
      getX() {
          console.log(</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.x);
      },
      };

      const obj2 </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> {
      x: </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">222</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">,
      };

      const obj3 </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> {
      x: </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">333</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">,
      };

      x </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">444</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;

      obj1.getX();

      const getX1 </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> obj1.getX;
      getX1();

      const getX2 </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> obj1.getX.bind(obj2);
      getX2();

      const getX3 </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> obj1.getX.bind(obj3);
      getX3();
    </span><span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">script</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">body</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">html</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>
</div>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230301151731380-1507448227.png" alt="" width="326" height="210" loading="lazy"></p>
</div>
</div>
</div>
<h3>2.5.2、组件中指定this</h3>
<p>下面的示例中this指向undefined</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class Counter extends Component {
constructor(props) {
    super(props);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.state = { count: 0<span style="color: rgba(0, 0, 0, 1)"> };
}

handleClick() {
    console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.setState({ count: <span style="color: rgba(0, 0, 255, 1)">this</span>.state.count + 1<span style="color: rgba(0, 0, 0, 1)"> });
}

render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div&gt;
      &lt;h2&gt;计算器&lt;/h2&gt;
      &lt;button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleClick}&gt;{<span style="color: rgba(0, 0, 255, 1)">this</span>.state.count}&lt;/button&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}</span></pre>
</div>
<p>运行时报错:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230301152008804-603962484.png" alt="" width="986" height="651" loading="lazy"></p>
<p>&nbsp;解决方法:</p>
<p>1、使用Function.bind指定this</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class Counter extends Component {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">构造函数,用于初始化当前组件</span>
<span style="color: rgba(0, 0, 0, 1)">constructor(props) {
    super(props); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">初始化属性</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义当前组件的状态对象</span>
    <span style="color: rgba(0, 0, 255, 1)">this</span>.state =<span style="color: rgba(0, 0, 0, 1)"> {
      n: </span>100<span style="color: rgba(0, 0, 0, 1)">,
    };
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">指定clickHandler函数中的this为当前组件,并生成新的函数</span>
    <span style="color: rgba(0, 0, 255, 1)">this</span>.clickHandler = <span style="color: rgba(0, 0, 255, 1)">this</span>.clickHandler.bind(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">debugger</span><span style="color: rgba(0, 0, 0, 1)">;
}

clickHandler() {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.setState({ n: <span style="color: rgba(0, 0, 255, 1)">this</span>.state.n + 1<span style="color: rgba(0, 0, 0, 1)"> });
}

render() {
    console.log(</span>"render"<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div&gt;
      &lt;h2&gt;计数器&lt;/h2&gt;
      &lt;button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.clickHandler}&gt;{<span style="color: rgba(0, 0, 255, 1)">this</span>.state.n}&lt;/button&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}</span></pre>
</div>
<p>&nbsp;</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230301152142501-1467674281.png" alt="" width="982" height="521" loading="lazy"></p>
<p>&nbsp;</p>
<p>&nbsp;可以看出this此时指向了Component这个对象</p>
<p>2、方法2,使用箭头函数,箭头函数中的this始终指向组件对象</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class Counter extends Component {
constructor(props) {
    super(props);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.state = { count: 0<span style="color: rgba(0, 0, 0, 1)"> };
}

handleClick </span>= () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.setState({ count: <span style="color: rgba(0, 0, 255, 1)">this</span>.state.count + 1<span style="color: rgba(0, 0, 0, 1)"> });
};

render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div&gt;
      &lt;h2&gt;计算器&lt;/h2&gt;
      &lt;button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleClick}&gt;{<span style="color: rgba(0, 0, 255, 1)">this</span>.state.count}&lt;/button&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}</span></pre>
</div>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230301152425195-1705244715.png" alt="" width="1107" height="623" loading="lazy"></p>
<p>简化</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class Counter2 extends Component {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">使用state属性初始化状态对象</span>
state = { n: 999<span style="color: rgba(0, 0, 0, 1)"> };

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">箭头函数中的this始终指向组件本身</span>
clickHandler = () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.setState({ n: <span style="color: rgba(0, 0, 255, 1)">this</span>.state.n + 1<span style="color: rgba(0, 0, 0, 1)"> });
};

render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div&gt;
      &lt;h2&gt;计数器&lt;/h2&gt;
      &lt;button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.clickHandler}&gt;{<span style="color: rgba(0, 0, 255, 1)">this</span>.state.n}&lt;/button&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}</span></pre>
</div>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230302095329559-1953044340.png" alt="" loading="lazy"></p>
<h3>2.5.3、componentDidMount</h3>
<h4 id="mounting">挂载</h4>
<p>当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:</p>
<ul>
<li><code class="gatsby-code-text">constructor()</code></li>
<li><code class="gatsby-code-text">static getDerivedStateFromProps()</code></li>
<li><code class="gatsby-code-text">render()</code></li>
<li><code class="gatsby-code-text">componentDidMount()</code></li>
</ul>
<p><code class="gatsby-code-text">componentDidMount()</code>&nbsp;会在组件挂载后(插入 DOM 树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。<span style="color: rgba(255, 0, 0, 1)">如需通过网络请求获取数据,此处是实例化请求的好地方</span>。</p>
<p>这个方法是比较适合添加订阅的地方。如果添加了订阅,请不要忘记在&nbsp;<code class="gatsby-code-text">componentWillUnmount()</code>&nbsp;里取消订阅</p>
<p>你可以在&nbsp;<code class="gatsby-code-text">componentDidMount()</code>&nbsp;里直接调用&nbsp;<code class="gatsby-code-text">setState()</code>。它将触发额外渲染,但此渲染会发生在浏览器更新屏幕之前。如此保证了即使在&nbsp;<code class="gatsby-code-text">render()</code>&nbsp;两次调用的情况下,用户也不会看到中间状态。请谨慎使用该模式,因为它会导致性能问题。通常,你应该在&nbsp;<code class="gatsby-code-text">constructor()</code>&nbsp;中初始化 state。如果你的渲染依赖于 DOM 节点的大小或位置,比如实现 modals 和 tooltips 等情况下,你可以使用此方式处理</p>
<h3>2.5.4、componentWillUnmount</h3>
<p><code class="gatsby-code-text">componentWillUnmount()</code>&nbsp;会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在&nbsp;<code class="gatsby-code-text">componentDidMount()</code>&nbsp;中创建的订阅等。</p>
<p><code class="gatsby-code-text">componentWillUnmount()</code>&nbsp;中不应调用&nbsp;<code class="gatsby-code-text">setState()</code>,因为该组件将永远不会重新渲染。组件实例卸载后,将永远不会再挂载它。</p>
<p>Timer.js</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class Timer extends Component {
constructor(props) {
    super(props);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.timer = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.state =<span style="color: rgba(0, 0, 0, 1)"> {
      time: </span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date().toLocaleString(),
    };
}

componentDidMount() {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.timer = setInterval(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.setState({ time: <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date().toLocaleString() });
      console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.time);
    }, </span>1000<span style="color: rgba(0, 0, 0, 1)">);
}

render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;div&gt;{<span style="color: rgba(0, 0, 255, 1)">this</span>.state.time}&lt;/div&gt;;
<span style="color: rgba(0, 0, 0, 1)">}
}</span></pre>
</div>
<p>Counter.js</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

import Timer from </span>"./Timer"<span style="color: rgba(0, 0, 0, 1)">;

export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class Counter extends Component {
constructor(props) {
    super(props);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.state = { count: 0, isShow: <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)"> };
}

handleClick </span>= () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.setState({ count: <span style="color: rgba(0, 0, 255, 1)">this</span>.state.count + 1<span style="color: rgba(0, 0, 0, 1)"> });
};

render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div&gt;
      &lt;<span style="color: rgba(0, 0, 0, 1)">button
          onClick</span>={() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
            </span><span style="color: rgba(0, 0, 255, 1)">this</span>.setState({ isShow: !<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.isShow });
          }}
      </span>&gt;<span style="color: rgba(0, 0, 0, 1)">
          显示</span>/隐藏
      &lt;/button&gt;
      &lt;div&gt;{<span style="color: rgba(0, 0, 255, 1)">this</span>.state.isShow &amp;&amp; &lt;Timer /&gt;}&lt;/div&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}</span></pre>
</div>
<p>结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230301160110495-1837680358.png" alt="" width="463" height="458" loading="lazy"></p>
<p>可以显示时钟,但隐藏时控制台依然输出,修改Timer.js文件,当组件卸载时清除时钟</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class Timer extends Component {
constructor(props) {
    super(props);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.timer = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.state =<span style="color: rgba(0, 0, 0, 1)"> {
      time: </span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date().toLocaleString(),
    };
}

componentDidMount() {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.timer = setInterval(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.setState({ time: <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date().toLocaleString() });
      console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.time);
    }, </span>1000<span style="color: rgba(0, 0, 0, 1)">);
}

componentWillUnmount() {
    clearInterval(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.timer);
}

render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;div&gt;{<span style="color: rgba(0, 0, 255, 1)">this</span>.state.time}&lt;/div&gt;;
<span style="color: rgba(0, 0, 0, 1)">}
}</span></pre>
</div>
<p>结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230301160245554-2017307800.png" alt="" width="645" height="361" loading="lazy"></p>
<h2>2.6、有状态组件和无状态组件</h2>
<p>是不是每个组件内部都需要定义state呢?当然不是。state用来反映组件内部状态变化,如果一个组件的内部状态是不变的,当然就用不到state,这样的组件称之为无状态组件,例如PostList。<br>反之,一个组件的内部状态会发生变化,就需要使用state
        来保存变化,这种组件称为有状态组件,例如PostItem。<br>定义无状态组件除了使用 ES 6 class的方式外,还可以使用函数定义,一个函数组件接收props作为参数,返回代表这个组件的UI React
        元素结构。<br>例如,下面是一个简单的函数组件:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Welcome (props) {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;h1&gt;Hello, {props.name }&lt;/hl&gt;;
}</pre>
</div>
<p>可以看出,函数组件的写法比类组件的写法要简洁很多,在<span style="color: rgba(255, 0, 0, 1)">使用无状态组件时,应该尽量将其定义成函数组件</span>。</p>
<p>在开发React应用时,一定要先认真思考哪些组件应该设计成有状态组件,哪些组件应该设计成无状态组件。并且,应该尽可能多地使用无状态组件,无状态组件不用关心状态的变化,只聚焦于UI的展示,因而更容易被复用。React应用组件设计的一般思路是,通过定义少数的有状态组件管理整个应用的状态变化,并且将状态通过props传递给其余的无状态组件,由无状态组件完成页面绝大部分UI的渲染工作。总之,有状态组件主要关注处理状态变化的业务逻辑,无状态组件主要关注组件UI的渲染。</p>
<p>下面让我们回过头来看一下BBS项目的组件设计。当前的组件设计并不合适,主要体现在:</p>
<p>(1)帖子列表通过一个常量data保存在组件之外,但帖子列表的数据增加或原有帖子的删除都会导致帖子列表数据的变化。</p>
<p>(2)每一个 PostItem都维持个 vote状态,但除了vote以外,帖子其他的信息(如标题、创建人等)都保存在PostList中,这显然也是不合理的。</p>
<p>我们对这两个组件进行重新设计,将PostList 设计为有状态组件,负责帖子列表数据的获取以及点赞行为的处理,将PostItem设计为无状态组件,只负责每一个帖子的<br>展示。</p>
<p>修改后的PostList.js:</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;
import PostItem from </span>"./PostItem"<span style="color: rgba(0, 0, 0, 1)">;

class PostList extends Component {
constructor(props) {
    super(props);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.state =<span style="color: rgba(0, 0, 0, 1)"> {
      posts: [],
    };
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.timer = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">改变handleVote中的this指向</span>
    <span style="color: rgba(0, 0, 255, 1)">this</span>.handleVote = <span style="color: rgba(0, 0, 255, 1)">this</span>.handleVote.bind(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
}

data </span>=<span style="color: rgba(0, 0, 0, 1)"> [
    {
      id: </span>1<span style="color: rgba(0, 0, 0, 1)">,
      title: </span>"男子吃霸王餐还教育老板和气生财"<span style="color: rgba(0, 0, 0, 1)">,
      author: </span>"小明"<span style="color: rgba(0, 0, 0, 1)">,
      date: </span>"2023-02-03 18:19:22"<span style="color: rgba(0, 0, 0, 1)">,
      vote: </span>0<span style="color: rgba(0, 0, 0, 1)">,
    },
    {
      id: </span>2<span style="color: rgba(0, 0, 0, 1)">,
      title: </span>"莫斯科险遭无人机炸弹攻击"<span style="color: rgba(0, 0, 0, 1)">,
      author: </span>"小军"<span style="color: rgba(0, 0, 0, 1)">,
      date: </span>"2023-01-22 21:22:35"<span style="color: rgba(0, 0, 0, 1)">,
      vote: </span>0<span style="color: rgba(0, 0, 0, 1)">,
    },
    {
      id: </span>3<span style="color: rgba(0, 0, 0, 1)">,
      title: </span>"武汉官方把房地产归为困难行业"<span style="color: rgba(0, 0, 0, 1)">,
      author: </span>"小华"<span style="color: rgba(0, 0, 0, 1)">,
      date: </span>"2022-12-22 15:14:56"<span style="color: rgba(0, 0, 0, 1)">,
      vote: </span>0<span style="color: rgba(0, 0, 0, 1)">,
    },
];

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">组件挂载到DOM后</span>
<span style="color: rgba(0, 0, 0, 1)">componentDidMount() {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.timer = setTimeout(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({
      posts: </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.data,
      });
    }, </span>1000<span style="color: rgba(0, 0, 0, 1)">);
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">组件从DOM中卸载</span>
<span style="color: rgba(0, 0, 0, 1)">componentWillUnmount() {
    clearTimeout(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.timer);
}

handleVote(id) {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">遍历所有的贴子,如果编号相同,则增加点赞后返回</span>
    const posts = <span style="color: rgba(0, 0, 255, 1)">this</span>.state.posts.map((item) =&gt;<span style="color: rgba(0, 0, 0, 1)">
      item.id </span>=== id ? { ...item, vote: ++<span style="color: rgba(0, 0, 0, 1)">item.vote } : item
    );
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">重新设置状态</span>
    <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({
      posts,
    });
}

render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div&gt;
      &lt;h2&gt;帖子列表:&lt;/h2&gt;
      &lt;ul&gt;<span style="color: rgba(0, 0, 0, 1)">
          {</span><span style="color: rgba(0, 0, 255, 1)">this</span>.state.posts.map((item) =&gt;<span style="color: rgba(0, 0, 0, 1)"> (
            </span>&lt;PostItem post={item} onVote={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleVote} /&gt;
<span style="color: rgba(0, 0, 0, 1)">          ))}
      </span>&lt;/ul&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> PostList;</pre>
</div>
<p>修改后的PostItem.js:</p>
<div class="cnblogs_code">
<pre>import React from "react"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> PostItem(props) {
const { post, onVote } </span>=<span style="color: rgba(0, 0, 0, 1)"> props;
const handleVote </span>= () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    onVote(post.id);
};

</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
    </span>&lt;li&gt;
      &lt;div&gt;{post.title}&lt;/div&gt;
      &lt;div&gt;<span style="color: rgba(0, 0, 0, 1)">
      创建人:</span>&lt;span&gt;{post.author}&lt;/span&gt;
      &lt;/div&gt;
      &lt;div&gt;<span style="color: rgba(0, 0, 0, 1)">
      创建时间:</span>&lt;span&gt;{post.date}&lt;/span&gt;
      &lt;/div&gt;
      &lt;div&gt;
      &lt;button onClick={handleVote}&gt;点赞 {post.vote}&lt;/button&gt;
      &lt;/div&gt;
    &lt;/li&gt;
<span style="color: rgba(0, 0, 0, 1)">);
}

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> PostItem;</pre>
</div>
<p>运行效果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230222095951101-1498974852.png" alt="" loading="lazy"></p>
<p>&nbsp;</p>
<h2>2.7、属性校验和默认属性</h2>
<h3>2.7.1.组件特殊属性——propTypes</h3>
<p>对Component设置propTypes属性,可以为Component的props属性进行类型检查。</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;
import PropTypes from </span>"prop-types"<span style="color: rgba(0, 0, 0, 1)">;

class Hello extends Component {
render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;h2&gt;Hello {<span style="color: rgba(0, 0, 255, 1)">this</span>.props.name}!&lt;/h2&gt;;
<span style="color: rgba(0, 0, 0, 1)">}
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">设置组件的props对象的类型信息</span>
Hello.propTypes =<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">要求name为string类型</span>
<span style="color: rgba(0, 0, 0, 1)">name: PropTypes.string,
};

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Hello;</pre>
</div>
<p>调用:</p>
<div class="cnblogs_code">
<pre>import ReactDOM from "react-dom/client"<span style="color: rgba(0, 0, 0, 1)">;
import React from </span>"react"<span style="color: rgba(0, 0, 0, 1)">;
import Hello from </span>"./Hello"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">1、创建根节点</span>
let root = ReactDOM.createRoot(document.getElementById("root"<span style="color: rgba(0, 0, 0, 1)">));

let vNode </span>= &lt;Hello name={99} /&gt;;

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">3、将Vnode渲染到根结点上</span>
root.render(vNode);</pre>
</div>
<p>PropTypes提供了许多验证工具,用来帮助你确定props数据的有效性。在上面这个例子中,我们使用了PropTypes.stirng。意思是:name的值类型应该是string。 当Component的props接收到一个无效的值时,浏览器控制台就会输出一个警告。因此,&lt;Hello name={99}/&gt; 控制台会出现如下警告:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230222104711557-748652769.png" alt="" width="1078" height="681" loading="lazy"></p>
<p>处于性能原因,类型检查仅在开发模式下进行。</p>
<h3>2.7.2.PropTypes的更多验证器</h3>
<div class="cnblogs_code">
<pre>import React from 'react'<span style="color: rgba(0, 0, 0, 1)">;
import PropTypes from </span>'prop-types'<span style="color: rgba(0, 0, 0, 1)">;

class MyComponent extends React.Component {
render() {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 利用属性做更多得事</span>
<span style="color: rgba(0, 0, 0, 1)">   }
}

MyComponent.propTypes </span>=<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">你可以定义一个属性是特定的JS类型(Array,Boolean,Function,Number,Object,String,Symbol)。默认情况下,这些都是可选的。</span>
<span style="color: rgba(0, 0, 0, 1)">
optionalArray: PropTypes.array,
optionalBool: PropTypes.bool,
optionalFunc: PropTypes.func,
optionalNumber: PropTypes.number,
optionalObject: PropTypes.object,
optionalString: PropTypes.string,
optionalSymbol: PropTypes.symbol,

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">指定类型为:任何可以被渲染的元素,包括数字,字符串,react 元素,数组,fragment。</span>
<span style="color: rgba(0, 0, 0, 1)">optionalNode: PropTypes.node,

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 指定类型为:一个react 元素</span>
<span style="color: rgba(0, 0, 0, 1)">optionalElement: PropTypes.element,

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">你可以类型为某个类的实例,这里使用JS的instanceOf操作符实现</span>
<span style="color: rgba(0, 0, 0, 1)">optionalMessage: PropTypes.instanceOf(Message),


</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">指定枚举类型:你可以把属性限制在某些特定值之内</span>
optionalEnum: PropTypes.oneOf(['News', 'Photos'<span style="color: rgba(0, 0, 0, 1)">]),

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 指定多个类型:你也可以把属性类型限制在某些指定的类型范围内</span>
<span style="color: rgba(0, 0, 0, 1)">optionalUnion: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
]),

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 指定某个类型的数组</span>
<span style="color: rgba(0, 0, 0, 1)">optionalArrayOf: PropTypes.arrayOf(PropTypes.number),

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 指定类型为对象,且对象属性值是特定的类型</span>
<span style="color: rgba(0, 0, 0, 1)">optionalObjectOf: PropTypes.objectOf(PropTypes.number),


</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">指定类型为对象,且可以规定哪些属性必须有,哪些属性可以没有</span>
<span style="color: rgba(0, 0, 0, 1)">optionalObjectWithShape: PropTypes.shape({
optionalProperty: PropTypes.string,
requiredProperty: PropTypes.number.isRequired
}),

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 指定类型为对象,且可以指定对象的哪些属性必须有,哪些属性可以没有。如果出现没有定义的属性,会出现警告。</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)">下面的代码optionalObjectWithStrictShape的属性值为对象,但是对象的属性最多有两个,optionalProperty 和 requiredProperty。</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)">出现第三个属性,控制台出现警告。</span>
<span style="color: rgba(0, 0, 0, 1)">optionalObjectWithStrictShape: PropTypes.exact({
optionalProperty: PropTypes.string,
requiredProperty: PropTypes.number.isRequired
}),

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">加上isReqired限制,可以指定某个属性必须提供,如果没有出现警告。</span>
<span style="color: rgba(0, 0, 0, 1)">requiredFunc: PropTypes.func.isRequired,
requiredAny: PropTypes.any.isRequired,

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 你也可以指定一个自定义的验证器。如果验证不通过,它应该返回Error对象,而不是`console.warn `或抛出错误。`oneOfType`中不起作用。</span>
customProp: <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(props, propName, componentName) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (!/matchme/<span style="color: rgba(0, 0, 0, 1)">.test(props)) {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Error(
      </span>'Invalid prop `' + propName + '` supplied to' +
      ' `' + componentName + '`. Validation failed.'<span style="color: rgba(0, 0, 0, 1)">
    );
}
},

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">你也可以提供一个自定义的验证器 arrayOf和objectOf。如果验证失败,它应该返回一个Error对象。</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)">验证器用来验证数组或对象的每个值。验证器的前两个参数是数组或对象本身,还有对应的key。</span>
customArrayProp: PropTypes.arrayOf(<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(propValue, key,   componentName, location, propFullName) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (!/matchme/<span style="color: rgba(0, 0, 0, 1)">.test(propValue)) {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Error(
      </span>'Invalid prop `' + propFullName + '` supplied to' +
      ' `' + componentName + '`. Validation failed.'<span style="color: rgba(0, 0, 0, 1)">
    );
}
})</span></pre>
</div>
<p>示例:</p>
<p>Hello.js</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;
import PropTypes from </span>"prop-types"<span style="color: rgba(0, 0, 0, 1)">;

class Hello extends Component {
render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;h2&gt;Hello {<span style="color: rgba(0, 0, 255, 1)">this</span>.props.name}!&lt;/h2&gt;;
<span style="color: rgba(0, 0, 0, 1)">}
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">设置组件的props对象的类型信息</span>
Hello.propTypes =<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">要求name为string类型</span>
<span style="color: rgba(0, 0, 0, 1)">name: PropTypes.string,
sex: PropTypes.oneOf([</span>"男", "女"<span style="color: rgba(0, 0, 0, 1)">]),
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">props 属性对象 ,propName 当前属性名,componentName组件名</span>
phone: <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (props, propName, componentName) {
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!/\d{13}/<span style="color: rgba(0, 0, 0, 1)">.test(props)) {
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Error("手机号码必须是13位的数字"<span style="color: rgba(0, 0, 0, 1)">);
    }
},
};

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Hello;</pre>
</div>
<p>index.js</p>
<div class="cnblogs_code">
<pre>import ReactDOM from "react-dom/client"<span style="color: rgba(0, 0, 0, 1)">;
import React from </span>"react"<span style="color: rgba(0, 0, 0, 1)">;
import PostList from </span>"./PostList"<span style="color: rgba(0, 0, 0, 1)">;
import Hello from </span>"./Hello"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">1、创建根节点</span>
let root = ReactDOM.createRoot(document.getElementById("root"<span style="color: rgba(0, 0, 0, 1)">));

let vNode </span>= &lt;Hello name={99} sex="未知" phone="abc" /&gt;;

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">3、将Vnode渲染到根结点上</span>
root.render(vNode);</pre>
</div>
<p>结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230222110402063-1679931379.png" alt="" loading="lazy"></p>
<h3>2.7.3.&nbsp;必填</h3>
<div class="cnblogs_code">
<pre>      static propTypes=<span style="color: rgba(0, 0, 0, 1)">{

                属性:PropTypes.类型.(是否必填,不能为空)

      }</span></pre>
</div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">city必须是string类型且必须填写</span>
city: PropTypes.string.isRequired,</pre>
</div>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230222114314643-1328467334.png" alt="" loading="lazy"></p>
<h3>2.7.4. 子元素</h3>
<p><strong>限制单个子元素</strong></p>
<p>使用 PropTypes.element 你可以指定只有一个子元素可以被传递给组件。</p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">将children限制为单个子元素。</span>
Greeting.propTypes =<span style="color: rgba(0, 0, 0, 1)"> {
name: PropTypes.string,
children: PropTypes.element.isRequired
};</span></pre>
</div>
<p>如果如下方式引用Greeting组件:</p>
<p>//传了两个子元素给组件Greeting那么,控制台会出现警告</p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">传了两个子元素给组件Greeting那么,控制台会出现警告</span>
&lt;Greeting name={'world'}&gt;
      &lt;span&gt;子元素1&lt;/span&gt;
      &lt;span&gt;子元素2&lt;/span&gt;
&lt;/Greeting&gt;</pre>
</div>
<p>警告如图:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230222100946649-1983955651.png" alt="" loading="lazy"></p>
<p>限制其它子元素</p>
<div class="cnblogs_code">
<pre>children: PropTypes.number,</pre>
</div>
<p>获取子元素:</p>
<p>使用children</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">class Hello extends Component {
render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;h2&gt;<span style="color: rgba(0, 0, 0, 1)">
      Hello {</span><span style="color: rgba(0, 0, 255, 1)">this</span>.props.name}! {<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props.children}
      </span>&lt;/h2&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}</span></pre>
</div>
<p>使用属性</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> index.tsx</span>
<span style="color: rgba(0, 0, 0, 1)">
import React from </span>'react'<span style="color: rgba(0, 0, 0, 1)">
import NavBar from </span>'./NavBar'<span style="color: rgba(0, 0, 0, 1)">

const ReactSlot </span>= () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
    </span>&lt;div&gt;
      &lt;<span style="color: rgba(0, 0, 0, 1)">NavBar
      leftSlot</span>={&lt;div&gt;left---这里内容可以随意填充&lt;/div&gt;}
      centerSlot={&lt;div&gt;center---这里内容可以随意填充&lt;/div&gt;}
      rightSlot={&lt;div&gt;right---这里内容可以随意填充&lt;/div&gt;}
      &gt;&lt;/NavBar&gt;
    &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">)
}

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> ReactSlot</pre>
</div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> NavBar.tsx</span>
<span style="color: rgba(0, 0, 0, 1)">
import React, { ReactNode } from </span>'react'<span style="color: rgba(0, 0, 0, 1)">
import </span>'./navbar.css'<span style="color: rgba(0, 0, 0, 1)">

type Props </span>=<span style="color: rgba(0, 0, 0, 1)"> {
leftSlot: ReactNode
centerSlot: ReactNode
rightSlot: ReactNode
}
const NavBar </span>= (props:Props) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
    </span>&lt;div className='navbar-container'&gt;
      &lt;div className='navbar-left'&gt;<span style="color: rgba(0, 0, 0, 1)">
      {props.leftSlot}
      </span>&lt;/div&gt;
      &lt;div className='navbar-center'&gt;<span style="color: rgba(0, 0, 0, 1)">
      {props.centerSlot}
      </span>&lt;/div&gt;
      &lt;div className='navbar-right'&gt;<span style="color: rgba(0, 0, 0, 1)">
      {props.rightSlot}
      </span>&lt;/div&gt;
    &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">)
}

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> NavBar</pre>
</div>
<p>Layout.js</p>
<div class="cnblogs_code">
<pre>import PropTypes from "prop-types"<span style="color: rgba(0, 0, 0, 1)">;
import React, { Component } from </span>"react"<span style="color: rgba(0, 0, 0, 1)">;
import </span>"./layout.css"<span style="color: rgba(0, 0, 0, 1)">;

export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class Layout extends Component {
render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div className="container"&gt;
      &lt;div className="item"&gt;{<span style="color: rgba(0, 0, 255, 1)">this</span>.props.left}&lt;/div&gt;
      &lt;div className="item"&gt;{<span style="color: rgba(0, 0, 255, 1)">this</span>.props.center}&lt;/div&gt;
      &lt;div className="item"&gt;{<span style="color: rgba(0, 0, 255, 1)">this</span>.props.right}&lt;/div&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}</span></pre>
</div>
<p>layout.css</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(128, 0, 0, 1)">.container</span>{<span style="color: rgba(255, 0, 0, 1)">
    display</span>:<span style="color: rgba(0, 0, 255, 1)"> flex</span>;
}<span style="color: rgba(128, 0, 0, 1)">

.item</span>{<span style="color: rgba(255, 0, 0, 1)">
    flex</span>:<span style="color: rgba(0, 0, 255, 1)"> auto</span>;<span style="color: rgba(255, 0, 0, 1)">
    min-height</span>:<span style="color: rgba(0, 0, 255, 1)"> 200px</span>;<span style="color: rgba(255, 0, 0, 1)">
    border</span>:<span style="color: rgba(0, 0, 255, 1)"> 1px solid #999</span>;<span style="color: rgba(255, 0, 0, 1)">
    margin</span>:<span style="color: rgba(0, 0, 255, 1)"> 20px</span>;
}</pre>
</div>
<p>index.js</p>
<div class="cnblogs_code">
<pre>import React from "react"<span style="color: rgba(0, 0, 0, 1)">;
import ReactDOM from </span>"react-dom/client"<span style="color: rgba(0, 0, 0, 1)">;
import Layout from </span>"./Layout"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">虚拟DOM,JSX</span>
const vnode =<span style="color: rgba(0, 0, 0, 1)"> (
</span>&lt;div&gt;
    &lt;<span style="color: rgba(0, 0, 0, 1)">Layout
      left</span>={&lt;h1&gt;侧边栏1&lt;/h1&gt;}
      center={&lt;h1&gt;中间区域2&lt;/h1&gt;}
      right={&lt;h1&gt;右侧内容3&lt;/h1&gt;}
    &gt;&lt;/Layout&gt;

    &lt;<span style="color: rgba(0, 0, 0, 1)">Layout
      left</span>={&lt;h1&gt;侧边栏a&lt;/h1&gt;}
      center={&lt;h1&gt;中间区域b&lt;/h1&gt;}
      right={&lt;h1&gt;右侧内容c&lt;/h1&gt;}
    &gt;&lt;/Layout&gt;

    &lt;<span style="color: rgba(0, 0, 0, 1)">Layout
      left</span>={&lt;h1&gt;侧边栏d&lt;/h1&gt;}
      center={&lt;h1&gt;中间区域e&lt;/h1&gt;}
      right={&lt;h1&gt;右侧内容f&lt;/h1&gt;}
    &gt;&lt;/Layout&gt;
&lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">2、创建根节点</span>
const root = ReactDOM.createRoot(document.querySelector("#root"<span style="color: rgba(0, 0, 0, 1)">));

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">3、将虚拟DOM节点挂载到根节点上</span>
root.render(vnode);</pre>
</div>
<p>效果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230307091223662-1877881294.png" alt="" width="1098" height="422" loading="lazy"></p>
<h3>2.7.5.指定默认属性值</h3>
<p>你可以给组件分配一个特殊的defaultProps属性。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">给Greeting属性中的name值指定默认值。当组件引用的时候,没有传入name属性时,会使用默认值。</span>
Greeting.defaultProps =<span style="color: rgba(0, 0, 0, 1)"> {
name: </span>'Stranger'<span style="color: rgba(0, 0, 0, 1)">
};</span></pre>
</div>
<p>// ES6可以这样写</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">class Greeting extends React.Component {
static defaultProps </span>=<span style="color: rgba(0, 0, 0, 1)"> {
    name: </span>'stranger'<span style="color: rgba(0, 0, 0, 1)">
}
render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div&gt;Hello, {<span style="color: rgba(0, 0, 255, 1)">this</span>.props.name}&lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    )
}
}</span></pre>
</div>
<p>Hello.js</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;
import PropTypes from </span>"prop-types"<span style="color: rgba(0, 0, 0, 1)">;

class Hello extends Component {
render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;h2&gt;<span style="color: rgba(0, 0, 0, 1)">
      Hello {</span><span style="color: rgba(0, 0, 255, 1)">this</span>.props.name}! {<span style="color: rgba(0, 0, 255, 1)">this</span>.props.children} {<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props.city}
      </span>&lt;/h2&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">设置组件的props对象的类型信息</span>
Hello.propTypes =<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">要求name为string类型</span>
<span style="color: rgba(0, 0, 0, 1)">name: PropTypes.string,
sex: PropTypes.oneOf([</span>"男", "女"<span style="color: rgba(0, 0, 0, 1)">]),
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">props 属性对象 ,propName 当前属性名,componentName组件名</span>
phone: <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (props, propName, componentName) {
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!/\d{13}/<span style="color: rgba(0, 0, 0, 1)">.test(props)) {
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Error("手机号码必须是13位的数字"<span style="color: rgba(0, 0, 0, 1)">);
    }
},
children: PropTypes.number,
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">city必须是string类型且必须填写</span>
<span style="color: rgba(0, 0, 0, 1)">city: PropTypes.string.isRequired,
};

Hello.defaultProps </span>=<span style="color: rgba(0, 0, 0, 1)"> {
city: </span>"珠海"<span style="color: rgba(0, 0, 0, 1)">,
};

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Hello;</pre>
</div>
<p>如果有默认值就变成了非必填了。</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230222114454224-1388824380.png" alt="" loading="lazy"></p>
<h2>&nbsp;2.8、React-组件样式的两种方式</h2>
<p>与传统使用CSS给HTML添加样式的方式相比,React在给元素添加样式的时候方式有所不同。React的核心哲学之一就是让可视化的组件自包含,并且可复用。这就是为什么HTML元素和Javascript放在一起组成了(组件)。本节内容我们将介绍React定义样式的方式。</p>
<h3><strong>2.8.1、第一种方式:选择器样式</strong></h3>
<p>首先创建Person.css,定义我们所需要的样式,具体样式代码定义为如下形式:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(128, 0, 0, 1)">.Person</span>{<span style="color: rgba(255, 0, 0, 1)">width</span>:<span style="color: rgba(0, 0, 255, 1)">60%</span>;<span style="color: rgba(255, 0, 0, 1)">margin</span>:<span style="color: rgba(0, 0, 255, 1)">16pxauto</span>;<span style="color: rgba(255, 0, 0, 1)">border</span>:<span style="color: rgba(0, 0, 255, 1)">1pxsolid#eee</span>;<span style="color: rgba(255, 0, 0, 1)">box-shadow</span>:<span style="color: rgba(0, 0, 255, 1)">2px3px#ccc</span>;<span style="color: rgba(255, 0, 0, 1)">padding</span>:<span style="color: rgba(0, 0, 255, 1)">16px</span>;<span style="color: rgba(255, 0, 0, 1)">text-align</span>:<span style="color: rgba(0, 0, 255, 1)">center</span>;}<span style="color: rgba(128, 0, 0, 1)">

input</span>{<span style="color: rgba(255, 0, 0, 1)">width</span>:<span style="color: rgba(0, 0, 255, 1)">200px</span>;<span style="color: rgba(255, 0, 0, 1)">border</span>:<span style="color: rgba(0, 0, 255, 1)">1pxsolid#eee</span>;<span style="color: rgba(255, 0, 0, 1)">outline</span>:<span style="color: rgba(0, 0, 255, 1)">none</span>;<span style="color: rgba(255, 0, 0, 1)">border-radius</span>:<span style="color: rgba(0, 0, 255, 1)">10px</span>;<span style="color: rgba(255, 0, 0, 1)">padding</span>:<span style="color: rgba(0, 0, 255, 1)">16px</span>;}</pre>
</div>
<p>如果要使用样式,需要在Person.js组件中引入:</p>
<p>import'./Person.css'</p>
<p>这时可以查看页面变化了。</p>
<h3><strong>2.8.2、第二种方式:内联样式</strong></h3>
<p>React推崇的是内联的方式定义样式。这样做的目的就在于让你的组件更加的容易复用。下面给按钮添加一个内联样式:</p>
<p>来到App.js文件,将button按钮定义为如下形式:</p>
<div class="cnblogs_code">
<pre>style={{backgroundColor:'white',border:'1px solid blue',padding:'8px',cursor:'pointer'}}&gt;</pre>
</div>
<p>更改状态值</p>
<p>需要注意的是,JSX中使用样式对象定义内联样式,复合样式使用驼峰命名法,对象属性直接使用逗号隔开。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 让数组中的每一项变成双倍</span>
const numbers = ;
const doubles </span>= numbers.map((item,index) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;li style={{ color: 'red', fontWeight: 200 }} key={index}&gt; {item * 2}}&lt;/li&gt;
})</pre>
</div>
<p>使用图片</p>
<div class="cnblogs_code">
<pre>import React from "react"<span style="color: rgba(0, 0, 0, 1)">;

import like from </span>"./assets/images/like.png"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> PostItem(props) {
const { post, onVote } </span>=<span style="color: rgba(0, 0, 0, 1)"> props;
const handleVote </span>= () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    onVote(post.id);
};

</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
    </span>&lt;li&gt;
      &lt;div&gt;{post.title}&lt;/div&gt;
      &lt;div&gt;<span style="color: rgba(0, 0, 0, 1)">
      创建人:</span>&lt;span&gt;{post.author}&lt;/span&gt;
      &lt;/div&gt;
      &lt;div&gt;<span style="color: rgba(0, 0, 0, 1)">
      创建时间:</span>&lt;span&gt;{post.date}&lt;/span&gt;
      &lt;/div&gt;
      &lt;div&gt;
      &lt;span&gt;
          &lt;img src={like} onClick={handleVote} /&gt;
      &lt;/span&gt;
      &lt;span&gt;{post.vote}&lt;/span&gt;
      &lt;/div&gt;
    &lt;/li&gt;
<span style="color: rgba(0, 0, 0, 1)">);
}

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> PostItem;</pre>
</div>
<p>注意图片不要放到src目录以外。</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230222161032551-860062032.png" alt="" width="317" height="365" loading="lazy"></p>
<p>&nbsp;</p>
<h2>2.9、组件的生命周期</h2>
<p>其实React组件并不是真正的DOM, 而是会生成JS对象的虚拟DOM, 虚拟DOM会经历创建,更新,删除的过程</p>
<p>这一个完整的过程就构成了组件的生命周期,React提供了钩子函数让我们可以在组件生命周期的不同阶段添加操作</p>
<p data-pid="01TyC5_B">在&nbsp;React v17版本删除<code>componentWillMount()</code>、<code>componentWillReceiveProps()</code>、<code>componentWillUpdate()</code>&nbsp;这三个函数,保留使用&nbsp;<code>UNSAFE_componentWillMount()</code>、<code>UNSAFE_componentWillReceiveProps()</code>、<code>UNSAFE_componentWillUpdate()</code></p>
<p data-pid="lYY7ShLH">这张图是从&nbsp;react生命周期链接里找的,里面有可以根据react不同版本查看对应的生命周期函数。</p>
<h3 data-pid="rOlM4FnY">2.9.1、生命周期总览</h3>
<p data-pid="rOlM4FnY">常用的:</p>
<p data-pid="rOlM4FnY"><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230223084424388-213799748.png" alt="" loading="lazy"></p>
<p>&nbsp;</p>
<p>&nbsp;不常用的:</p>
<p data-pid="rOlM4FnY"><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230223084343796-444525739.png" alt="" loading="lazy"></p>
<p data-pid="6kDNcdCM"><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230223095249309-916031090.png" alt="" loading="lazy"></p>
<p data-pid="6kDNcdCM">react的生命周期大概分为</p>
<ul>
<li data-pid="lUhdA9Ac">组件装载(Mount)组件第一次渲染到Dom树</li>
<li data-pid="cNC_eMBF">组件更新(update)组件state,props变化引发的重新渲染</li>
<li data-pid="Hyd3g1Z2">组件卸载(Unmount)组件从Dom树删除</li>
</ul>
<h3>2.9.2、构造方法&nbsp;constructor&nbsp;</h3>
<div>
<p>说明:</p>
<ol>
<li>如果不需要初始化state,或者不进行方法的绑定,则不需要实现constructor构造函数</li>
<li>在组件挂载前调用构造函数,如果继承<code>React.Component</code>,则必须调用<code>super(props)</code></li>
<li>constructor通常用于处理了state初始化和为事件处理函数绑定<code>this</code>实例</li>
<li>尽量避免将props外部数据赋值给组件内部state状态</li>
</ol>
<p>注意: constructor 构造函数只在初始化化的时候执行一次</p>
<p>示例代码如下:</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

class Counter extends Component {
constructor(props) {
    super(props);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.state =<span style="color: rgba(0, 0, 0, 1)"> {
      count: </span>0<span style="color: rgba(0, 0, 0, 1)">,
    };
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.handleClick = <span style="color: rgba(0, 0, 255, 1)">this</span>.handleClick.bind(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
}

handleClick() {
    console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.setState({ count: <span style="color: rgba(0, 0, 255, 1)">this</span>.state.count + 1<span style="color: rgba(0, 0, 0, 1)"> });
}

render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div&gt;
      &lt;h2&gt;计算器&lt;/h2&gt;
      &lt;button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleClick}&gt;点击加1&lt;/button&gt;
      &lt;span&gt; {<span style="color: rgba(0, 0, 255, 1)">this</span>.state.count} &lt;/span&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Counter;</pre>
</div>
<p>运行结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230223103338993-487304316.png" alt="" width="886" height="571" loading="lazy"></p>
<p>组件state也可以不定义在constructor构造函数中,事件函数也可以通过箭头函数处理this问题</p>
<p>因此如果不想使用constructor 也可以将两者移出</p>
<p>示例代码如下</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

class Counter extends Component {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">初始化组件状态</span>
state =<span style="color: rgba(0, 0, 0, 1)"> {
    count: </span>0<span style="color: rgba(0, 0, 0, 1)">,
};

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">箭头函数处理this问题</span>
handleClick = () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.setState(() =&gt; ({ count: <span style="color: rgba(0, 0, 255, 1)">this</span>.state.count + 1<span style="color: rgba(0, 0, 0, 1)"> }));
};

render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div&gt;
      &lt;h2&gt;计算器&lt;/h2&gt;
      &lt;button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleClick}&gt;点击加1&lt;/button&gt;
      &lt;span&gt; {<span style="color: rgba(0, 0, 255, 1)">this</span>.state.count} &lt;/span&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Counter;</pre>
</div>
<p>输出结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230223104349947-781155834.png" alt="" width="1005" height="643" loading="lazy"></p>
<p>&nbsp;箭头函数中的this指向了当前组件实例,而普通函数则指向undefined。<code>箭头函数</code>内<code>this</code>是词法作用域,由上下文确定。</p>
</div>
<h3>2.9.3、组件装载(Mount)</h3>
<ul>
<li data-pid="Auic58ge">constructor: 在此初始化state,绑定成员函数this环境,props本地化,constructor 构造函数只在初始化化的时候执行一次</li>
<li data-pid="YnX7bNP2">getDerivedStateFromProps:在创建时或更新时的render之前执行,让&nbsp;<code>props</code>&nbsp;能更新到组件内部&nbsp;<code>state</code>中,必须是静态的。它应返回一个对象来更新 state,如果返回&nbsp;<code class="gatsby-code-text">null</code>&nbsp;则不更新任何内容。</li>
<li data-pid="YnX7bNP2"><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230223090021209-1360570087.png" alt="" width="482" height="159" loading="lazy"></li>
<li data-pid="zezteVix">render:&nbsp;<code>渲染函数</code>,唯一的一定不能省略的函数,必须有返回值,返回null或false表示不渲染任何DOM元素。它是一个仅仅用于渲染的纯函数,返回值完全取决于this.state和this.props,不能在函数中任何修改props、state、拉取数据等具有副作用的操作。render函数返回的是JSX的对象,该函数并不因为这渲染到DOM树,何时进行真正的渲染是有React库决定的。</li>
<li data-pid="-jrboO4p">componentDidMount:&nbsp;<code>挂载成功函数</code>。该函数不会再render函数调用完成之后立即调用,因为render函数仅仅是返回了JSX的对象,并没有立即挂载到DOM树上,而componentDidMount是在组件被渲染到DOM树之后被调用的。另外,componentDidMount函数在进行服务器端渲染时不会被调用。</li>
</ul>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

class Hi extends Component {
constructor(props) {
    super(props);
    console.log(</span>"constructor()函数被调用,构造函数"<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.state =<span style="color: rgba(0, 0, 0, 1)"> {
      n: </span>100<span style="color: rgba(0, 0, 0, 1)">,
    };
}
render() {
    console.log(</span>"render()函数被调用,返回UI描述"<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;h2&gt;Hello {<span style="color: rgba(0, 0, 255, 1)">this</span>.props.name}!&lt;/h2&gt;;
<span style="color: rgba(0, 0, 0, 1)">}

static getDerivedStateFromProps() {
    console.log(
      </span>"getDerivedStateFromProps()函数被调用,让 props 能更新到组件内部 state中"<span style="color: rgba(0, 0, 0, 1)">
    );
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
}

componentDidMount() {
    console.log(</span>"componentDidMount()函数被调用,组件被挂载到DOM后"<span style="color: rgba(0, 0, 0, 1)">);
}
}

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Hi;</pre>
</div>
<p>运行结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230223095934462-370620075.png" alt="" loading="lazy"></p>
<p>修改案例,查看this</p>
<div class="cnblogs_code"><img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_28a721e4-2054-431f-8adb-eb34ac096042" class="code_img_closed"><img src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" id="code_img_opened_28a721e4-2054-431f-8adb-eb34ac096042" class="code_img_opened" style="display: none">
<div id="cnblogs_code_open_28a721e4-2054-431f-8adb-eb34ac096042" class="cnblogs_code_hide">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

class Hi extends Component {
constructor(props) {
    super(props);
    console.log(</span>"constructor()函数被调用,构造函数", <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.state =<span style="color: rgba(0, 0, 0, 1)"> {
      n: </span>100<span style="color: rgba(0, 0, 0, 1)">,
    };
}
render() {
    console.log(</span>"render()函数被调用,返回UI描述", <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;h2&gt;Hello {<span style="color: rgba(0, 0, 255, 1)">this</span>.props.name}!&lt;/h2&gt;;
<span style="color: rgba(0, 0, 0, 1)">}

static getDerivedStateFromProps() {
    console.log(
      </span>"getDerivedStateFromProps()函数被调用,让 props 能更新到组件内部 state中"<span style="color: rgba(0, 0, 0, 1)">,
      </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">
    );
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
}

componentDidMount() {
    console.log(</span>"componentDidMount()函数被调用,组件被挂载到DOM后", <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
}
}

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Hi;</pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<h3>2.9.4、getDerivedStateFromProps</h3>
<div>
<div>
<p><code>getDerivedStateFromProps</code> 的<strong>作用</strong>是为了让 <code>props</code> 能更新到组件内部 <code>state</code>中。他会在<code>render</code>方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 <code>state</code>,如果返回 <code>null</code> 则不更新任何内容。但注意在没有内容更新的情况下也一定要返回一个<code>null</code>值。不然会报错。</p>
<p>这个生命周期的意思就是从props中获取state,这个生命周期替换了原有的生命周期函数<code>componentWillReceiveProps</code>,<code>getDerivedStateFromProps</code>,它<br>
是一个静态函数,也就是说不能通过this来访问class的属性,也不<strong>推荐直接访问属性</strong>。而是通过参数提供的<code>nextPros</code>以及<code>prevState</code>来进行判断,根据新传入的<code>props</code>来映射<code>state</code>。<br>
<strong>注意:</strong>如果传入的props不影响state,则必须返回一个null,一般尽量写在末尾。</p>


</div>


</div>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230307113851024-1879261054.png" alt="" loading="lazy"></p>
<p>counter3.js</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class Counter3 extends Component {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">构造器</span>
<span style="color: rgba(0, 0, 0, 1)">constructor(props) {
    super(props);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.state =<span style="color: rgba(0, 0, 0, 1)"> { n: props.a };
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.clickHandler = <span style="color: rgba(0, 0, 255, 1)">this</span>.clickHandler.bind(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);

    console.log(</span>"constructor,构造器", <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获得状态从属性中</span>
<span style="color: rgba(0, 0, 0, 1)">static getDerivedStateFromProps(nextProps, state) {
    console.log(</span>"getDerivedStateFromProps,获得状态从属性中", <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
    console.log(nextProps, state);
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (nextProps.a !==<span style="color: rgba(0, 0, 0, 1)"> state.n) {
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> { n: nextProps.a };
    }
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
}

clickHandler() {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.setState({ n: <span style="color: rgba(0, 0, 255, 1)">this</span>.state.n + 1<span style="color: rgba(0, 0, 0, 1)"> });
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">渲染UI</span>
<span style="color: rgba(0, 0, 0, 1)">render() {
    console.log(</span>"render,渲染UI", <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div style={{ backgroundColor: "lightblue" }}&gt;
      &lt;h2&gt;Counter3计数器&lt;/h2&gt;
      &lt;button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.clickHandler}&gt;{<span style="color: rgba(0, 0, 255, 1)">this</span>.state.n}&lt;/button&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">挂载完成</span>
<span style="color: rgba(0, 0, 0, 1)">componentDidMount() {
    console.log(</span>"componentDidMount,挂载完成", <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
}
}</span></pre>
</div>
<p>Box.js</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;
import Counter3 from </span>"./Counter3"<span style="color: rgba(0, 0, 0, 1)">;

export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class Box extends Component {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">是否显示</span>
state = { isShow: <span style="color: rgba(0, 0, 255, 1)">true</span>, n: 100<span style="color: rgba(0, 0, 0, 1)"> };

clickHandle </span>= () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.setState({ n: <span style="color: rgba(0, 0, 255, 1)">this</span>.state.n + 100<span style="color: rgba(0, 0, 0, 1)"> });
};

render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div style={{ backgroundColor: "lightgreen", padding: "20px" }}&gt;
      &lt;h2&gt;Box父组件&lt;/h2&gt;
      &lt;Counter3 a={<span style="color: rgba(0, 0, 255, 1)">this</span>.state.n} /&gt;
      &lt;button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.clickHandle}&gt;n={<span style="color: rgba(0, 0, 255, 1)">this</span>.state.n}&lt;/button&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}</span></pre>
</div>
<p>index.js</p>
<div class="cnblogs_code">
<pre>import React from "react"<span style="color: rgba(0, 0, 0, 1)">;
import ReactDOM from </span>"react-dom/client"<span style="color: rgba(0, 0, 0, 1)">;
import Box from </span>"./Box"<span style="color: rgba(0, 0, 0, 1)">;


</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">虚拟DOM,JSX</span>
const vnode =<span style="color: rgba(0, 0, 0, 1)"> (
</span>&lt;div&gt;
    &lt;Box /&gt;
&lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">2、创建根节点</span>
const root = ReactDOM.createRoot(document.querySelector("#root"<span style="color: rgba(0, 0, 0, 1)">));

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">3、将虚拟DOM节点挂载到根节点上</span>
root.render(vnode);</pre>
</div>
<p>结果</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230307113557763-973725275.png" alt="" width="1081" height="640" loading="lazy"></p>
<h3>2.9.5、组件更新(update)</h3>
<p data-pid="3lrGhK9g">当组件挂载到DOM树上之后,<code>props/state</code>被修改会导致组件进行更新操作。更新过程会以此调用如下的生命周期函数:</p>
<ul>
<li data-pid="xH44zUBK">shouldComponentUpdate(nextProps, nextState):<code>是否重新渲染组件</code>&nbsp;返回bool值,true表示要更新,false表示不更新,使用得当将大大提高React组件的性能,避免不需要的渲染。</li>
<li data-pid="i5YM5FZy">getSnapshotBeforeUpdate:在render之后,React更新dom之前执行。它使您的组件可以在可能更改之前从DOM捕获一些信息(例如滚动位置),例如在聊天气泡页中用来计算滚动高度。它返回的任何值都将作为参数传递给componentDidUpdate()</li>
<li data-pid="q_K4TNO_">render:&nbsp;<code>渲染函数</code>。</li>
<li data-pid="V49DrHEU">componentDidUpdate:&nbsp;<code>更新完成函数</code>。</li>
</ul>
<h3>2.9.6、shouldComponentUpdate函数</h3>
<p>说明:</p>
<ol>
<li><code>shouldComponentUpdate</code>函数使用来判读是否更新渲染组件</li>
<li>函数返回值是一个布尔值,为true,表示继续走其他的生命周期函数,更新渲染组件</li>
<li>如果返回一个false表示,不在继续向下执行其他的生命周期函数,到此终止,不更新组件渲染</li>
<li>函数接受两个参数, 第一个参数为props将要更新的数据, 第二个参数为state将要更新的数据</li>
</ol>
<pre class="gatsby-code-jsx"><code class="gatsby-code-jsx"><span class="token function">shouldComponentUpdate<span class="token punctuation">(nextProps<span class="token punctuation">, nextState<span class="token punctuation">)</span></span></span></span></code></pre>
<p>根据&nbsp;<code class="gatsby-code-text">shouldComponentUpdate()</code>&nbsp;的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。默认行为是 state 每次发生变化组件都会重新渲染。大部分情况下,你应该遵循默认行为。</p>
<p>当 props 或 state 发生变化时,<code class="gatsby-code-text">shouldComponentUpdate()</code>&nbsp;会在渲染执行之前被调用。返回值默认为 true。首次渲染或使用&nbsp;<code class="gatsby-code-text">forceUpdate()</code>&nbsp;时不会调用该方法。</p>
<p>此方法仅作为性能优化的方式而存在。不要企图依靠此方法来“阻止”渲染,因为这可能会产生 bug。你应该考虑使用内置的&nbsp;<code class="gatsby-code-text">PureComponent</code>&nbsp;组件,而不是手动编写&nbsp;<code class="gatsby-code-text">shouldComponentUpdate()</code>。<code class="gatsby-code-text">PureComponent</code>&nbsp;会对 props 和 state 进行浅层比较,并减少了跳过必要更新的可能性。</p>
<p>如果你一定要手动编写此函数,可以将&nbsp;<code class="gatsby-code-text">this.props</code>&nbsp;与&nbsp;<code class="gatsby-code-text">nextProps</code>&nbsp;以及&nbsp;<code class="gatsby-code-text">this.state</code>&nbsp;与<code class="gatsby-code-text">nextState</code>&nbsp;进行比较,并返回&nbsp;<code class="gatsby-code-text">false</code>&nbsp;以告知 React 可以跳过更新。请注意,返回&nbsp;<code class="gatsby-code-text">false</code>&nbsp;并不会阻止子组件在 state 更改时重新渲染。</p>
<p>我们不建议在&nbsp;<code class="gatsby-code-text">shouldComponentUpdate()</code>&nbsp;中进行深层比较或使用&nbsp;<code class="gatsby-code-text">JSON.stringify()</code>。这样非常影响效率,且会损害性能。</p>
<p>目前,如果&nbsp;<code class="gatsby-code-text">shouldComponentUpdate()</code>&nbsp;返回&nbsp;<code class="gatsby-code-text">false</code>,则不会调用&nbsp;<code class="gatsby-code-text">UNSAFE_componentWillUpdate()</code>,<code class="gatsby-code-text">render()</code>&nbsp;和&nbsp;<code class="gatsby-code-text">componentDidUpdate()</code>。后续版本,React 可能会将&nbsp;<code class="gatsby-code-text">shouldComponentUpdate</code>&nbsp;视为提示而不是严格的指令,并且,当返回&nbsp;<code class="gatsby-code-text">false</code>&nbsp;时,仍可能导致组件重新渲染。</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

class Counter extends Component {
constructor(props) {
    super(props);
    console.log(</span>"constructor()构造函数", <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">初始化组件状态</span>
state =<span style="color: rgba(0, 0, 0, 1)"> {
    count: </span>0<span style="color: rgba(0, 0, 0, 1)">,
};

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">箭头函数处理this问题</span>
handleClick = () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    console.log(</span>"调用this.setState更新状态", <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.setState(() =&gt; ({ count: <span style="color: rgba(0, 0, 255, 1)">this</span>.state.count + 1<span style="color: rgba(0, 0, 0, 1)"> }));
};

render() {
    console.log(</span>"render()渲染UI", <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div&gt;
      &lt;h2&gt;计算器&lt;/h2&gt;
      &lt;button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleClick}&gt;点击加1&lt;/button&gt;
      &lt;span&gt; {<span style="color: rgba(0, 0, 255, 1)">this</span>.state.count} &lt;/span&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}

static getDerivedStateFromProps() {
    console.log(
      </span>"getDerivedStateFromProps()函数被调用,让 props 能更新到组件内部 state中"<span style="color: rgba(0, 0, 0, 1)">,
      </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">
    );
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">是否允许组件更新</span>
<span style="color: rgba(0, 0, 0, 1)">shouldComponentUpdate(props, state) {
    console.log(</span>"shouldComponentUpdate()函数被调用,", props, state, <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
}

componentDidMount() {
    console.log(</span>"componentDidMount()挂载成功", <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
}
}

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Counter;</pre>
</div>
<p>结果</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230223113436694-1935485773.png" alt="" loading="lazy"></p>
<h3>2.9.7、componentDidUpdate函数</h3>
<p>说明:</p>
<ol>
<li>组件render执行后,页面渲染完毕了以后执行的函数</li>
<li>接受两个参数,分别为更新前的props和state</li>
</ol>
<pre class="gatsby-code-jsx"><code class="gatsby-code-jsx"><span class="token function">componentDidUpdate<span class="token punctuation">(prevProps<span class="token punctuation">, prevState<span class="token punctuation">, snapshot<span class="token punctuation">)</span></span></span></span></span></code></pre>
<p><code class="gatsby-code-text">componentDidUpdate()</code>&nbsp;会在更新后会被立即调用。首次渲染不会执行此方法。</p>
<p>当组件更新后,可以在此处对 DOM 进行操作。如果你对更新前后的 props 进行了比较,也可以选择在此处进行网络请求。(例如,当 props 未发生变化时,则不会执行网络请求)。</p>
<div class="gatsby-highlight" data-language="jsx">
<pre class="gatsby-code-jsx"><code class="gatsby-code-jsx"><span class="token function">componentDidUpdate<span class="token punctuation">(<span class="token parameter">prevProps<span class="token punctuation">) <span class="token punctuation">{
<span class="token comment">// 典型用法(不要忘记比较 props):
<span class="token keyword">if <span class="token punctuation">(<span class="token keyword">this<span class="token punctuation">.props<span class="token punctuation">.userID <span class="token operator">!== prevProps<span class="token punctuation">.userID<span class="token punctuation">) <span class="token punctuation">{
    <span class="token keyword">this<span class="token punctuation">.<span class="token function">fetchData<span class="token punctuation">(<span class="token keyword">this<span class="token punctuation">.props<span class="token punctuation">.userID<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}
<span class="token punctuation">}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>你也可以在&nbsp;<code class="gatsby-code-text">componentDidUpdate()</code>&nbsp;中直接调用&nbsp;<code class="gatsby-code-text">setState()</code>,但请注意它必须被包裹在一个条件语句里,正如上述的例子那样进行处理,否则会导致死循环。它还会导致额外的重新渲染,虽然用户不可见,但会影响组件性能。不要将 props “镜像”给 state,请考虑直接使用 props。 欲了解更多有关内容,请参阅为什么 props 复制给 state 会产生 bug。</p>
<p>如果组件实现了&nbsp;<code class="gatsby-code-text">getSnapshotBeforeUpdate()</code>&nbsp;生命周期(不常用),则它的返回值将作为&nbsp;<code class="gatsby-code-text">componentDidUpdate()</code>&nbsp;的第三个参数 “snapshot” 参数传递。否则此参数将为 undefined。</p>
<blockquote>
<p>注意</p>
<p>如果&nbsp;<code class="gatsby-code-text">shouldComponentUpdate()</code>&nbsp;返回值为 false,则不会调用&nbsp;<code class="gatsby-code-text">componentDidUpdate()</code>。</p>
</blockquote>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

class Counter extends Component {
constructor(props) {
    super(props);
    console.log(</span>"constructor()构造函数", <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">初始化组件状态</span>
state =<span style="color: rgba(0, 0, 0, 1)"> {
    count: </span>0<span style="color: rgba(0, 0, 0, 1)">,
};

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">箭头函数处理this问题</span>
handleClick = () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    console.log(</span>"调用this.setState更新状态", <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.setState(() =&gt; ({ count: <span style="color: rgba(0, 0, 255, 1)">this</span>.state.count + 1<span style="color: rgba(0, 0, 0, 1)"> }));
};

render() {
    console.log(</span>"render()渲染UI", <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div&gt;
      &lt;h2&gt;计算器&lt;/h2&gt;
      &lt;button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleClick}&gt;点击加1&lt;/button&gt;
      &lt;span&gt; {<span style="color: rgba(0, 0, 255, 1)">this</span>.state.count} &lt;/span&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}

static getDerivedStateFromProps() {
    console.log(
      </span>"getDerivedStateFromProps()函数被调用,让 props 能更新到组件内部 state中"<span style="color: rgba(0, 0, 0, 1)">,
      </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">
    );
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">是否允许组件更新</span>
<span style="color: rgba(0, 0, 0, 1)">shouldComponentUpdate(props, state) {
    console.log(
      </span>"shouldComponentUpdate()函数被调用,是否允许组件更新"<span style="color: rgba(0, 0, 0, 1)">,
      props,
      state,
      </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">
    );
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">组件更新后</span>
<span style="color: rgba(0, 0, 0, 1)">componentDidUpdate(prevProps, prevState) {
    console.log(
      </span>"componentDidUpdate()被调用,组件更新后"<span style="color: rgba(0, 0, 0, 1)">,
      prevProps,
      prevState,
      </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">
    );
}

componentDidMount() {
    console.log(</span>"componentDidMount()挂载成功", <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
}
}

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Counter;</pre>
</div>
<p>结果</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230223161117186-2124266685.png" alt="" loading="lazy"></p>
<p>&nbsp;注意state是更新前的状态。</p>
<h3><strong>2.9.8、React.createRef()&nbsp;非生命周期函数</strong></h3>
<strong>获取非受控组件</strong>
<p>可以通过React.createRef()创建Refs并通过ref属性联系到React组件。Refs通常当组件被创建时被分配给实例变量,这样它们就能在组件中被引用。</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

class RefDemoCom extends Component {
constructor(props) {
    super(props);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.txtName =<span style="color: rgba(0, 0, 0, 1)"> React.createRef();
}

clickHandler </span>= () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.txtName);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.txtName.current.focus();
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.txtName.current.value = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date().toLocaleString();
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.txtName.current.style.color = "blue"<span style="color: rgba(0, 0, 0, 1)">;
};

render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div&gt;
      &lt;h2&gt;React.createRef()&lt;/h2&gt;
      &lt;input type="text" ref={<span style="color: rgba(0, 0, 255, 1)">this</span>.txtName} /&gt;
      &lt;button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.clickHandler}&gt;点击获得按钮&lt;/button&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> RefDemoCom;</pre>
</div>
<p>&nbsp;</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230223162842947-427726441.png" alt="" loading="lazy"></p>
<h3>2.9.9、getSnapshotBeforeUpdate函数</h3>
<p>说明:</p>
<ol>
<li><code>getSnapshotBeforeUpdate</code>必须跟<code>componentDidUpdate</code>一起使用,否则就报错</li>
<li>但是不能与<code>componentWillReceiveProps</code>和<code>componentWillUpdate</code>一起使用</li>
<li>state和props更新都会触发这个函数的执行, 在render函数之后执行</li>
<li>接受两个参数,更新前的props和当前的state</li>
<li>函数必须return 返回结果</li>
<li><code>getSnapshotBeforeUpdate</code>返回的结果将作为参数传递给<code>componentDidUpdate</code></li>
</ol>
<pre class="gatsby-code-jsx"><code class="gatsby-code-jsx"><span class="token function">getSnapshotBeforeUpdate<span class="token punctuation">(prevProps<span class="token punctuation">, prevState<span class="token punctuation">)</span></span></span></span></code></pre>
<p><code class="gatsby-code-text">getSnapshotBeforeUpdate()</code>&nbsp;在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期方法的任何返回值将作为参数传递给&nbsp;<code class="gatsby-code-text">componentDidUpdate()</code>。</p>
<p>此用法并不常见,但它可能出现在 UI 处理中,如需要以特殊方式处理滚动位置的聊天线程等。</p>
<p>应返回 snapshot 的值(或&nbsp;<code class="gatsby-code-text">null</code>)。</p>
<p>例如:</p>
<p>&nbsp;</p>
<div class="gatsby-highlight">
<pre class="gatsby-code-jsx"><code><span class="token keyword">class <span class="token class-name">ScrollingList <span class="token keyword">extends <span class="token class-name">React<span class="token punctuation">.Component <span class="token punctuation">{
<span class="token function">constructor<span class="token punctuation">(<span class="token parameter">props<span class="token punctuation">) <span class="token punctuation">{
    <span class="token keyword">super<span class="token punctuation">(props<span class="token punctuation">)<span class="token punctuation">;
    <span class="token keyword">this<span class="token punctuation">.listRef <span class="token operator">= React<span class="token punctuation">.<span class="token function">createRef<span class="token punctuation">(<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}

<span class="token function">getSnapshotBeforeUpdate<span class="token punctuation">(<span class="token parameter">prevProps<span class="token punctuation">, prevState<span class="token punctuation">) <span class="token punctuation">{
    <span class="token comment">// 我们是否在 list 中添加新的 items ?
    <span class="token comment">// 捕获滚动​​位置以便我们稍后调整滚动位置。
    <span class="token keyword">if <span class="token punctuation">(prevProps<span class="token punctuation">.list<span class="token punctuation">.length <span class="token operator">&lt; <span class="token keyword">this<span class="token punctuation">.props<span class="token punctuation">.list<span class="token punctuation">.length<span class="token punctuation">) <span class="token punctuation">{
      <span class="token keyword">const list <span class="token operator">= <span class="token keyword">this<span class="token punctuation">.listRef<span class="token punctuation">.current<span class="token punctuation">;
      <span class="token keyword">return list<span class="token punctuation">.scrollHeight <span class="token operator">- list<span class="token punctuation">.scrollTop<span class="token punctuation">;
    <span class="token punctuation">}
    <span class="token keyword">return <span class="token keyword">null<span class="token punctuation">;
<span class="token punctuation">}

<span class="token function">componentDidUpdate<span class="token punctuation">(<span class="token parameter">prevProps<span class="token punctuation">, prevState<span class="token punctuation">, snapshot<span class="token punctuation">) <span class="token punctuation">{
    <span class="token comment">// 如果我们 snapshot 有值,说明我们刚刚添加了新的 items,
    <span class="token comment">// 调整滚动位置使得这些新 items 不会将旧的 items 推出视图。
    <span class="token comment">//(这里的 snapshot 是 getSnapshotBeforeUpdate 的返回值)
    <span class="token keyword">if <span class="token punctuation">(snapshot <span class="token operator">!== <span class="token keyword">null<span class="token punctuation">) <span class="token punctuation">{
      <span class="token keyword">const list <span class="token operator">= <span class="token keyword">this<span class="token punctuation">.listRef<span class="token punctuation">.current<span class="token punctuation">;
      list<span class="token punctuation">.scrollTop <span class="token operator">= list<span class="token punctuation">.scrollHeight <span class="token operator">- snapshot<span class="token punctuation">;
    <span class="token punctuation">}
<span class="token punctuation">}

<span class="token function">render<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
    <span class="token keyword">return <span class="token punctuation">(
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;div <span class="token attr-name">ref<span class="token script language-javascript"><span class="token script-punctuation punctuation">=<span class="token punctuation">{<span class="token keyword">this<span class="token punctuation">.listRef<span class="token punctuation">}<span class="token punctuation">&gt;<span class="token punctuation">{<span class="token comment">/* ...contents... */<span class="token punctuation">}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/div<span class="token punctuation">&gt;
    <span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}
<span class="token punctuation">}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>在上述示例中,重点是从&nbsp;<code class="gatsby-code-text">getSnapshotBeforeUpdate</code>&nbsp;读取&nbsp;<code class="gatsby-code-text">scrollHeight</code>&nbsp;属性,因为 “render” 阶段生命周期(如&nbsp;<code class="gatsby-code-text">render</code>)和 “commit” 阶段生命周期(如&nbsp;<code class="gatsby-code-text">getSnapshotBeforeUpdate</code>&nbsp;和&nbsp;<code class="gatsby-code-text">componentDidUpdate</code>)之间可能存在延迟。</p>
示例:
<div>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

class BeforeUpdateDemo extends Component {
constructor(props) {
    super(props);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.state = { name: "tom"<span style="color: rgba(0, 0, 0, 1)"> };
}

componentDidMount() {
    setTimeout(() </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.setState({ name: "jack"<span style="color: rgba(0, 0, 0, 1)"> });
    }, </span>1000<span style="color: rgba(0, 0, 0, 1)">);
}

getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log(</span>"更新前的状态:"<span style="color: rgba(0, 0, 0, 1)">, prevState);
    document.getElementById(</span>"prev").innerHTML = "更新前:" +<span style="color: rgba(0, 0, 0, 1)"> prevState.name;
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> document.getElementById("container"<span style="color: rgba(0, 0, 0, 1)">).clientHeight;
}

componentDidUpdate(prevProps, prevState, preHeight) {
    console.log(</span>"更新后的状态:", <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state);
    document.getElementById(</span>"next").innerHTML = "更新后:" + <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.name;
    const height </span>= document.getElementById("container"<span style="color: rgba(0, 0, 0, 1)">).clientHeight;
    console.log(</span>"原高度:" +<span style="color: rgba(0, 0, 0, 1)"> preHeight);
    console.log(</span>"现高度:" +<span style="color: rgba(0, 0, 0, 1)"> height);
}

render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div id="container"&gt;
      &lt;h2&gt;示例&lt;/h2&gt;
      &lt;div id="prev"&gt;&lt;/div&gt;
      &lt;div id="next"&gt;&lt;/div&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> BeforeUpdateDemo;</pre>
</div>
<p>运行结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230224095107077-248715769.png" alt="" width="574" height="458" loading="lazy"></p>
<p>示例:</p>
<p>UserList.js</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class UserList extends Component {
constructor(props) {
    super(props);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.state =<span style="color: rgba(0, 0, 0, 1)"> {
      count: </span>100<span style="color: rgba(0, 0, 0, 1)">,
      users: [</span>"jack", "rose", "mali"<span style="color: rgba(0, 0, 0, 1)">],
    };
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.div1 =<span style="color: rgba(0, 0, 0, 1)"> React.createRef();
}

clickHandler </span>= () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({
      count: </span><span style="color: rgba(0, 0, 255, 1)">this</span>.state.count + 1<span style="color: rgba(0, 0, 0, 1)">,
      users: [...</span><span style="color: rgba(0, 0, 255, 1)">this</span>.state.users, <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.count],
    });
};

render() {
    console.log(</span>"render被调用"<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;<span style="color: rgba(0, 0, 0, 1)">div
      id</span>="div1"<span style="color: rgba(0, 0, 0, 1)">
      ref</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.div1}
      style</span>={{ backgroundColor: "yellow", padding: "10px", margin: "10px"<span style="color: rgba(0, 0, 0, 1)"> }}
      </span>&gt;
      &lt;h2&gt;用户信息&lt;/h2&gt;
      &lt;div&gt;<span style="color: rgba(0, 0, 0, 1)">
          用户名:</span>&lt;span&gt;{<span style="color: rgba(0, 0, 255, 1)">this</span>.props.username}&lt;/span&gt;
      &lt;/div&gt;
      &lt;div&gt;
          &lt;button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.clickHandler}&gt;{<span style="color: rgba(0, 0, 255, 1)">this</span>.state.count}&lt;/button&gt;
      &lt;/div&gt;
      &lt;div&gt;
          &lt;ul&gt;<span style="color: rgba(0, 0, 0, 1)">
            {</span><span style="color: rgba(0, 0, 255, 1)">this</span>.state.users.map((user) =&gt;<span style="color: rgba(0, 0, 0, 1)"> (
            </span>&lt;li key={user}&gt;{user}&lt;/li&gt;
<span style="color: rgba(0, 0, 0, 1)">            ))}
          </span>&lt;/ul&gt;
      &lt;/div&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">更新前获得DOM的快照信息</span>
<span style="color: rgba(0, 0, 0, 1)">getSnapshotBeforeUpdate(prevprops, prevstate) {
    console.log(</span>"上次的属性:", prevprops, "当前的属性:", <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props);
    console.log(</span>"上次的状态:", prevstate, "当前的状态:", <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state);
    console.log(</span>"getSnapshotBeforeUpdate被调用"<span style="color: rgba(0, 0, 0, 1)">);
    console.log(</span>"更新前的DOM:", <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.div1.current.innerHTML);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.div1.current.clientHeight;
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">更新完成时执行的函数</span>
<span style="color: rgba(0, 0, 0, 1)">componentDidUpdate(prevProps, prevState, snapshot) {
    console.log(</span>"更新前DOM的高度值:"<span style="color: rgba(0, 0, 0, 1)">, snapshot);
    console.log(</span>"当前DOM的高度值:", <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.div1.current.clientHeight);
    console.log(</span>"componentDidUpdate被调用"<span style="color: rgba(0, 0, 0, 1)">);
    console.log(</span>"更新后的DOM:", <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.div1.current.innerHTML);
}
}</span></pre>
</div>
<p>Box.js</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;
import Counter4 from </span>"./Counter4"<span style="color: rgba(0, 0, 0, 1)">;
import UserList from </span>"./UserList"<span style="color: rgba(0, 0, 0, 1)">;

export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class Box extends Component {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">是否显示</span>
state = { isShow: <span style="color: rgba(0, 0, 255, 1)">true</span>, x: 100, username: "jack"<span style="color: rgba(0, 0, 0, 1)"> };

clickHandle </span>= () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({
      x: </span><span style="color: rgba(0, 0, 255, 1)">this</span>.state.x + 100<span style="color: rgba(0, 0, 0, 1)">,
      username: </span><span style="color: rgba(0, 0, 255, 1)">this</span>.state.username + "!"<span style="color: rgba(0, 0, 0, 1)">,
    });
};

render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div style={{ backgroundColor: "lightgreen", padding: "20px" }}&gt;
      &lt;h2&gt;Box父组件&lt;/h2&gt;
      &lt;UserList username={<span style="color: rgba(0, 0, 255, 1)">this</span>.state.username} /&gt;
      &lt;button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.clickHandle}&gt; a={<span style="color: rgba(0, 0, 255, 1)">this</span>.state.x} &lt;/button&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}</span></pre>
</div>
<p>运行效果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230310094834283-1507751695.png" alt="" loading="lazy"></p>
</div>
<h3>2.9.10.组件卸载(Unmount)</h3>
<ul>
<li data-pid="kkFQyS5e">componentWillUnmount:当React组件要从DOM树上删除前,会调用一次这个函数。这个函数经常用于去除componentDidMount函数带来的副作用,例如<code>清除计时器</code>、<code>清除事件监听</code>。</li>
</ul>
<p>示例:</p>
<p><code class="gatsby-code-text">componentWillUnmount()</code>&nbsp;会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在&nbsp;<code class="gatsby-code-text">componentDidMount()</code>&nbsp;中创建的订阅等。</p>
<p><code class="gatsby-code-text">componentWillUnmount()</code>&nbsp;中不应调用&nbsp;<code class="gatsby-code-text">setState()</code>,因为该组件将永远不会重新渲染。组件实例卸载后,将永远不会再挂载它。</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;

class HelloComponent extends Component {
componentWillUnmount() {
    console.log(</span>"componentWillUnmount()被调用了!"<span style="color: rgba(0, 0, 0, 1)">);
}
render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;div&gt;HelloComponent被加载了&lt;/div&gt;;
<span style="color: rgba(0, 0, 0, 1)">}
}

class WillUnmountDemo extends Component {
state </span>=<span style="color: rgba(0, 0, 0, 1)"> {
    i: </span>0<span style="color: rgba(0, 0, 0, 1)">,
};

handleClick </span>= () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({
      i: </span>++<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.i,
    });
};

render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div&gt;
      &lt;h2&gt;计算器&lt;/h2&gt;
      &lt;button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleClick}&gt;i的当前值:{<span style="color: rgba(0, 0, 255, 1)">this</span>.state.i}&lt;/button&gt;
      {<span style="color: rgba(0, 0, 255, 1)">this</span>.state.i % 2 === 0 ?<span style="color: rgba(0, 0, 0, 1)"> (
          </span>&lt;HelloComponent /&gt;
<span style="color: rgba(0, 0, 0, 1)">      ) : (
          </span>&lt;h2&gt;HelloComponent未加载&lt;/h2&gt;
<span style="color: rgba(0, 0, 0, 1)">      )}
      </span>&lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> WillUnmountDemo;</pre>
</div>
<p>结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230224105330447-2097779343.png" alt="" loading="lazy"></p>
<h3>2.9.11、React V16.3 新增的生命周期方法</h3>
<p data-pid="xyXJ6cv5">React v16.3虽然是一个小版本升级,但是却对React组件生命周期函数有巨大变化。</p>
<p data-pid="kminrZ6D">新增生命周期如下:</p>
<ul>
<li data-pid="hDSrJIrJ">getDerivedStateFromProps</li>
<li data-pid="NY5Kpp52">getSnapshotBeforeUpdate</li>
</ul>
<p data-pid="m2UFNOko">同时逐渐废弃了以下生命周期:</p>
<ul>
<li data-pid="EegoBwIJ">componentWillReceiveProps</li>
<li data-pid="sZhKZ2pA">componentWillMount</li>
<li data-pid="rFqD3V0R">componentWillUpdate</li>
</ul>
<p data-pid="RtcjxmPG">17 版本删除了&nbsp;<code>componentWillMount()</code>、<code>componentWillReceiveProps()</code>、<code>componentWillUpdate()</code>&nbsp;这三个函数,保留使用&nbsp;<code>UNSAFE_componentWillMount()</code>、<code>UNSAFE_componentWillReceiveProps()</code>、<code>UNSAFE_componentWillUpdate()</code></p>
<h3 data-pid="2pyoeLhn">2.9.12、不能使用setState的生命周期</h3>
<p data-pid="lYY7ShLH"><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230223083955913-1389753059.jpg" alt="" width="1123" height="705"></p>
<h3>2.9.13、小结</h3>
<p><img src="https://pic2.zhimg.com/80/v2-1bfaaee07f83cb32456e382c26f526d5_720w.webp" width="636" height="434" class="origin_image zh-lightbox-thumb lazy" data-caption="" data-size="normal" data-rawwidth="912" data-rawheight="622" data-original="https://pic2.zhimg.com/v2-1bfaaee07f83cb32456e382c26f526d5_r.jpg" data-actualsrc="https://pic2.zhimg.com/v2-1bfaaee07f83cb32456e382c26f526d5_b.jpg" data-lazy-status="ok"></p>
<h1>三、作业</h1>
<p>3.0、页面中有一个按钮,点击按钮显示隐藏右边的文字Hello,初始化时显示,点击第一次隐藏,再点击显示。。。</p>
<p>3.1、定义一个组件,当文本框中输入内容时在文本框后显示输入的值,双向绑定。</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202302/63651-20230222151947080-1065723587.png" alt="" loading="lazy"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>3.2、请完成课程中的所有示例。</p>
<p>3.3、请定义一个vue分页组件,可以实现客户端分页功能,接收参数</p>
<p><img src="https://images2017.cnblogs.com/blog/63651/201801/63651-20180103110507299-1799453712.png" alt="" class="medium-zoom-image"></p>
<p><img src="https://images2017.cnblogs.com/blog/63651/201801/63651-20180103110521565-887271076.png" alt="" class="medium-zoom-image"></p>
<p><img src="https://images2017.cnblogs.com/blog/63651/201801/63651-20180103110557596-353162854.png" alt="" class="medium-zoom-image"></p>
<p><img src="https://images2017.cnblogs.com/blog/63651/201801/63651-20180103110610846-122792669.png" alt="" class="medium-zoom-image"></p>
<p>3.4、定义一个对话框组件,要求显示在body标签内</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230301110222925-1581217551.png" alt="" width="892" height="376" loading="lazy"></p>
<div id="blog_post_info_block">3.5、定义一个选项卡组件,3个不同的组件,点击卡片名称时动态切换。</div>
<div><img src="https://img2022.cnblogs.com/blog/63651/202211/63651-20221104113432182-1636032943.png" alt="" class="medium-zoom-image" loading="lazy"></div>
<p>3.6、完成如下示例</p>
<p data-pid="sjwuhqud">举例:举个在实际项目中使用此生命周期的例子,在聊天时的气泡页会遇到弹新的消息气泡场景,此时组件重新渲染了,如果不重新给外层包裹的dom计算滚动高度,会导致dom不停下滑。</p>
<p data-pid="RwkJ-B1v">在线demo:&nbsp;getSnapshotBeforeUpdate例子</p>
<div class="GifPlayer css-1jjs8o8" data-size="normal" data-za-detail-view-path-module="GifItem"><img src="https://pic4.zhimg.com/v2-5494ddcb87d6ebd552185e1e49e5cb73_b.jpg" alt="动图封面" width="486" height="828" class="ztext-gif" data-thumbnail="https://pic4.zhimg.com/v2-5494ddcb87d6ebd552185e1e49e5cb73_b.jpg" data-size="normal"></div>
<p data-pid="Uzgb8JL0">而使用getSnapshotBeforeUpdate可以优化此场景。在getSnapshotBeforeUpdate生命周期中记录外层dom元素的高度<code>perScrollHeight</code>,在componentDidUpdate中重新计算外层dom的scrollTop。此时,页面滚动一段距离不会出现消息跳动的现象&nbsp;优化后的例子</p>
<blockquote data-pid="e-FUpIpq">this.listRef.current.scrollTop = curScrollTop + (this.listRef.current.scrollHeight - perScrollHeight)</blockquote>
<p data-pid="VMxPgBq7">代码:&nbsp;github代码</p>
<p>3.7、定义一个子组件,每隔1秒数字加1,在父组件中定义一个按钮进行显示隐藏子组件,隐藏子组件时要求停止计数,点击显示时从0开始重新计数。</p>
<p>3.8、</p>
<p>使用ant desgin完成一个后台管理界面</p>
<p>搭建一个后台系统&nbsp;实现商品展示&nbsp;以及&nbsp;增删查改&nbsp;并且点击菜单项的时候通过路由跳转页面,路由跳转组件&gt;=3</p>
<p>跳转页面&nbsp;1.表格数据&nbsp;2.Form表单&nbsp;3.跳转一个组件即可&nbsp;</p>
<p>如果增删查改都写在表格页面&nbsp;&nbsp;(另外两个路由随意跳转组件)&nbsp;&nbsp;</p>
<p>主要目的提前预习路由熟悉路由匹配规则&nbsp;少让老师操点心</p>
<p>使用react&nbsp;脚手架&nbsp;</p>
<p>npm&nbsp;i&nbsp;antd</p>
<p>npm&nbsp;i&nbsp;react-router-dom</p>
<p>npm&nbsp;i&nbsp;axios&nbsp;(也可以使用js对静态数据CRUD)</p>
<p>效果图如下(仅供参考)<br><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230303114108840-562027289.jpg" alt=""></p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230303114120958-215047571.jpg" alt=""></p>
<h1>四、视频</h1>
<p>&nbsp;https://www.bilibili.com/video/BV1Vb411D7qa/?share_source=copy_web&amp;vd_source=475a31f3c5d6353a782007cd4c638a8a</p>
<h1>五、源码</h1><br><br>
来源:https://www.cnblogs.com/best/p/17098929.html
頁: [1]
查看完整版本: React学习笔记(二)—— JSX、组件与生命周期