React学习笔记(三)—— 组件高级
<h1>一、列表和keys</h1><h2 id="h2-lists-and-keys-">1.1、Lists and Keys (列表和键)</h2>
<p>首先,我们回顾一下在javascript中怎么去变换列表。</p>
<p>下面了的代码,我们用到了数组函数的map方法来实现数组的每一个值变成它的2倍,同时返回一个新数组,最后打印出了这个数组:</p>
<div class="cnblogs_code">
<pre>const numbers = ;
const doubled </span>= numbers.map(number=>number * 2<span style="color: rgba(0, 0, 0, 1)">);
console.log(doubled);</span></pre>
</div>
<p>最终在控制台的打印结果是:。</p>
<p>在React中,转换一个数组到列表,几乎是相同的。</p>
<p>下面,我们依次通过调用数组的map方法,并返回一个用li标签包含数组值当元素,最后分配它们到listItems数组里面:</p>
<div class="cnblogs_code">
<pre>const numbers = ;
const listItems </span>= numbers.map(number => <li>{number}</li>);</pre>
</div>
<p>现在我们把完整的listItems用一个ul标签包起来,同时render it to the DOM。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">ReactDOM.render(
</span><ul>{listItems}</ul>,
document.getElementById('root'<span style="color: rgba(0, 0, 0, 1)">)
);</span></pre>
</div>
<p>这样就会在页面中显示一个带列表符号的ul列表,项目编号是从1到5。</p>
<h2 id="h2-basic-list-component-">1.2、Basic List Component(一个基础的列表组件)</h2>
<p>我们经常会在一个组件里面输出一个列表elements。</p>
<p>好,我们来写一个组件实现前面同样的功能,这个组件接受一个数字数组,最后返回一个无序列表。</p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> NumberList(props){
const numbers </span>=<span style="color: rgba(0, 0, 0, 1)"> props.numbers;
const listItems </span>= numbers.map(number => <li>{number}</li>);
<span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><ul><span style="color: rgba(0, 0, 0, 1)">
{listItems}
</span></ul>
<span style="color: rgba(0, 0, 0, 1)"> );
};
const numbers </span>= ;
ReactDOM.render(
</span><NumberList numbers={numbers}/>,
document.getElementById('root'<span style="color: rgba(0, 0, 0, 1)">)
);</span></pre>
</div>
<p>如果你运行上面的代码,你将会得到一个警告:你需要为每一个li元素提供一个key属性,这个“Key”字符串属性当你创建一个列表元素的时候必须添加。我们将在接下来讨论一下它为什么这么重要。</p>
<p>让我们在numbers.map()分配一个key属性到列表里面的li标签里来解决上面给出的警告提示问题,代码如下:</p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> NumberList(props){
const numbers </span>=<span style="color: rgba(0, 0, 0, 1)"> props.numbers;
const listItems </span>= numbers.map(number => <li key={number.toString()}>{number}</li>);
<span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><ul>{listItems}</ul>
<span style="color: rgba(0, 0, 0, 1)"> );
};
const numbers </span>= ;
ReactDOM.render(
</span><NumberList numbers={numbers} />,
document.getElementById('root'<span style="color: rgba(0, 0, 0, 1)">)
);</span></pre>
</div>
<h2 id="h2-keys-key-">1.3、Keys(如何设置的key属性值)</h2>
<p>那我们为什么非要需要这个key属性呢?其实这个key属性可以帮助React确定一下那个列表选项改变了、是新增加的、或者被删除了,反正这个key属性就是用来让react跟踪列表在过去的时间发生了什么变化。key属性值应该在数组里面指定,这样就能保证列表元素就能有一个稳定的身份验证值。</p>
<div class="cnblogs_code">
<pre> const numbers = ;
const listItems </span>= numbers.map(number => <li key={number.toString()}>{number}</li>);</pre>
</div>
<p>最好的方法设置key值就是指定一个独一无二的字符串值来把当前列表元素同它的兄弟列表元素分离开来。但是通常情况下,你的后台给你的接口数据中都应该有一个当前数据为一个的”id”值,那么你就可以用这个id值来设置key属性值。代码大概像这样子:</p>
<div class="cnblogs_code">
<pre>const todoItems = todos.map(todo => <li key={todo.id}>{todo.text}</li>);</pre>
</div>
<p>如果你的接口没有这样的一个id值来确定列表元素的key属性,那么最后的办法就是把当前列表的元素的索引值设置为key属性值了。如:</p>
<div class="cnblogs_code">
<pre> const todoItems = todos.map((todo,index) =><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)">只有在todo数据里面没有独一无二的id值的情况才这么做</span>
<li key={index}><span style="color: rgba(0, 0, 0, 1)">
{todo.text}
</span></li>
));</pre>
</div>
<p>我们不推荐你使用索引值来作为key属性值,因为你的列表重新排序的时候,这样会严重使程序变得很慢。如果你愿意的话,可以在这里(in-depth explanation about why keys are necessary)得到更多有关的信息!</p>
<h2 id="h2-extracting-components-with-keys-key-">1.4、Extracting Components with Keys(当我们提取一个组件到另一个组件的时候,需要注意怎么管理key)</h2>
<p>key属性只有在数组数据环境中才有意义,其它地方是没有意义的。</p>
<p>例如,当我们实现一个ListItem组件的时候,这个组件封装了一个li元素,那么我们不应该在li元素上直接设置key属性,因为没有意义,key是用来跟踪数组才有意义,于是我们在NumberList组件使用到ListItem组件的时候,在数组方法里面设置key属性才有意义。好,我们先来看一个错误设置key属性的版本:</p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> ListItem(props){
const value </span>=<span style="color: rgba(0, 0, 0, 1)"> props.value;
</span><span style="color: rgba(0, 0, 255, 1)">return</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)">这里是错误的,因为这里不需要指定key属性</span>
<li key={value.toString()}><span style="color: rgba(0, 0, 0, 1)">
{value}
</span></li>
<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)"> NumberList(props){
const numbers </span>=<span style="color: rgba(0, 0, 0, 1)"> props.numbers;
const listItems </span>= numbers.map(number =><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)">这里也是错误的,因为这里才是真的需要指定key属性值的地方</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">记住一个要点就是:key属性只会在用到有关js处理数组有关的环境中用到</span>
<ListItem value={number} />
<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><ul><span style="color: rgba(0, 0, 0, 1)">
{listItems}
</span></ul>
<span style="color: rgba(0, 0, 0, 1)"> );
};
const numbers </span>= ;
ReactDOM.render(
</span><NumberList numbers={numbers} />,
document.getElementById('root'<span style="color: rgba(0, 0, 0, 1)">)
);</span></pre>
</div>
<p><strong>正确地设置key属性版本的例子在下面:</strong></p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> ListItem(props){
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">正确的,这儿不需要设置key属性</span>
<span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><li>{props.value}</li>
<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)"> NumberList(props){
const numbers </span>=<span style="color: rgba(0, 0, 0, 1)"> props.numbers;
const listItems </span>= numbers.map((number) =><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)">正确,这儿才是真的需要设置key属性的地方</span>
<ListItem key={number.toString()} value={number}/>
<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><ul><span style="color: rgba(0, 0, 0, 1)">
{listItems}
</span></ul>
<span style="color: rgba(0, 0, 0, 1)"> );
};
const numbers </span>= ;
ReactDOM.render(
</span><NumberList numbers={numbers} />,
document.getElementById('root'<span style="color: rgba(0, 0, 0, 1)">)
);</span></pre>
</div>
<p><strong>为了帮你这里理解这一层,我自己的理解就是:并不是渲染到页面中的li标签需要key属性,(同时li标签也是没有关系的,我们在这里之所有用到li标签,只是更形象的说明问题,其实你也可以用div等等其它标签)之所要设置key属性,是React内部用来方便管理一个数组数据,跟踪数组里面的每一个数据!所以一个最好的法则就是,凡是需要调用map方法的时候你使用key属性就对了!</strong></p>
<h2 id="h2-keys-must-only-be-unique-among-siblings-key-">1.5、Keys Must Only Be Unique Among Siblings(key属性值相对于兄弟数据来说是独一无二的,记住只是相对于兄弟数据,其它数据没有关系)</h2>
<p>key属性值只会跟当前(同一个)数组数据之间是独一无二的,而不用是全局独一无二的,例如,有两个数组,那么它们的key就可以是一样的。如:</p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Blog(props){
const sidebar </span>=<span style="color: rgba(0, 0, 0, 1)"> (
</span><ul><span style="color: rgba(0, 0, 0, 1)">
{props.posts.map((post) </span>=><span style="color: rgba(0, 0, 0, 1)"> (
</span><li key={post.id}><span style="color: rgba(0, 0, 0, 1)">
{post.title}
</span></li>
<span style="color: rgba(0, 0, 0, 1)"> ))}
</span></ul>
<span style="color: rgba(0, 0, 0, 1)"> );
const content </span>= props.posts.map((post) =><span style="color: rgba(0, 0, 0, 1)"> (
</span><div key={post.id}>
<h3>{post.title}</h3>
<p>{post.content}</p>
</div>
<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><div><span style="color: rgba(0, 0, 0, 1)">
{sidebar}
</span><hr />
<span style="color: rgba(0, 0, 0, 1)"> {constent}
</span></div>
<span style="color: rgba(0, 0, 0, 1)"> );
};
const posts </span>=<span style="color: rgba(0, 0, 0, 1)"> [
{id: </span>1, title: 'Hello World', content: 'Welcome to learning React!'<span style="color: rgba(0, 0, 0, 1)">},
{id: </span>2, title: 'Installation', content: 'You can install React from npm.'<span style="color: rgba(0, 0, 0, 1)">}
];
ReactDOM.render(
</span><Blog posts={posts} />,
document.getElementById('root'<span style="color: rgba(0, 0, 0, 1)">)
);</span></pre>
</div>
<p>key属性只是给react一个用于跟踪数据的线索而已,并不是传递给组件的,如果你需要个组件设置一样一个属性,那么可以用不同的属性名代替:</p>
<div class="cnblogs_code">
<pre> const content = posts.map((post) =><span style="color: rgba(0, 0, 0, 1)"> (
</span><Post key={post.id} id={post.id} title={post.title} />
));</pre>
</div>
<p>在这个例子中,Post组件可以读id属性,但是不能读key属性。</p>
<h1>二、受控组件与非受控组件</h1>
<h2>2.1、受控组件</h2>
<p>如果一个表单元素的值是由React 来管理的,那么它就是一个受控组件。React 组件渲染表单元素,并在用户和表单元素发生交互时控制表单元素的行为,从而保证组件的 state 成为界面上所有元素状态的唯一来源对于不同的表单元素, React 的控制方式略有不同,下面我们就来看一下三类常用表单元素的控制方式。</p>
<h3>2.1.1、文本框</h3>
<p>文本框包含类型为text 的input 无素和 textarea元素。它们受控的主要原理是,通过表单元素的 value属性设置表单元素的值,通过表单元素的onChange 事件监听值的变化,并将变化同步到React 组件的 state中。</p>
<p>LoginForm.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 LoginForm 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)"> {
username: </span>""<span style="color: rgba(0, 0, 0, 1)">,
password: </span>""<span style="color: rgba(0, 0, 0, 1)">,
};
}
changeHandle </span>= (e) =><span style="color: rgba(0, 0, 0, 1)"> {
let target </span>=<span style="color: rgba(0, 0, 0, 1)"> e.target;
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({ : target.value });
};
handleSubmit </span>= (e) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(
</span>"username:" + <span style="color: rgba(0, 0, 255, 1)">this</span>.state.username + " password:" + <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.password
);
e.preventDefault();
};
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<div>
<h2>用户登录</h2>
<form onSubmit={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleSubmit}>
<fieldset>
<legend>用户信息</legend>
<p>
<label>帐号:</label>
<<span style="color: rgba(0, 0, 0, 1)">input
type</span>="text"<span style="color: rgba(0, 0, 0, 1)">
name</span>="username"<span style="color: rgba(0, 0, 0, 1)">
value</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.username}
onChange</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.changeHandle}
</span>/>
</p>
<p>
<label>密码:</label>
<<span style="color: rgba(0, 0, 0, 1)">input
type</span>="password"<span style="color: rgba(0, 0, 0, 1)">
name</span>="password"<span style="color: rgba(0, 0, 0, 1)">
onChange</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.changeHandle}
value</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.password}
</span>/>
</p>
<p>
<button>登录</button>
</p>
</fieldset>
</form>
</div>
<div><span style="color: rgba(0, 0, 0, 1)">
uid:{</span><span style="color: rgba(0, 0, 255, 1)">this</span>.state.username} - pwd:{<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.password}
</span></div>
</div>
<span style="color: rgba(0, 0, 0, 1)"> );
}
}</span></pre>
</div>
<p>运行结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230303154952268-135538702.png" alt="" width="564" height="553" loading="lazy"></p>
<p>用户名和密码两个表单元素的值是从组件的 state中获取的,当用户更改表单元素的值时,onChange事件会被触发,对应的 handleChange处理函数会把变化同步到组件的 state,新的 state又会触发表单元素重新渲染,从而实现对表单元素状态的控制。</p>
<p>这个例子还包含一个处理多个表单元素的技巧:通过为两个 input元素分别指定name属性,使用同一个函数 handleChange处理元素值的变化,在处理函数中根据元素的name属性区分事件的来源。这样的写法显然比为每一个 input元素指定一个处理函数简洁得多。textarea的使用方式和input几乎一致,这里不再赘述。</p>
<h3>2.1.2、高阶函数</h3>
<div>
<div class="section">
<p class="paragraph text-align-type-left"><span data-font-family="Monaco">高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。</span></p>
<p class="paragraph text-align-type-left"><span data-font-family="Monaco">1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。</span></p>
<p class="paragraph text-align-type-left"><span data-font-family="Monaco">常见的高阶函数有:Promise、setTimeout、arr.map()等等</span></p>
<p class="paragraph text-align-type-left"><span data-font-family="Monaco">2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。</span></p>
<div>
<div class="section">
<p class="paragraph text-align-type-left">常见的高阶函数有:Function.bind</p>
<p class="paragraph text-align-type-left">自定义高阶函数</p>
<div class="cnblogs_code">
<pre><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
<span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
*
*高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。
1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
常见的高阶函数有:Promise、setTimeout、arr.map()等等
</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)">*
* cal 是一个函数
* n1数字,参数2
* n2数字,参数3
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> superCal(cal, n1, n2) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (cal && n1 > 0 && n2 > 0<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><span style="color: rgba(0, 0, 255, 1)">return</span> cal(n1 * 2, n2 * 2<span style="color: rgba(0, 0, 0, 1)">);
};
}
}
let result </span>= superCal((a, b) => a + b, 1, 2<span style="color: rgba(0, 0, 0, 1)">)();
console.log(result);
</span></script>
</body>
</html></pre>
</div>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230314103621752-1375338738.png" alt="" width="417" height="127" loading="lazy"></p>
<p class="paragraph text-align-type-left"><span data-font-family="Monaco">示例1:</span></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 LoginForm extends Component {
state </span>=<span style="color: rgba(0, 0, 0, 1)"> {
uid: </span>"abc"<span style="color: rgba(0, 0, 0, 1)">,
pwd: </span>"123"<span style="color: rgba(0, 0, 0, 1)">,
code: </span>"5678"<span style="color: rgba(0, 0, 0, 1)">,
};
handleSubmit </span>= (e) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(`uid:${</span><span style="color: rgba(0, 0, 255, 1)">this</span>.state.uid} - pwd:${<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.pwd}`);
</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)"> e.preventDefault();
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">高阶函数+闭包</span>
formChange = (propKey) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> (e) =><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({ : e.target.value });
};
};
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<h2>用户登录</h2>
<form onSubmit={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleSubmit}>
<fieldset>
<legend>用户信息</legend>
<p>
<label htmlFor="uid">帐号:</label>
<<span style="color: rgba(0, 0, 0, 1)">input
type</span>="text"<span style="color: rgba(0, 0, 0, 1)">
name</span>="uid"<span style="color: rgba(0, 0, 0, 1)">
id</span>="uid"<span style="color: rgba(0, 0, 0, 1)">
value</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.uid}
onChange</span>={<span style="color: rgba(0, 0, 255, 1)">this</span>.formChange("uid"<span style="color: rgba(0, 0, 0, 1)">)}
</span>></input>
</p>
<p>
<label htmlFor="pwd">密码:</label>
<<span style="color: rgba(0, 0, 0, 1)">input
type</span>="password"<span style="color: rgba(0, 0, 0, 1)">
name</span>="pwd"<span style="color: rgba(0, 0, 0, 1)">
id</span>="pwd"<span style="color: rgba(0, 0, 0, 1)">
value</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.pwd}
onChange</span>={<span style="color: rgba(0, 0, 255, 1)">this</span>.formChange("pwd"<span style="color: rgba(0, 0, 0, 1)">)}
</span>></input>
</p>
<p>
<label htmlFor="pwd">验证:</label>
<<span style="color: rgba(0, 0, 0, 1)">input
type</span>="text"<span style="color: rgba(0, 0, 0, 1)">
name</span>="code"<span style="color: rgba(0, 0, 0, 1)">
id</span>="code"<span style="color: rgba(0, 0, 0, 1)">
value</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.code}
onChange</span>={<span style="color: rgba(0, 0, 255, 1)">this</span>.formChange("code"<span style="color: rgba(0, 0, 0, 1)">)}
</span>></input>
</p>
<p>
<button>登录</button>
</p>
</fieldset>
{`uid:${<span style="color: rgba(0, 0, 255, 1)">this</span>.state.uid} - pwd:${<span style="color: rgba(0, 0, 255, 1)">this</span>.state.pwd} - code:${<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.code}`}
</span></form>
</div>
<span style="color: rgba(0, 0, 0, 1)"> );
}
}</span></pre>
</div>
<p>运行效果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230314100148220-1810302132.png" alt="" loading="lazy"></p>
</div>
</div>
</div>
</div>
<h3>2.1.3、柯里化</h3>
<div>
<div class="section">
<p class="paragraph text-align-type-left"><span data-font-family="Monaco">函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。</span></p>
<div class="cnblogs_code">
<pre><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
<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, 128, 0, 1)">*
* cal 是一个函数
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> superCal(cal) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (cal) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> (n1) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> (n2) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> cal(n1 * 2, n2 * 2<span style="color: rgba(0, 0, 0, 1)">);
};
};
}
}
let result </span>= superCal((a, b) => a + b)(1)(2<span style="color: rgba(0, 0, 0, 1)">);
console.log(result);
</span></script>
</body>
</html></pre>
</div>
<p>结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230314104223575-226563419.png" alt="" loading="lazy"></p>
</div>
</div>
<h3>2.1.4、列表</h3>
<p>列表select元素是最复杂的表单元素,它可以用来创建一个下拉列表:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">select</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">option </span><span style="color: rgba(255, 0, 0, 1)">value</span><span style="color: rgba(0, 0, 255, 1)">="react"</span><span style="color: rgba(0, 0, 255, 1)">></span>React<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">option</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">option </span><span style="color: rgba(255, 0, 0, 1)">value</span><span style="color: rgba(0, 0, 255, 1)">="redux"</span><span style="color: rgba(0, 0, 255, 1)">></span>Redux<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">option</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">option </span><span style="color: rgba(255, 0, 0, 1)">selected value</span><span style="color: rgba(0, 0, 255, 1)">="mobx"</span><span style="color: rgba(0, 0, 255, 1)">></span>MobX<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)"> option</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">select</span><span style="color: rgba(0, 0, 255, 1)">></span></pre>
</div>
<p>通过指定selected属性可以定义哪一个选项(option)处于选中状态,所以上面的例子中,Mobx这一选项是列表的初始值,处于选中状态。在React中,对select的处理方式有所不同,它通过在select上定义 value属性来决定哪一个option元素处于选中状态。这样,对select的控制只需要在select 这一个元素上修改即可,而不需要关注 option元素。下面是一个例子:</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 SelectForm 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)"> {
value: </span>"mobx"<span style="color: rgba(0, 0, 0, 1)">,
};
}
changeHandle </span>= (e) =><span style="color: rgba(0, 0, 0, 1)"> {
let target </span>=<span style="color: rgba(0, 0, 0, 1)"> e.target;
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({ value: target.value });
};
handleSubmit </span>= (e) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>"value:" + <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.value);
e.preventDefault();
};
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<div>
<h2>使用技术</h2>
<form onSubmit={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleSubmit}>
<fieldset>
<legend>技术信息</legend>
<p>
<label>技术列表:</label>
<select value={<span style="color: rgba(0, 0, 255, 1)">this</span>.state.value} onChange={<span style="color: rgba(0, 0, 255, 1)">this</span>.changeHandle}>
<option value="react">React</option>
<option value="redux">Redux</option>
<option value="mobx">MobX</option>
</select>
</p>
<p>
<button>提交</button>
</p>
</fieldset>
</form>
</div>
<div>value:{<span style="color: rgba(0, 0, 255, 1)">this</span>.state.value}</div>
</div>
<span style="color: rgba(0, 0, 0, 1)"> );
}
}</span></pre>
</div>
<p>运行</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230303160406844-1603821358.png" alt="" loading="lazy"></p>
<p> </p>
<h3> 2.1.5、复选框与单选框</h3>
<p>复选框是类型为checkbox的input元素,单选框是类型为 radio的input元素,它们的受控方式不同于类型为text 的 input元素。通常,复选框和单选框的值是不变的,需要改变的是它们的checked 状态,因此React 控制的属性不再是value属性,而是checked属性。例如:</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 SelectForm 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)"> {
react: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
redux: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
mobx: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
};
}
handleChange </span>= (e) =><span style="color: rgba(0, 0, 0, 1)"> {
let target </span>=<span style="color: rgba(0, 0, 0, 1)"> e.target;
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({ : target.checked });
};
handleSubmit </span>= (e) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(JSON.stringify(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state));
e.preventDefault();
};
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<div>
<h2>使用技术</h2>
<form onSubmit={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleSubmit}>
<fieldset>
<legend>技术信息</legend>
<p>
<label>技术列表:</label>
<<span style="color: rgba(0, 0, 0, 1)">input
type</span>="checkbox"<span style="color: rgba(0, 0, 0, 1)">
name</span>="react"<span style="color: rgba(0, 0, 0, 1)">
value</span>="react"<span style="color: rgba(0, 0, 0, 1)">
checked</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.react}
onChange</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.handleChange}
</span>/>
<span style="color: rgba(0, 0, 0, 1)"> React
</span><<span style="color: rgba(0, 0, 0, 1)">input
type</span>="checkbox"<span style="color: rgba(0, 0, 0, 1)">
name</span>="redux"<span style="color: rgba(0, 0, 0, 1)">
value</span>="redux"<span style="color: rgba(0, 0, 0, 1)">
checked</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.redux}
onChange</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.handleChange}
</span>/>
<span style="color: rgba(0, 0, 0, 1)"> Redux
</span><<span style="color: rgba(0, 0, 0, 1)">input
type</span>="checkbox"<span style="color: rgba(0, 0, 0, 1)">
name</span>="mobx"<span style="color: rgba(0, 0, 0, 1)">
value</span>="mobx"<span style="color: rgba(0, 0, 0, 1)">
checked</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.mobx}
onChange</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.handleChange}
</span>/>
<span style="color: rgba(0, 0, 0, 1)"> Mobx
</span></p>
<p>
<button>提交</button>
</p>
</fieldset>
</form>
</div>
<div>{JSON.stringify(<span style="color: rgba(0, 0, 255, 1)">this</span>.state)}</div>
</div>
<span style="color: rgba(0, 0, 0, 1)"> );
}
}</span></pre>
</div>
<p>运行结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230303161639572-366096672.png" alt="" width="730" height="536" loading="lazy"></p>
<h3>2.1.6、升级后的BBS</h3>
<p>让每个帖子支持编辑功能:</p>
<p>PostItem.js</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;
import like from </span>"./like.png"<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 PostItem 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)"> {
editing: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
post: props.post,
};
}
static getDerivedStateFromProps(props, state) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (props.post.vote !==<span style="color: rgba(0, 0, 0, 1)"> state.post.vote) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> { post: props.post };
}
</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)">;
}
handleVote </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.props.onVote(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props.post.id);
};
handleTitleChange </span>= (e) =><span style="color: rgba(0, 0, 0, 1)"> {
const newPost </span>= { ...<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.post, title: e.target.value };
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({ post: newPost });
};
handleEditPost </span>= (e) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.editing) {
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props.onSave({
...</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.post,
date: </span><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><span style="color: rgba(0, 0, 0, 1)">.setState({
editing: </span>!<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.editing,
});
};
render() {
const { post } </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state;
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><li className="item">
<div className="title"><span style="color: rgba(0, 0, 0, 1)">
{</span><span style="color: rgba(0, 0, 255, 1)">this</span>.state.editing ?<span style="color: rgba(0, 0, 0, 1)"> (
</span><form>
<<span style="color: rgba(0, 0, 0, 1)">textarea
value</span>=<span style="color: rgba(0, 0, 0, 1)">{post.title}
onChange</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.handleTitleChange}
cols</span>="30"<span style="color: rgba(0, 0, 0, 1)">
rows</span>="3"
/>
</form>
<span style="color: rgba(0, 0, 0, 1)"> ) : (
post.title
)}
</span></div>
<div><span style="color: rgba(0, 0, 0, 1)">
创建人:</span><span>{post.author}</span>
</div>
<div><span style="color: rgba(0, 0, 0, 1)">
创建时间:</span><span>{post.date}</span>
</div>
<div>
<img src={like} alt="点赞" onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleVote}></img>
<span style="color: rgba(0, 0, 0, 1)"> {post.vote}
</span></div>
<div>
<button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleEditPost}><span style="color: rgba(0, 0, 0, 1)">
{</span><span style="color: rgba(0, 0, 255, 1)">this</span>.state.editing ? "保存" : "编辑"<span style="color: rgba(0, 0, 0, 1)">}
</span></button>
</div>
</li>
<span style="color: rgba(0, 0, 0, 1)"> );
}
}</span></pre>
</div>
<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)">;
</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, 0, 1)">
export class PostList extends Component {
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, 0, 255, 1)">this</span>.state =<span style="color: rgba(0, 0, 0, 1)"> {
posts: [], </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)"> };
</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, 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)">将voteHandle函数中的this指向组件对象</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.voteHandle = <span style="color: rgba(0, 0, 255, 1)">this</span>.voteHandle.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>1001<span style="color: rgba(0, 0, 0, 1)">,
title: </span>"百度萝卜快跑超百台无人车落地武汉,订单量突破200"<span style="color: rgba(0, 0, 0, 1)">,
author: </span>"小明"<span style="color: rgba(0, 0, 0, 1)">,
date: </span>"2023-03-01 12:12:18"<span style="color: rgba(0, 0, 0, 1)">,
vote: </span>0<span style="color: rgba(0, 0, 0, 1)">,
},
{
id: </span>1002<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-15 23:15:26"<span style="color: rgba(0, 0, 0, 1)">,
vote: </span>0<span style="color: rgba(0, 0, 0, 1)">,
},
{
id: </span>1003<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-11-26 18:17:44"<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)">componentDidMount() {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">模拟后台AJAX加载</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.timer = setTimeout(() =><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)">当组件将被卸载时</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);
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">根据id点赞</span>
<span style="color: rgba(0, 0, 0, 1)">voteHandle(id) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果当前帖子的id是我们要投票的帖子,则生成一个新对象,并更新投票数</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果不是要找的帖子,则直接返回</span>
let newPosts = <span style="color: rgba(0, 0, 255, 1)">this</span>.state.posts.map((item) =><span style="color: rgba(0, 0, 0, 1)">
item.id </span>=== id ? { ...item, vote: item.vote + 1<span style="color: rgba(0, 0, 0, 1)"> } : item
);
</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, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({ posts: newPosts });
}
saveHandle </span>= (newPost) =><span style="color: rgba(0, 0, 0, 1)"> {
const newPosts </span>= <span style="color: rgba(0, 0, 255, 1)">this</span>.state.posts.map((item) =><span style="color: rgba(0, 0, 0, 1)">
item.id </span>=== newPost.id ?<span style="color: rgba(0, 0, 0, 1)"> newPost : item
);
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({ posts: newPosts });
};
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<h2>帖子列表:</h2>
{<span style="color: rgba(0, 0, 255, 1)">this</span>.state.posts.map((item) =><span style="color: rgba(0, 0, 0, 1)"> (
</span><<span style="color: rgba(0, 0, 0, 1)">PostItem
key</span>=<span style="color: rgba(0, 0, 0, 1)">{item.id}
post</span>=<span style="color: rgba(0, 0, 0, 1)">{item}
posts</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.posts}
onVote</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.voteHandle}
onSave</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.saveHandle}
</span>/>
<span style="color: rgba(0, 0, 0, 1)"> ))}
</span></div>
<span style="color: rgba(0, 0, 0, 1)"> );
}
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> PostList;</pre>
</div>
<p>运行结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230307160448245-2141796390.png" alt="" width="733" height="731" loading="lazy"></p>
<p>参考代码二:</p>
<p>PostItem.js</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;
import like from </span>"./images/dz.png"<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, 128, 0, 1)">*/</span><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 PostItem 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)"> {
editing: </span><span style="color: rgba(0, 0, 255, 1)">false</span>, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">编辑中</span>
post: props.post, <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)"> };
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">当props变化时更新本组件的状态</span>
<span style="color: rgba(0, 0, 0, 1)">static getDerivedStateFromProps(props, state) {
console.log(props, state);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (props.post.vote !==<span style="color: rgba(0, 0, 0, 1)"> state.post.vote) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> { post: props.post };
}
</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>
onVote = (id) =><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)">.props.onVote(id);
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">保存事件</span>
saveHandle = (e) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.editing) {
let newPost </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)">.state.post,
date: </span><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><span style="color: rgba(0, 0, 0, 1)">.props.onSave(newPost);
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.setState({ editing: !<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.editing, post: newPost });
}
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({
editing: </span>!<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.editing,
});
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">标题修改事件</span>
titleChange = (e) =><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({
post: { ...</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.post, title: e.target.value },
});
};
render() {
let post </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.post;
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><li>
<div><span style="color: rgba(0, 0, 0, 1)">
{</span><span style="color: rgba(0, 0, 255, 1)">this</span>.state.editing ?<span style="color: rgba(0, 0, 0, 1)"> (
</span><form>
<<span style="color: rgba(0, 0, 0, 1)">textarea
value</span>=<span style="color: rgba(0, 0, 0, 1)">{post.title}
cols</span>="40"<span style="color: rgba(0, 0, 0, 1)">
rows</span>="3"<span style="color: rgba(0, 0, 0, 1)">
onChange</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.titleChange}
</span>></textarea>
</form>
<span style="color: rgba(0, 0, 0, 1)"> ) : (
post.title
)}
</span></div>
<div><span style="color: rgba(0, 0, 0, 1)">
创建人:</span><span>{post.author}</span>
</div>
<div><span style="color: rgba(0, 0, 0, 1)">
创建时间:</span><span>{post.date}</span>
</div>
<div>
<<span style="color: rgba(0, 0, 0, 1)">img
src</span>=<span style="color: rgba(0, 0, 0, 1)">{like}
alt</span>="点赞"<span style="color: rgba(0, 0, 0, 1)">
onClick</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)">.onVote(post.id);
}}
</span>/>
<span style="color: rgba(0, 0, 0, 1)"> {post.vote}
</span></div>
<p>
<button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.saveHandle}><span style="color: rgba(0, 0, 0, 1)">
{</span><span style="color: rgba(0, 0, 255, 1)">this</span>.state.editing ? "保存" : "编辑"<span style="color: rgba(0, 0, 0, 1)">}
</span></button>
</p>
</li>
<span style="color: rgba(0, 0, 0, 1)"> );
}
}</span></pre>
</div>
<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)">;
</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, 0, 1)">
export class PostList extends Component {
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, 0, 255, 1)">this</span>.state =<span style="color: rgba(0, 0, 0, 1)"> {
posts: [], </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)"> };
</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, 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)">将voteHandle函数中的this指向组件对象</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.voteHandle = <span style="color: rgba(0, 0, 255, 1)">this</span>.voteHandle.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>1001<span style="color: rgba(0, 0, 0, 1)">,
title: </span>"百度萝卜快跑超百台无人车落地武汉,订单量突破200"<span style="color: rgba(0, 0, 0, 1)">,
author: </span>"小明"<span style="color: rgba(0, 0, 0, 1)">,
date: </span>"2023-03-01 12:12:18"<span style="color: rgba(0, 0, 0, 1)">,
vote: </span>0<span style="color: rgba(0, 0, 0, 1)">,
},
{
id: </span>1002<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-15 23:15:26"<span style="color: rgba(0, 0, 0, 1)">,
vote: </span>0<span style="color: rgba(0, 0, 0, 1)">,
},
{
id: </span>1003<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-11-26 18:17:44"<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)">componentDidMount() {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">模拟后台AJAX加载</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.timer = setTimeout(() =><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)">当组件将被卸载时</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);
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">根据id点赞</span>
<span style="color: rgba(0, 0, 0, 1)">voteHandle(id) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果当前帖子的id是我们要投票的帖子,则生成一个新对象,并更新投票数</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果不是要找的帖子,则直接返回</span>
let newPosts = <span style="color: rgba(0, 0, 255, 1)">this</span>.state.posts.map((item) =><span style="color: rgba(0, 0, 0, 1)">
item.id </span>=== id ? { ...item, vote: item.vote + 1<span style="color: rgba(0, 0, 0, 1)"> } : item
);
console.log(newPosts);
</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, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({ posts: newPosts });
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">保存事件</span>
saveHandle = (newPost) =><span style="color: rgba(0, 0, 0, 1)"> {
const newPosts </span>= <span style="color: rgba(0, 0, 255, 1)">this</span>.state.posts.map((item) =><span style="color: rgba(0, 0, 0, 1)">
item.id </span>=== newPost.id ?<span style="color: rgba(0, 0, 0, 1)"> newPost : item
);
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({ posts: newPosts });
console.log(newPosts);
};
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<h2>帖子列表:</h2>
{<span style="color: rgba(0, 0, 255, 1)">this</span>.state.posts.map((item) =><span style="color: rgba(0, 0, 0, 1)"> (
</span><<span style="color: rgba(0, 0, 0, 1)">PostItem
key</span>=<span style="color: rgba(0, 0, 0, 1)">{item.id}
post</span>=<span style="color: rgba(0, 0, 0, 1)">{item}
onVote</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.voteHandle}
onSave</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.saveHandle}
</span>/>
<span style="color: rgba(0, 0, 0, 1)"> ))}
</span></div>
<span style="color: rgba(0, 0, 0, 1)"> );
}
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> PostList;</pre>
</div>
<p>效果</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230315152131215-1108356996.png" alt="" width="498" height="330" loading="lazy"></p>
<h2>2.2、非受控组件</h2>
<h3>2.2.1、使用非受控组件</h3>
<p style="margin: 0; padding: 0; box-sizing: inherit; font-size: 24px; line-height: 1.7; max-width: 42em; color: rgba(107, 107, 107, 1); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif">在大多数情况下,我们推荐使用 受控组件 来处理表单数据。在一个受控组件中,表单数据是由 React 组件来管理的。另一种替代方案是使用非受控组件,这时表单数据将交由 DOM 节点来处理。</p>
<p style="margin: 30px 0 0; padding: 0; box-sizing: inherit; font-size: 17px; line-height: 1.7; max-width: 42em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif">要编写一个非受控组件,而不是为每个状态更新都编写数据处理函数,你可以 使用 ref 来从 DOM 节点中获取表单数据。</p>
<p style="margin: 30px 0 0; padding: 0; box-sizing: inherit; font-size: 17px; line-height: 1.7; max-width: 42em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif">例如,下面的代码使用非受控组件接受一个表单的值:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">class NameForm extends React.Component {
constructor(props) {
super(props);
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.handleSubmit = <span style="color: rgba(0, 0, 255, 1)">this</span>.handleSubmit.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)">this</span>.input =<span style="color: rgba(0, 0, 0, 1)"> React.createRef();
}
handleSubmit(event) {
alert(</span>'A name was submitted: ' + <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.input.current.value);
event.preventDefault();
}
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><form onSubmit={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleSubmit}>
<label><span style="color: rgba(0, 0, 0, 1)">
Name:
</span><input type="text" ref={<span style="color: rgba(0, 0, 255, 1)">this</span>.input} />
</label>
<input type="submit" value="Submit" />
</form>
<span style="color: rgba(0, 0, 0, 1)"> );
}
}</span></pre>
</div>
<p><span style="font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 17px">因为非受控组件将真实数据储存在 DOM 节点中,所以在使用非受控组件时,有时候反而更容易同时集成 React 和非 React 代码。如果你不介意代码美观性,并且希望快速编写代码,使用非受控组件往往可以减少你的代码量。否则,你应该使用受控组件。</span></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 SearchForm extends Component {
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, 0, 255, 1)">this</span>.keyword =<span style="color: rgba(0, 0, 0, 1)"> React.createRef();
}
submitHandle </span>= (e) =><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)">.keyword.current.value);
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.keyword.current.style.backgroundColor = "lightgreen"<span style="color: rgba(0, 0, 0, 1)">;
e.preventDefault();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">return false;</span>
<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><div>
<form onSubmit={<span style="color: rgba(0, 0, 255, 1)">this</span>.submitHandle}>
<p>
<label htmlFor="keyword">关键字:</label>
<input type="text" id="keyword" name="keyword" ref={<span style="color: rgba(0, 0, 255, 1)">this</span>.keyword} />
<button>搜索</button>
</p>
</form>
</div>
<span style="color: rgba(0, 0, 0, 1)"> );
}
}</span></pre>
</div>
<p> </p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230316094919130-1698347103.png" alt="" loading="lazy"></p>
<p> </p>
<h3><span style="font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 17px">2.2.2、默认值</span></h3>
<p style="margin: 30px 0 0; padding: 0; box-sizing: inherit; font-size: 17px; line-height: 1.7; max-width: 42em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif">在 React 渲染生命周期时,表单元素上的 <code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2); color: rgba(26, 26, 26, 1)">value</code> 将会覆盖 DOM 节点中的值。在非受控组件中,你经常希望 React 能赋予组件一个初始值,但是不去控制后续的更新。 在这种情况下, 你可以指定一个 <code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2); color: rgba(26, 26, 26, 1)">defaultValue</code> 属性,而不是 <code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2); color: rgba(26, 26, 26, 1)">value</code>。在一个组件已经挂载之后去更新 <code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2); color: rgba(26, 26, 26, 1)">defaultValue</code> 属性的值,不会造成 DOM 上值的任何更新。</p>
<p style="margin: 30px 0 0; padding: 0; box-sizing: inherit; font-size: 17px; line-height: 1.7; max-width: 42em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif">如果直接给value赋值则会提示错误:</p>
<p style="margin: 30px 0 0; padding: 0; box-sizing: inherit; font-size: 17px; line-height: 1.7; max-width: 42em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif"><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230316095300172-898843138.png" alt="" loading="lazy"></p>
<p> </p>
<p>使用defaultValue</p>
<div class="cnblogs_code">
<pre><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><form onSubmit={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleSubmit}>
<label><span style="color: rgba(0, 0, 0, 1)">
Name:
</span><<span style="color: rgba(0, 0, 0, 1)">input
defaultValue</span>="Bob"<span style="color: rgba(0, 0, 0, 1)">
type</span>="text"<span style="color: rgba(0, 0, 0, 1)">
ref</span>={<span style="color: rgba(0, 0, 255, 1)">this</span>.input} />
</label>
<input type="submit" value="Submit" />
</form>
<span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<p><span style="font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 17px">同样,</span><code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2); color: rgba(26, 26, 26, 1)"><input type="checkbox"></code><span style="font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 17px"> 和 </span><code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2); color: rgba(26, 26, 26, 1)"><input type="radio"></code><span style="font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 17px"> 支持 </span><code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2); color: rgba(26, 26, 26, 1)">defaultChecked</code><span style="font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 17px">,</span><code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2); color: rgba(26, 26, 26, 1)"><select></code><span style="font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 17px"> 和 </span><code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2); color: rgba(26, 26, 26, 1)"><textarea></code><span style="font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 17px"> 支持 </span><code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2); color: rgba(26, 26, 26, 1)">defaultValue</code><span style="font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 17px">。</span></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 SearchForm extends Component {
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, 0, 255, 1)">this</span>.keyword =<span style="color: rgba(0, 0, 0, 1)"> React.createRef();
}
submitHandle </span>= (e) =><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)">.keyword.current.value);
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.keyword.current.style.backgroundColor = "lightgreen"<span style="color: rgba(0, 0, 0, 1)">;
e.preventDefault();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">return false;</span>
<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><div>
<form onSubmit={<span style="color: rgba(0, 0, 255, 1)">this</span>.submitHandle}>
<p>
<label htmlFor="keyword">关键字:</label>
<<span style="color: rgba(0, 0, 0, 1)">input
defaultValue</span>="请输入搜索关键字"<span style="color: rgba(0, 0, 0, 1)">
type</span>="text"<span style="color: rgba(0, 0, 0, 1)">
id</span>="keyword"<span style="color: rgba(0, 0, 0, 1)">
name</span>="keyword"<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)">.keyword}
</span>/>
<button>搜索</button>
</p>
<p><span style="color: rgba(0, 0, 0, 1)">
同样,
</span><input type="checkbox" defaultChecked={<span style="color: rgba(0, 0, 255, 1)">true</span>} /> 和{" "}
<input type="radio" defaultChecked={<span style="color: rgba(0, 0, 255, 1)">false</span>} /> 支持 defaultChecked,
<select defaultValue="usa">
<option value="zh-cn">中国</option>
<option value="usa">美国</option>
</select>{" "}
和 <textarea defaultValue="请输入多行文本" cols="40" rows="3" />{" "}
<span style="color: rgba(0, 0, 0, 1)"> 支持 defaultValue。
</span></p>
</form>
</div>
<span style="color: rgba(0, 0, 0, 1)"> );
}
}</span></pre>
</div>
<p> </p>
<p>结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230316100051403-2135231711.png" alt="" loading="lazy"></p>
<p> </p>
<h3>2.2.3、文件输入</h3>
<p style="margin: 30px 0 0; padding: 0; box-sizing: inherit; font-size: 17px; line-height: 1.7; max-width: 42em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif">在 HTML 中,<code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2); color: rgba(26, 26, 26, 1)"><input type="file"></code> 可以让用户选择一个或多个文件上传到服务器,或者通过使用 File API 进行操作。</p>
<div class="gatsby-highlight" style="margin: 25px -30px; padding: 0 15px; box-sizing: inherit; background: rgba(40, 44, 52, 1); color: rgba(255, 255, 255, 1); border-radius: 10px; overflow: auto; tab-size: 1.5em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: medium" data-language="html">
<pre class="gatsby-code-html"><code class="gatsby-code-html" style="margin: 0; padding: 0; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 1em"><span class="token tag" style="margin: 0; padding: 0; box-sizing: inherit; color: rgba(252, 146, 158, 1)"><span class="token tag" style="margin: 0; padding: 0; box-sizing: inherit"><span class="token punctuation" style="margin: 0; padding: 0; box-sizing: inherit; color: rgba(136, 198, 190, 1)"><</span>input</span> <span class="token attr-name" style="margin: 0; padding: 0; box-sizing: inherit; color: rgba(197, 165, 197, 1)">type</span><span class="token attr-value" style="margin: 0; padding: 0; box-sizing: inherit; color: rgba(141, 200, 145, 1)"><span class="token punctuation attr-equals" style="margin: 0; padding: 0; box-sizing: inherit; color: rgba(136, 198, 190, 1)">=</span><span class="token punctuation" style="margin: 0; padding: 0; box-sizing: inherit; color: rgba(136, 198, 190, 1)">"</span>file<span class="token punctuation" style="margin: 0; padding: 0; box-sizing: inherit; color: rgba(136, 198, 190, 1)">"</span></span> <span class="token punctuation" style="margin: 0; padding: 0; box-sizing: inherit; color: rgba(136, 198, 190, 1)">/></span></span></code></pre>
</div>
<p style="margin: 30px 0 0; padding: 0; box-sizing: inherit; font-size: 17px; line-height: 1.7; max-width: 42em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif">在 React 中,<code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2); color: rgba(26, 26, 26, 1)"><input type="file" /></code> 始终是一个非受控组件,因为它的值只能由用户设置,而不能通过代码控制。</p>
<p style="margin: 30px 0 0; padding: 0; box-sizing: inherit; font-size: 17px; line-height: 1.7; max-width: 42em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif">您应该使用 File API 与文件进行交互。下面的例子显示了如何创建一个 DOM 节点的 ref 从而在提交表单时获取文件的信息。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">class FileInput extends React.Component {
constructor(props) {
super(props);
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.handleSubmit = <span style="color: rgba(0, 0, 255, 1)">this</span>.handleSubmit.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)">this</span>.fileInput =<span style="color: rgba(0, 0, 0, 1)"> React.createRef();
}
handleSubmit(event) {
event.preventDefault();
alert(
`Selected file </span>- ${<span style="color: rgba(0, 0, 255, 1)">this</span>.fileInput.current.files.name}`
);
}
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><form onSubmit={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleSubmit}>
<label><span style="color: rgba(0, 0, 0, 1)">
Upload file:
</span><input type="file" ref={<span style="color: rgba(0, 0, 255, 1)">this</span>.fileInput} />
</label>
<br />
<button type="submit">Submit</button>
</form>
<span style="color: rgba(0, 0, 0, 1)"> );
}
}
const root </span>=<span style="color: rgba(0, 0, 0, 1)"> ReactDOM.createRoot(
document.getElementById(</span>'root'<span style="color: rgba(0, 0, 0, 1)">)
);
root.render(</span><FileInput />);</pre>
</div>
<h1>三、React新特性</h1>
<h2>3.1、render新的返回类型</h2>
<p>React16之前render方法必须返回单个元素,现在render可以返回多种不同的元素:</p>
<p style="margin: 30px 0 0; padding: 0; box-sizing: inherit; font-size: 17px; line-height: 1.7; max-width: 42em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif"><code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2); color: rgba(26, 26, 26, 1)">render()</code> 方法是 class 组件中唯一必须实现的方法。</p>
<p style="margin: 30px 0 0; padding: 0; box-sizing: inherit; font-size: 17px; line-height: 1.7; max-width: 42em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif">当 <code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2); color: rgba(26, 26, 26, 1)">render</code> 被调用时,它会检查 <code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2); color: rgba(26, 26, 26, 1)">this.props</code> 和 <code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2); color: rgba(26, 26, 26, 1)">this.state</code> 的变化并返回以下类型之一:</p>
<ul style="margin-top: 20px; margin-right: 0; margin-bottom: 0; padding: 0 0 0 20px; box-sizing: inherit; list-style-position: initial; list-style-image: initial; font-size: medium; color: rgba(26, 26, 26, 1); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif">
<li style="margin-top: 10px; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; box-sizing: inherit"><span style="margin: 0; padding: 0; box-sizing: inherit; font-weight: bolder">React 元素</span>。通常通过 JSX 创建。例如,<code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2)"><div /></code> 会被 React 渲染为 DOM 节点,<code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2)"><MyComponent /></code> 会被 React 渲染为自定义组件,无论是 <code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2)"><div /></code> 还是 <code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2)"><MyComponent /></code> 均为 React 元素。</li>
<li style="margin-top: 10px; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; box-sizing: inherit"><span style="margin: 0; padding: 0; box-sizing: inherit; font-weight: bolder">数组或 fragments</span>。 使得 render 方法可以返回多个元素。欲了解更多详细信息,请参阅 fragments 文档。</li>
<li style="margin-top: 10px; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; box-sizing: inherit"><span style="margin: 0; padding: 0; box-sizing: inherit; font-weight: bolder">Portals</span>。可以渲染子节点到不同的 DOM 子树中。欲了解更多详细信息,请参阅有关 portals 的文档。</li>
<li style="margin-top: 10px; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; box-sizing: inherit"><span style="margin: 0; padding: 0; box-sizing: inherit; font-weight: bolder">字符串或数值类型</span>。它们在 DOM 中会被渲染为文本节点。</li>
<li style="margin-top: 10px; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; box-sizing: inherit"><span style="margin: 0; padding: 0; box-sizing: inherit; font-weight: bolder">布尔类型或 <code class="gatsby-code-text" style="margin: 0; padding: 0; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 1em">null</code></span>。什么都不渲染。(主要用于支持返回 <code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2)">test && <Child /></code> 的模式,其中 test 为布尔类型。)</li>
</ul>
<p style="margin: 30px 0 0; padding: 0; box-sizing: inherit; font-size: 17px; line-height: 1.7; max-width: 42em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif"><code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2); color: rgba(26, 26, 26, 1)">render()</code> 函数应该为纯函数,这意味着在不修改组件 state 的情况下,每次调用时都返回相同的结果,并且它不会直接与浏览器交互。</p>
<p style="margin: 30px 0 0; padding: 0; box-sizing: inherit; font-size: 17px; line-height: 1.7; max-width: 42em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif">如需与浏览器进行交互,请在 <code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2); color: rgba(26, 26, 26, 1)">componentDidMount()</code> 或其他生命周期方法中执行你的操作。保持 <code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2); color: rgba(26, 26, 26, 1)">render()</code> 为纯函数,可以使组件更容易思考。</p>
<blockquote style="margin: 20px -30px 30px; padding: 20px 45px 20px 26px; box-sizing: inherit; background-color: rgba(255, 229, 100, 0.3); border-left-width: 9px; border-left-color: rgba(255, 229, 100, 1); color: rgba(0, 0, 0, 1); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: medium">
<p style="margin: 0; padding: 0; box-sizing: inherit; font-size: 17px; line-height: 1.7; max-width: 42em; font-weight: bold">注意</p>
<p style="margin: 0; padding: 0; box-sizing: inherit; font-size: 17px; line-height: 1.7; max-width: 42em">如果 <code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2); color: rgba(26, 26, 26, 1)">shouldComponentUpdate()</code> 返回 false,则不会调用 <code class="gatsby-code-text" style="margin: 0; padding: 0 3px; box-sizing: inherit; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.94em; background: rgba(255, 229, 100, 0.2); color: rgba(26, 26, 26, 1)">render()</code>。</p>
</blockquote>
<h3 style="margin: 30px 0 0; padding: 0; box-sizing: inherit; font-size: 17px; line-height: 1.7; max-width: 42em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif">3.1.1、返回数组</h3>
<div class="cnblogs_code">
<pre>export <span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class Hi extends Component {
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> [<span>Hello</span>, <span>React</span>, <span>render()!</span>];
<span style="color: rgba(0, 0, 0, 1)">}
}</span></pre>
</div>
<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 Render1 extends Component {
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> [
</span><h2 key={1}>Hello </h2>,
<h2 key={2}> React</h2>,
<h2 key={3}> Render!</h2>,
<span style="color: rgba(0, 0, 0, 1)"> ];
}
}</span></pre>
</div>
<p>需要增加key,不然报错。</p>
<h3>3.1.2、fragments</h3>
<ul style="margin: 0 0 0 25px; padding: 0.3em 0; outline: none; list-style: decimal; line-height: 30px">
<li style="color: rgba(34, 34, 34, 1); font-family: tahoma, arial, "Microsoft YaHei"; font-size: 16px; margin-top: 0; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; outline: none; list-style: disc">可以将子列表分组,而无需向DOM添加额外节点</li>
<li style="color: rgba(34, 34, 34, 1); font-family: tahoma, arial, "Microsoft YaHei"; font-size: 16px; margin-top: 0; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; outline: none; list-style: disc">简单理解:空标签</li>
<li style="color: rgba(34, 34, 34, 1); font-family: tahoma, arial, "Microsoft YaHei"; font-size: 16px; margin-top: 0; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; outline: none; list-style: disc"><React.Fragment></React.Fragment> 或 <></></li>
<li style="margin-top: 0; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; outline: none; list-style: disc">
<div class="cnblogs_code" style="color: rgba(34, 34, 34, 1); font-family: tahoma, arial, "Microsoft YaHei"; font-size: 16px">
<pre><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><React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
<span style="color: rgba(0, 0, 0, 1)">)
}</span></pre>
</div>
<ul style="margin: 0 0 0 25px; padding: 0.3em 0; outline: none; list-style: decimal; line-height: 30px">
<li style="color: rgba(34, 34, 34, 1); font-family: tahoma, arial, "Microsoft YaHei"; font-size: 16px; margin-top: 0; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; outline: none; list-style: disc">以下面的代码为例,如果Columns组件返回多个td元素才能实现效果,但是如果我们在Columns组件中使用了div父元素,则会使td元素失效。Fragment则可以解决这个问题。</li>
<li style="margin-top: 0; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; outline: none; list-style: disc">
<div class="cnblogs_code" style="color: rgba(34, 34, 34, 1); font-family: tahoma, arial, "Microsoft YaHei"; font-size: 16px">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">table.js</span>
const Table = () =><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><table>
<tr>
<Columns />
</tr>
</table>
<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)">columns.js</span>
const Columns = () =><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><div>
<td>Hello</td>
<td>World</td>
</div>
<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>
<table>
<tr>
<div>
<td>Hello</td>
<td>World</td>
</div>
</tr>
</table>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">此时 td 是失效的,可以使用Fragemengt解决这个问题</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)">columns.js</span>
const Columns = () =><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><React.Fragment>
<td>Hello</td>
<td>World</td>
</React.Fragment>
<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)">通过上面的方法我们就可以正确的输出table啦:</span>
<table>
<tr>
<td>Hello</td>
<td>World</td>
</tr>
</table></pre>
</div>
<p><strong><span style="color: rgba(34, 34, 34, 1); font-family: tahoma, arial, "Microsoft YaHei""><span style="font-size: 16px">短语法</span></span></strong></p>
</li>
<li style="margin-top: 0; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; outline: none; list-style: disc">可以使用一种新的,且更简短的类似空标签的语法来声明 Fragments</li>
<li style="margin-top: 0; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; outline: none; list-style: disc"><> </></li>
<li style="margin-top: 0; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; outline: none; list-style: disc">不支持 key 或属性</li>
<li style="margin-top: 0; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; outline: none; list-style: disc">
<div class="cnblogs_code">
<pre>const Cloumns = () =><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><>
<td>Hello</td>
<td>World</td>
</>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}</span></pre>
</div>
<p><strong>带key 的Fragments</strong></p>
</li>
<li style="margin-top: 0; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; outline: none; list-style: disc">使用显式 <React.Fragment> 语法声明的片段可能具有 key</li>
<li style="margin-top: 0; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; outline: none; list-style: disc">key 是唯一可以传递给 Fragment 的属性</li>
<li style="margin-top: 0; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; outline: none; list-style: disc">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Glossary(props) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><dl><span style="color: rgba(0, 0, 0, 1)">
{props.items.map(item </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)"> 没有`key`,React 会发出一个关键警告</span>
<React.Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</React.Fragment>
<span style="color: rgba(0, 0, 0, 1)"> ))}
</span></dl>
<span style="color: rgba(0, 0, 0, 1)">)
}</span> </pre>
</div>
</li>
</ul>
</li>
</ul>
<h3>3.1.3、Portals</h3>
<p>Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。</p>
<div class="gatsby-highlight" data-language="jsx">
<pre class="gatsby-code-jsx"><code class="gatsby-code-jsx">ReactDOM<span class="token punctuation">.<span class="token function">createPortal<span class="token punctuation">(child<span class="token punctuation">, container<span class="token punctuation">)</span></span></span></span></span></code></pre>
</div>
<p>第一个参数(<code class="gatsby-code-text">child</code>)是任何可渲染的 React 子元素,例如一个元素,字符串或 fragment。第二个参数(<code class="gatsby-code-text">container</code>)是一个 DOM 元素。</p>
<p>通常来讲,当你从组件的 render 方法返回一个元素时,该元素将被挂载到 DOM 节点中离其最近的父节点:</p>
<div class="gatsby-highlight has-highlighted-lines" data-language="jsx">
<pre class="gatsby-code-jsx"><code class="gatsby-code-jsx"><span class="token function">render<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
<span class="token comment">// React 挂载了一个新的 div,并且把子元素渲染其中
<span class="token keyword">return <span class="token punctuation">(
<span class="gatsby-highlight-code-line"> <span class="token tag"><span class="token tag"><span class="token punctuation"><div<span class="token punctuation">><span class="token plain-text"><span class="token plain-text"> <span class="token punctuation">{<span class="token keyword">this<span class="token punctuation">.props<span class="token punctuation">.children<span class="token punctuation">}<span class="token plain-text">
<span class="gatsby-highlight-code-line"><span class="token plain-text"> <span class="token tag"><span class="token tag"><span class="token punctuation"></div<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></code></pre>
</div>
<p>然而,有时候将子元素插入到 DOM 节点中的不同位置也是有好处的:</p>
<div class="gatsby-highlight has-highlighted-lines" data-language="jsx">
<pre class="gatsby-code-jsx"><code class="gatsby-code-jsx"><span class="token function">render<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
<span class="token comment">// React 并*没有*创建一个新的 div。它只是把子元素渲染到 `domNode` 中。
<span class="token comment">// `domNode` 是一个可以在任何位置的有效 DOM 节点。
<span class="token keyword">return ReactDOM<span class="token punctuation">.<span class="token function">createPortal<span class="token punctuation">(
<span class="token keyword">this<span class="token punctuation">.props<span class="token punctuation">.children<span class="token punctuation">,
<span class="gatsby-highlight-code-line"> domNode<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></code></pre>
</div>
<p>一个 portal 的典型用例是当父组件有 <code class="gatsby-code-text">overflow: hidden</code> 或 <code class="gatsby-code-text">z-index</code> 样式时,但你需要子组件能够在视觉上“跳出”其容器。例如,对话框、悬浮卡以及提示框:</p>
<p>注意:</p>
<p>当在使用 portal 时, 记住管理键盘焦点就变得尤为重要。</p>
<p>对于模态对话框,通过遵循 WAI-ARIA 模态开发实践,来确保每个人都能够运用它。</p>
<p>示例:</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;
import ReactDOM from </span>"react-dom"<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 Hi extends Component {
constructor(props) {
super(props);
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.container = document.createElement("div"<span style="color: rgba(0, 0, 0, 1)">);
document.body.appendChild(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.container);
}
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> ReactDOM.createPortal(<h2>Portal</h2>, this.container);
<span style="color: rgba(0, 0, 0, 1)">}
}</span></pre>
</div>
<p>运行结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230308110707037-9828574.png" alt="" loading="lazy"></p>
<p>卸载时需要移除</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">componentWillUnmount() {
document.body.removeChild(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.container);
}</span></pre>
</div>
<h2>3.2、错误边界</h2>
<div>
<div>部分 UI 的异常不应该破坏了整个应用。为了解决 React 用户的这一问题,React 16 引入了一种称为 “错误边界” 的新概念。 错误边界是用于捕获其子组件树 JavaScript 异常,记录错误并展示一个回退的 UI 的 React 组件,而不是整个组件树的异常。错误组件在渲染期间,生命周期方法内,以及整个组件树构造函数内捕获错误。</div>
<div>
<pre class="gatsby-code-jsx"><code class="gatsby-code-jsx"><span class="token function">componentDidCatch<span class="token punctuation">(error<span class="token punctuation">, info<span class="token punctuation">)</span></span></span></span></code></pre>
<p>此生命周期在后代组件抛出错误后被调用。 它接收两个参数:</p>
<ol>
<li><code class="gatsby-code-text">error</code> —— 抛出的错误。</li>
<li><code class="gatsby-code-text">info</code> —— 带有 <code class="gatsby-code-text">componentStack</code> key 的对象,其中包含有关组件引发错误的栈信息。</li>
</ol>
<p><code class="gatsby-code-text">componentDidCatch()</code> 会在“提交”阶段被调用,因此允许执行副作用。 它应该用于记录错误之类的情况:</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">class <span class="token class-name">ErrorBoundary <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">.state <span class="token operator">= <span class="token punctuation">{ <span class="token literal-property property">hasError<span class="token operator">: <span class="token boolean">false <span class="token punctuation">}<span class="token punctuation">;
<span class="token punctuation">}
<span class="token keyword">static <span class="token function">getDerivedStateFromError<span class="token punctuation">(<span class="token parameter">error<span class="token punctuation">) <span class="token punctuation">{
<span class="token comment">// 更新 state 使下一次渲染可以显示降级 UI
<span class="token keyword">return <span class="token punctuation">{ <span class="token literal-property property">hasError<span class="token operator">: <span class="token boolean">true <span class="token punctuation">}<span class="token punctuation">;
<span class="token punctuation">}
<span class="gatsby-highlight-code-line"><span class="token function">componentDidCatch<span class="token punctuation">(<span class="token parameter">error<span class="token punctuation">, info<span class="token punctuation">) <span class="token punctuation">{<span class="gatsby-highlight-code-line"> <span class="token comment">// "组件堆栈" 例子:<span class="gatsby-highlight-code-line"> <span class="token comment">// in ComponentThatThrows (created by App)<span class="gatsby-highlight-code-line"> <span class="token comment">// in ErrorBoundary (created by App)<span class="gatsby-highlight-code-line"> <span class="token comment">// in div (created by App)<span class="gatsby-highlight-code-line"> <span class="token comment">// in App<span class="gatsby-highlight-code-line"> <span class="token function">logComponentStackToMyService<span class="token punctuation">(info<span class="token punctuation">.componentStack<span class="token punctuation">)<span class="token punctuation">;<span class="gatsby-highlight-code-line"><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">if <span class="token punctuation">(<span class="token keyword">this<span class="token punctuation">.state<span class="token punctuation">.hasError<span class="token punctuation">) <span class="token punctuation">{
<span class="token comment">// 你可以渲染任何自定义的降级 UI
<span class="token keyword">return <span class="token tag"><span class="token tag"><span class="token punctuation"><h1<span class="token punctuation">><span class="token plain-text">Something went wrong.<span class="token tag"><span class="token tag"><span class="token punctuation"></h1<span class="token punctuation">><span class="token punctuation">;
<span class="token punctuation">}
<span class="token keyword">return <span class="token keyword">this<span class="token punctuation">.props<span class="token punctuation">.children<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></code></pre>
</div>
<p>React 的开发和生产构建版本在 <code class="gatsby-code-text">componentDidCatch()</code> 的方式上有轻微差别。</p>
<p>在开发模式下,错误会冒泡至 <code class="gatsby-code-text">window</code>,这意味着任何 <code class="gatsby-code-text">window.onerror</code> 或 <code class="gatsby-code-text">window.addEventListener('error', callback)</code> 会中断这些已经被 <code class="gatsby-code-text">componentDidCatch()</code> 捕获的错误。</p>
<p>相反,在生产模式下,错误不会冒泡,这意味着任何根错误处理器只会接受那些没有显式地被 <code class="gatsby-code-text">componentDidCatch()</code> 捕获的错误。</p>
<blockquote>
<p>注意</p>
<p>如果发生错误,你可以通过调用 <code class="gatsby-code-text">setState</code> 使用 <code class="gatsby-code-text">componentDidCatch()</code> 渲染降级 UI,但在未来的版本中将不推荐这样做。 可以使用静态 <code class="gatsby-code-text">getDerivedStateFromError()</code> 来处理降级渲染。</p>
</blockquote>
</div>
</div>
<p>特别注意:</p>
<div>
<ul>
<li>事件处理 (比如调用了一个不存在的方法<code>this.abc()</code>,并不会执行<code>componentDidCatch</code>)</li>
<li>异步代码 (例如 <code>setTimeout</code> 或 <code>requestAnimationFrame</code> 回调函数)</li>
<li>服务端渲染</li>
<li>错误边界自身抛出来的错误 (而不是其子组件)</li>
</ul>
<div>
<div>
<p>当render()函数出现问题时,<code>componentDidCatch</code>会捕获异常并处理</p>
<div class="_2Uzcx_">
<pre class="line-numberslanguage-csharp"><code class="language-csharp">此时,<span class="token function">render<span class="token punctuation">(<span class="token punctuation">)函数里面发生错误,则 componentDidCatch 会进行调用,在里面进行相应的处理
<span class="token function">render<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">let a <span class="token operator">= <span class="token punctuation">[<span class="token number">1<span class="token punctuation">,<span class="token number">2<span class="token punctuation">,<span class="token number">3<span class="token punctuation">]
<span class="token keyword">let <span class="token keyword">value <span class="token operator">= a<span class="token punctuation">[<span class="token number">3<span class="token punctuation">]<span class="token punctuation">.<span class="token function">toString<span class="token punctuation">(<span class="token punctuation">) 对 undefined 进行操作
<span class="token keyword">return <span class="token punctuation">(<span class="token punctuation">.<span class="token punctuation">.<span class="token punctuation">.<span class="token punctuation">.<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></code></pre>
</div>
<p>防止 <strong><em>页面</em></strong> 级别的崩溃~</p>
</div>
</div>
</div>
<p>示例:</p>
<p>ErrorCom.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 ErrorCom extends Component {
state </span>= { n: 0<span style="color: rgba(0, 0, 0, 1)"> };
clickHandle </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (let i = 0; i < 100; i++<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(
{
n: </span><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, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.state.n === 3<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Error("发生了错误"<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><div>
<h2>组件</h2>
<span style="color: rgba(0, 0, 0, 1)"> 点击3次就异常了
</span><button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.clickHandle}>{<span style="color: rgba(0, 0, 255, 1)">this</span>.state.n}</button>
</div>
<span style="color: rgba(0, 0, 0, 1)"> );
}
}</span></pre>
</div>
<p>index.js</p>
<div class="cnblogs_code">
<pre>import ErrorCom from "./ErrorCom"<span style="color: rgba(0, 0, 0, 1)">;
const vnode </span>=<span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<ErrorCom />
<Hi />
</div>
);</pre>
</div>
<p>默认情况:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230308155416986-465690830.png" alt="" width="346" height="233" loading="lazy"></p>
<p> 发生错误时整个应用崩溃</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230308155444013-312613994.png" alt="" width="1128" height="837" loading="lazy"></p>
<p> 改进,定义ErrorBoundary组件</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 ErrorBoundary extends Component {
state </span>= { error: <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)"> };
componentDidCatch(error, info) {
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({ error });
}
render() {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.error) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<h2>发生了错误</h2>
<div>{<span style="color: rgba(0, 0, 255, 1)">this</span>.state.error && <span style="color: rgba(0, 0, 255, 1)">this</span>.state.error.toString()}</div>
</div>
<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)">this</span><span style="color: rgba(0, 0, 0, 1)">.props.children;
}
}</span></pre>
</div>
<p>修改index.js文件</p>
<div class="cnblogs_code">
<pre>const vnode =<span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<ErrorBoundary>
<ErrorCom />
</ErrorBoundary>
<Hi />
</div>
);</pre>
</div>
<p>发生错误时,仅当前控制失效了:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230308155730685-1558443995.png" alt="" width="1020" height="792" loading="lazy"></p>
<h2> 3.3、自定义DOM属性</h2>
<p>React 16 之前会忽略不是把的HTML和SVG属性,现在React会把不识别的属性传递给DOM。</p>
<p>React16之前:</p>
<p> <div cust-attr="someting"></div><br>会被渲染成:</p>
<p> <div></div></p>
<p>React 16渲染出来的节点:</p>
<p> <div cust-attr="someting"></div></p>
<h2> 3.4、组件的state</h2>
<h3>3.4.1、组件state</h3>
<p>1,设计合适的state</p>
<p>state必须能代表一个组件UI呈现的完整状态集,代表一个组件UI呈现的最小状态集。</p>
<p>state必须能代表一个组件UI呈现的完整状态集又可以分成两类数据:用作渲染组件时使用到的数据的来源,用作组件UI展现形式的判断依据:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">class Hello 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)"> {
user: </span>'react', <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">用作渲染组件时使用到的数据的来源</span>
display: <span style="color: rgba(0, 0, 255, 1)">true</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() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div><span style="color: rgba(0, 0, 0, 1)">
{
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.state.display ? <h1>{<span style="color: rgba(0, 0, 255, 1)">this</span>.state.user}</h1> : <></><span style="color: rgba(0, 0, 0, 1)">
}
</span></div>
<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>在es6中,可以使用this.属性名定义一个class的属性,也可以说属性是直接挂载在this下的一个变量。因此,state和props实际上也是组件的属性,只不过是react在Component class中预定义好的属性。除了state和props以外的其他组件属性称为组件的普通属性。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">class Hello 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, 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)"> {
date: </span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date()
}
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.updateDate = <span style="color: rgba(0, 0, 255, 1)">this</span>.updateDate.bind(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
}
componentDidMount(){
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.timer = setInterval(<span style="color: rgba(0, 0, 255, 1)">this</span>.updateDate, 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);
}
updateDate(){
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({
date: </span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date()
})
}
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<h1>{<span style="color: rgba(0, 0, 255, 1)">this</span>.state.date.toString()}</h1>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Hello;</pre>
</div>
<p>组件中用到的一个变量是否应该作为state可以通过下面4条依据判断:</p>
<ol>
<li>这个变量是否通过props从父组件中获取?如果是,那么它不是一个状态</li>
<li>这个变量是否在生命周期中都保持不变?如果是,那么它不是一个状态</li>
<li>这个变量是否可以通过其他状态(state)或者属性(props)计算得到?如果是,那么它不是一个状态</li>
<li>这个变量是否在组件的render方法中使用?如果不是,那么它不是一个状态,这种情况更适合定义为组件的一个普通属性</li>
</ol>
<h3>3.4.2、正确修改state</h3>
<p>①不能直接修改state,需要使用setState()</p>
<p>②state的更新是异步的</p>
<p>React会将多次setState的状态合并成一次状态修改,不能依赖当前的state计算下一个state(props也是异步的)。</p>
<p>例如:连续两次点击加入购物车,实际数量只会加1,在React合并多次修改为1次的情况下,相当于执行了:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">Object.assign(
previousState,
{quantity: </span><span style="color: rgba(0, 0, 255, 1)">this</span>.state.quantity + 1<span style="color: rgba(0, 0, 0, 1)">},
{quantity: </span><span style="color: rgba(0, 0, 255, 1)">this</span>.state.quantity + 1<span style="color: rgba(0, 0, 0, 1)">}
)</span></pre>
</div>
<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 ErrorCom extends Component {
state </span>= { n: 0<span style="color: rgba(0, 0, 0, 1)"> };
clickHandle </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (let i = 0; i < 100; i++<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(
{
n: </span><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, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.state.n === 300<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Error("发生了错误"<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><div>
<h2>组件</h2>
<span style="color: rgba(0, 0, 0, 1)"> 点击3次就异常了
</span><button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.clickHandle}>{<span style="color: rgba(0, 0, 255, 1)">this</span>.state.n}</button>
</div>
<span style="color: rgba(0, 0, 0, 1)"> );
}
}</span></pre>
</div>
<p>上面的代码虽然循环了100次,实际每次只增加了1。</p>
<p>这种情况下,可以使用另一个接收一个函数作为参数的setState,这个函数有两个参数,第一个是当前修改后的最新状态的前一个状态preState,第二个参数是当前最新的属性props:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">this</span>.setState((preState,props) =><span style="color: rgba(0, 0, 0, 1)"> ({
quantity: preState.quantity </span>+ 1<span style="color: rgba(0, 0, 0, 1)">;
}))</span></pre>
</div>
<h3>3.4.3、state的更新是一个合并的过程</h3>
<p>后设置的state会覆盖前面的状态,如果不存在则添加。</p>
<h3>3.4.4、state与不可变对象</h3>
<p>直接修改state,组件不会render;state包含的所有状态都应该是不可变对象,当state中某个状态发生变化时,应该重新创建这个状态对象,而不是直接修改原来的状态。创建新的状态有以下三种方法:</p>
<p>状态的类型是不可变类型(数字、字符串、布尔值、null、undefined):因为状态是不可变类型,所以直接赋一个新值即可<br>状态的类型是数组:可以使用数组的concat或者es6的扩展语法,slice方法、filter方法。不能使用push、pop、shift、unshift、splice等方法修改数组类型的状态,因为这些方法会在原数组基础上修改。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">this</span>.setState((preState) =><span style="color: rgba(0, 0, 0, 1)"> ({
arr: [...preState.arr,</span>'react'<span style="color: rgba(0, 0, 0, 1)">];
}))
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.setState((preState) =><span style="color: rgba(0, 0, 0, 1)"> ({
arr: preState.arr.concat([</span>'react'<span style="color: rgba(0, 0, 0, 1)">])
}))</span></pre>
</div>
<p>状态的类型是普通对象(不包含字符串、数组):使用ES6的Object.assgin方法或者对象扩展语法</p>
<div class="cnblogs_code">
<pre>Object.assign({},preState.owner,{name:"tom"});</pre>
</div>
<p>或者</p>
<div class="cnblogs_code">
<pre>{...preState.owner,name:"tom"}</pre>
</div>
<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 StuList 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)">{
stu:{id:</span>1001,name:'tom',age:19<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>
a=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)">普通属性</span>
users =<span style="color: rgba(0, 0, 0, 1)"> [
{ id: </span>1001, name: "John"<span style="color: rgba(0, 0, 0, 1)"> },
{ id: </span>1002, name: "Jack"<span style="color: rgba(0, 0, 0, 1)"> },
{ id: </span>1003, name: "Rose"<span style="color: rgba(0, 0, 0, 1)"> },
{ id: </span>1004, name: "Lili"<span style="color: rgba(0, 0, 0, 1)"> },
{ id: </span>1005, name: "Jery"<span style="color: rgba(0, 0, 0, 1)"> },
];
handleAddProp</span>=(e)=><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, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">Object.assign是重新定义对象,使用后面的对象覆盖前面的对象,有就替换,没有就添加</span>
let newStudent=Object.assign({},<span style="color: rgba(0, 0, 255, 1)">this</span>.state.stu,{height:198<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({stu:newStudent});
</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({stu:{...<span style="color: rgba(0, 0, 255, 1)">this</span>.state.stu,weight:70,age:88<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><div>
<h2>学生信息:{JSON.stringify(<span style="color: rgba(0, 0, 255, 1)">this</span>.state)}</h2>
<p>
<button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleAddProp}>增加属性height,weight</button>
</p>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}</span></pre>
</div>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230317105447813-2139093066.png" alt="" width="696" height="127" loading="lazy"></p>
<p> </p>
<p> </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 StuList 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)">{
stus:[</span>1,2,3,4,5,6,7,8,9,10<span style="color: rgba(0, 0, 0, 1)">]
}
}
handleEditArray</span>=(e)=><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)">追加11到数组结尾</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">this.setState({stus:[...this.state.stus,11]});</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">this.setState({stus:this.state.stus.concat(11)});</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)">this.setState({stus:this.state.stus.filter(n=>n%2===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, 128, 0, 1)">this.setState({stus:this.state.stus.map(n=>n%2===1?n:-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, 128, 0, 1)"> let newStus=[];</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> let index=1;</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> for(var i=0; i<this.state.stus.length;i++){</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> if(i!==index){</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> newStus.push(this.state.stus);</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>
let newStus=JSON.parse(JSON.stringify(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.stus));
newStus.splice(</span>1,1<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({stus:newStus});
}
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<h2>数组:{JSON.stringify(<span style="color: rgba(0, 0, 255, 1)">this</span>.state.stus)}</h2>
<p>
<button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleEditArray}>修改数组</button>
</p>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}</span></pre>
</div>
<h2>13.5、Axios</h2>
<p>Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。</p>
<p>源代码与英文帮助:https://github.com/axios/axios</p>
<h3 id="特性" class="article-heading">3.5.1、特性</h3>
<ul>
<li>从浏览器中创建 XMLHttpRequests</li>
<li>从 node.js 创建 http 请求</li>
<li>支持 Promise API</li>
<li>拦截请求和响应</li>
<li>转换请求数据和响应数据</li>
<li>取消请求</li>
<li>自动转换 JSON 数据</li>
<li>客户端支持防御 XSRF</li>
</ul>
<h3 id="浏览器支持" class="article-heading">3.5.2、浏览器支持</h3>
<img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230309113718505-1584434952.png" alt="" loading="lazy">
<h3 id="安装" class="article-heading">3.5.3、安装</h3>
<p>使用 npm:</p>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line">$ npm install axios<br></span></pre>
</td>
</tr>
</tbody>
</table>
<p>使用 bower:</p>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line">$ bower install axios<br></span></pre>
</td>
</tr>
</tbody>
</table>
<p>使用 cdn:</p>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line"><span class="tag"><<span class="name">script <span class="attr">src=<span class="string">"https://unpkg.com/axios/dist/axios.min.js"><span class="tag"></<span class="name">script><br></span></span></span></span></span></span></span></pre>
</td>
</tr>
</tbody>
</table>
<h3 id="案例" class="article-heading">3.5.4、案例</h3>
<p>执行 <code>GET</code> 请求</p>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line"><span class="comment">// 为给定 ID 的 user 创建请求<br><span class="line">axios.get(<span class="string">'/user?ID=12345')<br><span class="line">.then(<span class="function"><span class="keyword">function (<span class="params">response) {<br><span class="line"> <span class="built_in">console.log(response);<br><span class="line">})<br><span class="line">.catch(<span class="function"><span class="keyword">function (<span class="params">error) {<br><span class="line"> <span class="built_in">console.log(error);<br><span class="line">});<br><span class="line"><br><span class="line"><span class="comment">// 上面的请求也可以这样做<br><span class="line">axios.get(<span class="string">'/user', {<br><span class="line"> params: {<br><span class="line"> ID: <span class="number">12345<br><span class="line"> }<br><span class="line">})<br><span class="line">.then(<span class="function"><span class="keyword">function (<span class="params">response) {<br><span class="line"> <span class="built_in">console.log(response);<br><span class="line">})<br><span class="line">.catch(<span class="function"><span class="keyword">function (<span class="params">error) {<br><span class="line"> <span class="built_in">console.log(error);<br><span class="line">});<br></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></pre>
</td>
</tr>
</tbody>
</table>
<p>执行 <code>POST</code> 请求</p>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line">axios.post(<span class="string">'/user', {<br><span class="line"> firstName: <span class="string">'Fred',<br><span class="line"> lastName: <span class="string">'Flintstone'<br><span class="line">})<br><span class="line">.then(<span class="function"><span class="keyword">function (<span class="params">response) {<br><span class="line"> <span class="built_in">console.log(response);<br><span class="line">})<br><span class="line">.catch(<span class="function"><span class="keyword">function (<span class="params">error) {<br><span class="line"> <span class="built_in">console.log(error);<br><span class="line">});<br></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
</td>
</tr>
</tbody>
</table>
<p>执行多个并发请求</p>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line"><span class="function"><span class="keyword">function <span class="title">getUserAccount(<span class="params">) {<br><span class="line"><span class="keyword">return axios.get(<span class="string">'/user/12345');<br><span class="line">}<br><span class="line"><br><span class="line"><span class="function"><span class="keyword">function <span class="title">getUserPermissions(<span class="params">) {<br><span class="line"><span class="keyword">return axios.get(<span class="string">'/user/12345/permissions');<br><span class="line">}<br><span class="line"><br><span class="line">axios.all()<br><span class="line">.then(axios.spread(<span class="function"><span class="keyword">function (<span class="params">acct, perms) {<br><span class="line"> <span class="comment">// 两个请求现在都执行完成<br><span class="line">}));<br></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></pre>
</td>
</tr>
</tbody>
</table>
<h3 id="axios-API" class="article-heading">3.5.5、axios API</h3>
<p>可以通过向 <code>axios</code> 传递相关配置来创建请求</p>
<h5 id="axios-config" class="article-heading">axios(config)</h5>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line"><span class="comment">// 发送 POST 请求<br><span class="line">axios({<br><span class="line">method: <span class="string">'post',<br><span class="line">url: <span class="string">'/user/12345',<br><span class="line">data: {<br><span class="line"> firstName: <span class="string">'Fred',<br><span class="line"> lastName: <span class="string">'Flintstone'<br><span class="line">}<br><span class="line">});<br></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
</td>
</tr>
</tbody>
</table>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line"><span class="comment">// 获取远端图片<br><span class="line">axios({<br><span class="line">method:<span class="string">'get',<br><span class="line">url:<span class="string">'http://bit.ly/2mTM3nY',<br><span class="line">responseType:<span class="string">'stream'<br><span class="line">})<br><span class="line">.then(<span class="function"><span class="keyword">function(<span class="params">response) {<br><span class="line">response.data.pipe(fs.createWriteStream(<span class="string">'ada_lovelace.jpg'))<br><span class="line">});<br></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
</td>
</tr>
</tbody>
</table>
<h5 id="axios-url-config" class="article-heading">axios(url[, config])</h5>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line"><span class="comment">// 发送 GET 请求(默认的方法)<br><span class="line">axios(<span class="string">'/user/12345');<br></span></span></span></span></pre>
</td>
</tr>
</tbody>
</table>
<h3 id="请求方法的别名" class="article-heading">3.5.6、请求方法的别名</h3>
<p>为方便起见,为所有支持的请求方法提供了别名</p>
<h5 id="axios-request-config" class="article-heading">axios.request(config)</h5>
<h5 id="axios-get-url-config" class="article-heading">axios.get(url[, config])</h5>
<h5 id="axios-delete-url-config" class="article-heading">axios.delete(url[, config])</h5>
<h5 id="axios-head-url-config" class="article-heading">axios.head(url[, config])</h5>
<h5 id="axios-options-url-config" class="article-heading">axios.options(url[, config])</h5>
<h5 id="axios-post-url-data-config" class="article-heading">axios.post(url[, data[, config]])</h5>
<h5 id="axios-put-url-data-config" class="article-heading">axios.put(url[, data[, config]])</h5>
<h5 id="axios-patch-url-data-config" class="article-heading">axios.patch(url[, data[, config]])</h5>
<h6 id="注意" class="article-heading">注意</h6>
<p>在使用别名方法时, <code>url</code>、<code>method</code>、<code>data</code> 这些属性都不必在配置中指定。</p>
<h3 id="并发" class="article-heading">3.5.7、并发</h3>
<p>处理并发请求的助手函数</p>
<h5 id="axios-all-iterable" class="article-heading">axios.all(iterable)</h5>
<h5 id="axios-spread-callback" class="article-heading">axios.spread(callback)</h5>
<h3 id="创建实例" class="article-heading">3.5.8、创建实例</h3>
<p>可以使用自定义配置新建一个 axios 实例</p>
<h5 id="axios-create-config" class="article-heading">axios.create()</h5>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line"><span class="keyword">const instance = axios.create({<br><span class="line">baseURL: <span class="string">'https://some-domain.com/api/',<br><span class="line">timeout: <span class="number">1000,<br><span class="line">headers: {<span class="string">'X-Custom-Header': <span class="string">'foobar'}<br><span class="line">});<br></span></span></span></span></span></span></span></span></span></span></pre>
</td>
</tr>
</tbody>
</table>
<h3 id="实例方法" class="article-heading">3.5.9、实例方法</h3>
<p>以下是可用的实例方法。指定的配置将与实例的配置合并。</p>
<h5 id="axios-request-config-1" class="article-heading">axios#request(config)</h5>
<h5 id="axios-get-url-config-1" class="article-heading">axios#get(url[, config])</h5>
<h5 id="axios-delete-url-config-1" class="article-heading">axios#delete(url[, config])</h5>
<h5 id="axios-head-url-config-1" class="article-heading">axios#head(url[, config])</h5>
<h5 id="axios-options-url-config-1" class="article-heading">axios#options(url[, config])</h5>
<h5 id="axios-post-url-data-config-1" class="article-heading">axios#post(url[, data[, config]])</h5>
<h5 id="axios-put-url-data-config-1" class="article-heading">axios#put(url[, data[, config]])</h5>
<h5 id="axios-patch-url-data-config-1" class="article-heading">axios#patch(url[, data[, config]])</h5>
<h3 id="请求配置" class="article-heading">3.5.10、请求配置</h3>
<p>这些是创建请求时可以用的配置选项。只有 <code>url</code> 是必需的。如果没有指定 <code>method</code>,请求将默认使用 <code>get</code> 方法。</p>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line">{<br><span class="line"> <span class="comment">// `url` 是用于请求的服务器 URL<br><span class="line">url: <span class="string">'/user',<br><span class="line"><br><span class="line"><span class="comment">// `method` 是创建请求时使用的方法<br><span class="line">method: <span class="string">'get', <span class="comment">// default<br><span class="line"><br><span class="line"><span class="comment">// `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。<br><span class="line"><span class="comment">// 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL<br><span class="line">baseURL: <span class="string">'https://some-domain.com/api/',<br><span class="line"><br><span class="line"><span class="comment">// `transformRequest` 允许在向服务器发送前,修改请求数据<br><span class="line"><span class="comment">// 只能用在 'PUT', 'POST' 和 'PATCH' 这几个请求方法<br><span class="line"><span class="comment">// 后面数组中的函数必须返回一个字符串,或 ArrayBuffer,或 Stream<br><span class="line">transformRequest: [<span class="function"><span class="keyword">function (<span class="params">data, headers) {<br><span class="line"> <span class="comment">// 对 data 进行任意转换处理<br><span class="line"> <span class="keyword">return data;<br><span class="line">}],<br><span class="line"><br><span class="line"><span class="comment">// `transformResponse` 在传递给 then/catch 前,允许修改响应数据<br><span class="line">transformResponse: [<span class="function"><span class="keyword">function (<span class="params">data) {<br><span class="line"> <span class="comment">// 对 data 进行任意转换处理<br><span class="line"> <span class="keyword">return data;<br><span class="line">}],<br><span class="line"><br><span class="line"><span class="comment">// `headers` 是即将被发送的自定义请求头<br><span class="line">headers: {<span class="string">'X-Requested-With': <span class="string">'XMLHttpRequest'},<br><span class="line"><br><span class="line"><span class="comment">// `params` 是即将与请求一起发送的 URL 参数<br><span class="line"><span class="comment">// 必须是一个无格式对象(plain object)或 URLSearchParams 对象<br><span class="line">params: {<br><span class="line"> ID: <span class="number">12345<br><span class="line">},<br><span class="line"><br><span class="line"> <span class="comment">// `paramsSerializer` 是一个负责 `params` 序列化的函数<br><span class="line"><span class="comment">// (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)<br><span class="line">paramsSerializer: <span class="function"><span class="keyword">function(<span class="params">params) {<br><span class="line"> <span class="keyword">return Qs.stringify(params, {<span class="attr">arrayFormat: <span class="string">'brackets'})<br><span class="line">},<br><span class="line"><br><span class="line"><span class="comment">// `data` 是作为请求主体被发送的数据<br><span class="line"><span class="comment">// 只适用于这些请求方法 'PUT', 'POST', 和 'PATCH'<br><span class="line"><span class="comment">// 在没有设置 `transformRequest` 时,必须是以下类型之一:<br><span class="line"><span class="comment">// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams<br><span class="line"><span class="comment">// - 浏览器专属:FormData, File, Blob<br><span class="line"><span class="comment">// - Node 专属: Stream<br><span class="line">data: {<br><span class="line"> firstName: <span class="string">'Fred'<br><span class="line">},<br><span class="line"><br><span class="line"><span class="comment">// `timeout` 指定请求超时的毫秒数(0 表示无超时时间)<br><span class="line"><span class="comment">// 如果请求话费了超过 `timeout` 的时间,请求将被中断<br><span class="line">timeout: <span class="number">1000,<br><span class="line"><br><span class="line"> <span class="comment">// `withCredentials` 表示跨域请求时是否需要使用凭证<br><span class="line">withCredentials: <span class="literal">false, <span class="comment">// default<br><span class="line"><br><span class="line"><span class="comment">// `adapter` 允许自定义处理请求,以使测试更轻松<br><span class="line"><span class="comment">// 返回一个 promise 并应用一个有效的响应 (查阅 (#response-api)).<br><span class="line">adapter: <span class="function"><span class="keyword">function (<span class="params">config) {<br><span class="line"> <span class="comment">/* ... */<br><span class="line">},<br><span class="line"><br><span class="line"> <span class="comment">// `auth` 表示应该使用 HTTP 基础验证,并提供凭据<br><span class="line"><span class="comment">// 这将设置一个 `Authorization` 头,覆写掉现有的任意使用 `headers` 设置的自定义 `Authorization`头<br><span class="line">auth: {<br><span class="line"> username: <span class="string">'janedoe',<br><span class="line"> password: <span class="string">'s00pers3cret'<br><span class="line">},<br><span class="line"><br><span class="line"> <span class="comment">// `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'<br><span class="line">responseType: <span class="string">'json', <span class="comment">// default<br><span class="line"><br><span class="line"><span class="comment">// `responseEncoding` indicates encoding to use for decoding responses<br><span class="line"><span class="comment">// Note: Ignored for `responseType` of 'stream' or client-side requests<br><span class="line">responseEncoding: <span class="string">'utf8', <span class="comment">// default<br><span class="line"><br><span class="line"> <span class="comment">// `xsrfCookieName` 是用作 xsrf token 的值的cookie的名称<br><span class="line">xsrfCookieName: <span class="string">'XSRF-TOKEN', <span class="comment">// default<br><span class="line"><br><span class="line"><span class="comment">// `xsrfHeaderName` is the name of the http header that carries the xsrf token value<br><span class="line">xsrfHeaderName: <span class="string">'X-XSRF-TOKEN', <span class="comment">// default<br><span class="line"><br><span class="line"> <span class="comment">// `onUploadProgress` 允许为上传处理进度事件<br><span class="line">onUploadProgress: <span class="function"><span class="keyword">function (<span class="params">progressEvent) {<br><span class="line"> <span class="comment">// Do whatever you want with the native progress event<br><span class="line">},<br><span class="line"><br><span class="line"><span class="comment">// `onDownloadProgress` 允许为下载处理进度事件<br><span class="line">onDownloadProgress: <span class="function"><span class="keyword">function (<span class="params">progressEvent) {<br><span class="line"> <span class="comment">// 对原生进度事件的处理<br><span class="line">},<br><span class="line"><br><span class="line"> <span class="comment">// `maxContentLength` 定义允许的响应内容的最大尺寸<br><span class="line">maxContentLength: <span class="number">2000,<br><span class="line"><br><span class="line"><span class="comment">// `validateStatus` 定义对于给定的HTTP 响应状态码是 resolve 或 rejectpromise 。如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),promise 将被 resolve; 否则,promise 将被 rejecte<br><span class="line">validateStatus: <span class="function"><span class="keyword">function (<span class="params">status) {<br><span class="line"> <span class="keyword">return status >= <span class="number">200 && status < <span class="number">300; <span class="comment">// default<br><span class="line">},<br><span class="line"><br><span class="line"><span class="comment">// `maxRedirects` 定义在 node.js 中 follow 的最大重定向数目<br><span class="line"><span class="comment">// 如果设置为0,将不会 follow 任何重定向<br><span class="line">maxRedirects: <span class="number">5, <span class="comment">// default<br><span class="line"><br><span class="line"><span class="comment">// `socketPath` defines a UNIX Socket to be used in node.js.<br><span class="line"><span class="comment">// e.g. '/var/run/docker.sock' to send requests to the docker daemon.<br><span class="line"><span class="comment">// Only either `socketPath` or `proxy` can be specified.<br><span class="line"><span class="comment">// If both are specified, `socketPath` is used.<br><span class="line">socketPath: <span class="literal">null, <span class="comment">// default<br><span class="line"><br><span class="line"><span class="comment">// `httpAgent` 和 `httpsAgent` 分别在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理。允许像这样配置选项:<br><span class="line"><span class="comment">// `keepAlive` 默认没有启用<br><span class="line">httpAgent: <span class="keyword">new http.Agent({ <span class="attr">keepAlive: <span class="literal">true }),<br><span class="line">httpsAgent: <span class="keyword">new https.Agent({ <span class="attr">keepAlive: <span class="literal">true }),<br><span class="line"><br><span class="line"><span class="comment">// 'proxy' 定义代理服务器的主机名称和端口<br><span class="line"><span class="comment">// `auth` 表示 HTTP 基础验证应当用于连接代理,并提供凭据<br><span class="line"><span class="comment">// 这将会设置一个 `Proxy-Authorization` 头,覆写掉已有的通过使用 `header` 设置的自定义 `Proxy-Authorization` 头。<br><span class="line">proxy: {<br><span class="line"> host: <span class="string">'127.0.0.1',<br><span class="line"> port: <span class="number">9000,<br><span class="line"> auth: {<br><span class="line"> username: <span class="string">'mikeymike',<br><span class="line"> password: <span class="string">'rapunz3l'<br><span class="line"> }<br><span class="line">},<br><span class="line"><br><span class="line"><span class="comment">// `cancelToken` 指定用于取消请求的 cancel token<br><span class="line"><span class="comment">// (查看后面的 Cancellation 这节了解更多)<br><span class="line">cancelToken: <span class="keyword">new CancelToken(<span class="function"><span class="keyword">function (<span class="params">cancel) {<br><span class="line">})<br><span class="line">}<br></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></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></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
</td>
</tr>
</tbody>
</table>
<h3 id="响应结构" class="article-heading">3.5.11、响应结构</h3>
<p>某个请求的响应包含以下信息</p>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line">{<br><span class="line"><span class="comment">// `data` 由服务器提供的响应<br><span class="line">data: {},<br><span class="line"><br><span class="line"><span class="comment">// `status` 来自服务器响应的 HTTP 状态码<br><span class="line">status: <span class="number">200,<br><span class="line"><br><span class="line"><span class="comment">// `statusText` 来自服务器响应的 HTTP 状态信息<br><span class="line">statusText: <span class="string">'OK',<br><span class="line"><br><span class="line"><span class="comment">// `headers` 服务器响应的头<br><span class="line">headers: {},<br><span class="line"><br><span class="line"> <span class="comment">// `config` 是为请求提供的配置信息<br><span class="line">config: {},<br><span class="line"> <span class="comment">// 'request'<br><span class="line"><span class="comment">// `request` is the request that generated this response<br><span class="line"><span class="comment">// It is the last ClientRequest instance in node.js (in redirects)<br><span class="line"><span class="comment">// and an XMLHttpRequest instance the browser<br><span class="line">request: {}<br><span class="line">}<br></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></pre>
</td>
</tr>
</tbody>
</table>
<p>使用 <code>then</code> 时,你将接收下面这样的响应 :</p>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line">axios.get(<span class="string">'/user/12345')<br><span class="line">.then(<span class="function"><span class="keyword">function(<span class="params">response) {<br><span class="line"> <span class="built_in">console.log(response.data);<br><span class="line"> <span class="built_in">console.log(response.status);<br><span class="line"> <span class="built_in">console.log(response.statusText);<br><span class="line"> <span class="built_in">console.log(response.headers);<br><span class="line"> <span class="built_in">console.log(response.config);<br><span class="line">});<br></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
</td>
</tr>
</tbody>
</table>
<p>在使用 <code>catch</code> 时,或传递 rejection callback 作为 <code>then</code> 的第二个参数时,响应可以通过 <code>error</code> 对象可被使用,正如在错误处理这一节所讲。</p>
<h3 id="配置默认值" class="article-heading">3.5.12、配置默认值</h3>
<p>你可以指定将被用在各个请求的配置默认值</p>
<h4 id="全局的-axios-默认值" class="article-heading">全局的 axios 默认值</h4>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line">axios.defaults.baseURL = <span class="string">'https://api.example.com';<br><span class="line">axios.defaults.headers.common[<span class="string">'Authorization'] = AUTH_TOKEN;<br><span class="line">axios.defaults.headers.post[<span class="string">'Content-Type'] = <span class="string">'application/x-www-form-urlencoded';<br></span></span></span></span></span></span></span></pre>
</td>
</tr>
</tbody>
</table>
<h4 id="自定义实例默认值" class="article-heading">自定义实例默认值</h4>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line"><span class="comment">// Set config defaults when creating the instance<br><span class="line"><span class="keyword">const instance = axios.create({<br><span class="line">baseURL: <span class="string">'https://api.example.com'<br><span class="line">});<br><span class="line"><br><span class="line"><span class="comment">// Alter defaults after instance has been created<br><span class="line">instance.defaults.headers.common[<span class="string">'Authorization'] = AUTH_TOKEN;<br></span></span></span></span></span></span></span></span></span></span></span></span></pre>
</td>
</tr>
</tbody>
</table>
<h4 id="配置的优先顺序" class="article-heading">配置的优先顺序</h4>
<p>配置会以一个优先顺序进行合并。这个顺序是:在 <code>lib/defaults.js</code> 找到的库的默认值,然后是实例的 <code>defaults</code> 属性,最后是请求的 <code>config</code> 参数。后者将优先于前者。这里是一个例子:</p>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line"><span class="comment">// 使用由库提供的配置的默认值来创建实例<br><span class="line"><span class="comment">// 此时超时配置的默认值是 `0`<br><span class="line"><span class="keyword">var instance = axios.create();<br><span class="line"><br><span class="line"><span class="comment">// 覆写库的超时默认值<br><span class="line"><span class="comment">// 现在,在超时前,所有请求都会等待 2.5 秒<br><span class="line">instance.defaults.timeout = <span class="number">2500;<br><span class="line"><br><span class="line"><span class="comment">// 为已知需要花费很长时间的请求覆写超时设置<br><span class="line">instance.get(<span class="string">'/longRequest', {<br><span class="line">timeout: <span class="number">5000<br><span class="line">});<br></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
</td>
</tr>
</tbody>
</table>
<h3 id="拦截器" class="article-heading">3.5.13、拦截器</h3>
<p>在请求或响应被 <code>then</code> 或 <code>catch</code> 处理前拦截它们。</p>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line"><span class="comment">// 添加请求拦截器<br><span class="line">axios.interceptors.request.use(<span class="function"><span class="keyword">function (<span class="params">config) {<br><span class="line"> <span class="comment">// 在发送请求之前做些什么<br><span class="line"> <span class="keyword">return config;<br><span class="line">}, <span class="function"><span class="keyword">function (<span class="params">error) {<br><span class="line"> <span class="comment">// 对请求错误做些什么<br><span class="line"> <span class="keyword">return <span class="built_in">Promise.reject(error);<br><span class="line">});<br><span class="line"><br><span class="line"><span class="comment">// 添加响应拦截器<br><span class="line">axios.interceptors.response.use(<span class="function"><span class="keyword">function (<span class="params">response) {<br><span class="line"> <span class="comment">// 对响应数据做点什么<br><span class="line"> <span class="keyword">return response;<br><span class="line">}, <span class="function"><span class="keyword">function (<span class="params">error) {<br><span class="line"> <span class="comment">// 对响应错误做点什么<br><span class="line"> <span class="keyword">return <span class="built_in">Promise.reject(error);<br><span class="line">});<br></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></pre>
</td>
</tr>
</tbody>
</table>
<p>如果你想在稍后移除拦截器,可以这样:</p>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line"><span class="keyword">const myInterceptor = axios.interceptors.request.use(<span class="function"><span class="keyword">function (<span class="params">) {<span class="comment">/*...*/});<br><span class="line">axios.interceptors.request.eject(myInterceptor);<br></span></span></span></span></span></span></span></pre>
</td>
</tr>
</tbody>
</table>
<p>可以为自定义 axios 实例添加拦截器</p>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line"><span class="keyword">const instance = axios.create();<br><span class="line">instance.interceptors.request.use(<span class="function"><span class="keyword">function (<span class="params">) {<span class="comment">/*...*/});<br></span></span></span></span></span></span></span></pre>
</td>
</tr>
</tbody>
</table>
<h3 id="错误处理" class="article-heading">3.5.14、错误处理</h3>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line">axios.get(<span class="string">'/user/12345')<br><span class="line">.catch(<span class="function"><span class="keyword">function (<span class="params">error) {<br><span class="line"> <span class="keyword">if (error.response) {<br><span class="line"> <span class="comment">// The request was made and the server responded with a status code<br><span class="line"> <span class="comment">// that falls out of the range of 2xx<br><span class="line"> <span class="built_in">console.log(error.response.data);<br><span class="line"> <span class="built_in">console.log(error.response.status);<br><span class="line"> <span class="built_in">console.log(error.response.headers);<br><span class="line"> } <span class="keyword">else <span class="keyword">if (error.request) {<br><span class="line"> <span class="comment">// The request was made but no response was received<br><span class="line"> <span class="comment">// `error.request` is an instance of XMLHttpRequest in the browser and an instance of<br><span class="line"> <span class="comment">// http.ClientRequest in node.js<br><span class="line"> <span class="built_in">console.log(error.request);<br><span class="line"> } <span class="keyword">else {<br><span class="line"> <span class="comment">// Something happened in setting up the request that triggered an Error<br><span class="line"> <span class="built_in">console.log(<span class="string">'Error', error.message);<br><span class="line"> }<br><span class="line"> <span class="built_in">console.log(error.config);<br><span class="line">});<br></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></pre>
</td>
</tr>
</tbody>
</table>
<p>Y可以使用 <code>validateStatus</code> 配置选项定义一个自定义 HTTP 状态码的错误范围。</p>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line">axios.get(<span class="string">'/user/12345', {<br><span class="line">validateStatus: <span class="function"><span class="keyword">function (<span class="params">status) {<br><span class="line"> <span class="keyword">return status < <span class="number">500; <span class="comment">// Reject only if the status code is greater than or equal to 500<br><span class="line">}<br><span class="line">})<br></span></span></span></span></span></span></span></span></span></span></span></span></pre>
</td>
</tr>
</tbody>
</table>
<h3 id="取消" class="article-heading">3.5.15、取消</h3>
<p>使用 <em>cancel token</em> 取消请求</p>
<blockquote>
<p>Axios 的 cancel token API 基于cancelable promises proposal,它还处于第一阶段。</p>
</blockquote>
<p>可以使用 <code>CancelToken.source</code> 工厂方法创建 cancel token,像这样:</p>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line"><span class="keyword">const CancelToken = axios.CancelToken;<br><span class="line"><span class="keyword">const source = CancelToken.source();<br><span class="line"><br><span class="line">axios.get(<span class="string">'/user/12345', {<br><span class="line">cancelToken: source.token<br><span class="line">}).catch(<span class="function"><span class="keyword">function(<span class="params">thrown) {<br><span class="line"><span class="keyword">if (axios.isCancel(thrown)) {<br><span class="line"> <span class="built_in">console.log(<span class="string">'Request canceled', thrown.message);<br><span class="line">} <span class="keyword">else {<br><span class="line"> <span class="comment">// 处理错误<br><span class="line">}<br><span class="line">});<br><span class="line"><br><span class="line">axios.post(<span class="string">'/user/12345', {<br><span class="line">name: <span class="string">'new name'<br><span class="line">}, {<br><span class="line">cancelToken: source.token<br><span class="line">})<br><span class="line"><br><span class="line"><span class="comment">// 取消请求(message 参数是可选的)<br><span class="line">source.cancel(<span class="string">'Operation canceled by the user.');<br></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></pre>
</td>
</tr>
</tbody>
</table>
<p>还可以通过传递一个 executor 函数到 <code>CancelToken</code> 的构造函数来创建 cancel token:</p>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line"><span class="keyword">const CancelToken = axios.CancelToken;<br><span class="line"><span class="keyword">let cancel;<br><span class="line"><br><span class="line">axios.get(<span class="string">'/user/12345', {<br><span class="line">cancelToken: <span class="keyword">new CancelToken(<span class="function"><span class="keyword">function <span class="title">executor(<span class="params">c) {<br><span class="line"> <span class="comment">// executor 函数接收一个 cancel 函数作为参数<br><span class="line"> cancel = c;<br><span class="line">})<br><span class="line">});<br><span class="line"><br><span class="line"><span class="comment">// cancel the request<br><span class="line">cancel();<br></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
</td>
</tr>
</tbody>
</table>
<blockquote>
<p>注意: 可以使用同一个 cancel token 取消多个请求</p>
</blockquote>
<h3 id="使用-application-x-www-form-urlencoded-format" class="article-heading">3.5.16、使用 application/x-www-form-urlencoded format</h3>
<p>默认情况下,axios将JavaScript对象序列化为JSON。 要以application / x-www-form-urlencoded格式发送数据,您可以使用以下选项之一。</p>
<h4 id="浏览器" class="article-heading">浏览器</h4>
<p>在浏览器中,您可以使用URLSearchParams API,如下所示:</p>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line"><span class="keyword">const params = <span class="keyword">new URLSearchParams();<br><span class="line">params.append(<span class="string">'param1', <span class="string">'value1');<br><span class="line">params.append(<span class="string">'param2', <span class="string">'value2');<br><span class="line">axios.post(<span class="string">'/foo', params);<br></span></span></span></span></span></span></span></span></span></span></span></pre>
</td>
</tr>
</tbody>
</table>
<blockquote>
<p>请注意,所有浏览器都不支持URLSearchParams(请参阅caniuse.com),但可以使用polyfill(确保填充全局环境)。</p>
</blockquote>
<p>或者,您可以使用qs库编码数据:</p>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line"><span class="keyword">const qs = <span class="built_in">require(<span class="string">'qs');<br><span class="line">axios.post(<span class="string">'/foo', qs.stringify({ <span class="string">'bar': <span class="number">123 }));<br></span></span></span></span></span></span></span></span></pre>
</td>
</tr>
</tbody>
</table>
<p>或者以另一种方式(ES6),</p>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line"><span class="keyword">import qs <span class="keyword">from <span class="string">'qs';<br><span class="line"><span class="keyword">const data = { <span class="string">'bar': <span class="number">123 };<br><span class="line"><span class="keyword">const options = {<br><span class="line">method: <span class="string">'POST',<br><span class="line">headers: { <span class="string">'content-type': <span class="string">'application/x-www-form-urlencoded' },<br><span class="line">data: qs.stringify(data),<br><span class="line">url,<br><span class="line">};<br><span class="line">axios(options);<br></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
</td>
</tr>
</tbody>
</table>
<h4 id="Node-js" class="article-heading">Node.js</h4>
<p>在node.js中,您可以使用querystring模块,如下所示:</p>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line"><span class="keyword">const querystring = <span class="built_in">require(<span class="string">'querystring');<br><span class="line">axios.post(<span class="string">'http://something.com/', querystring.stringify({ <span class="attr">foo: <span class="string">'bar' }));<br></span></span></span></span></span></span></span></span></pre>
</td>
</tr>
</tbody>
</table>
<p>您也可以使用qs库。</p>
<h4 id="Semver" class="article-heading">Semver</h4>
<p>在axios达到1.0版本之前,破坏性更改将以新的次要版本发布。 例如0.5.1和0.5.4将具有相同的API,但0.6.0将具有重大变化。</p>
<h4 id="Promises" class="article-heading">Promises</h4>
<p>axios 依赖原生的 ES6 Promise 实现而被支持. 如果你的环境不支持 ES6 Promise,你可以使用 polyfill.</p>
<h3 id="TypeScript" class="article-heading">3.5.17、TypeScript</h3>
<p>axios包括TypeScript定义。</p>
<table>
<tbody>
<tr>
<td class="code">
<pre><span class="line"><span class="keyword">import axios <span class="keyword">from <span class="string">'axios';<br><span class="line">axios.get(<span class="string">'/user?ID=12345');<br></span></span></span></span></span></span></pre>
</td>
</tr>
</tbody>
</table>
<h3> 3.5.18、简单应用</h3>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;
import axios from </span>"axios"<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 StudentListAxios extends Component {
state </span>=<span style="color: rgba(0, 0, 0, 1)"> {
students: [],
};
componentDidMount() {
axios
.get(</span>"http://127.0.0.1:8081/api/students"<span style="color: rgba(0, 0, 0, 1)">)
.then((res) </span>=><span style="color: rgba(0, 0, 0, 1)"> {
console.log(res);
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({ students: res.data });
})
.</span><span style="color: rgba(0, 0, 255, 1)">catch</span>((error) =><span style="color: rgba(0, 0, 0, 1)"> alert(error));
}
handleDelete </span>= (e) =><span style="color: rgba(0, 0, 0, 1)"> {
const that </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">;
let id </span>=<span style="color: rgba(0, 0, 0, 1)"> e.target.id;
axios
.</span><span style="color: rgba(0, 0, 255, 1)">delete</span>(`http:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">127.0.0.1:8081/api/students/${id}`)</span>
.then((res) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (res.data === 1<span style="color: rgba(0, 0, 0, 1)">) {
let newStudent </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.students.filter(
(item) </span>=> item.studentId !==<span style="color: rgba(0, 0, 0, 1)"> id
);
that.setState({
students: newStudent,
});
}
})
.</span><span style="color: rgba(0, 0, 255, 1)">catch</span>((error) =><span style="color: rgba(0, 0, 0, 1)"> alert(error));
e.preventDefault();
};
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<h2>学生列表</h2>
<table border="1" width="100%">
<tbody>
<tr>
<th>学号</th>
<th>姓名</th>
<th>性别</th>
<th>操作</th>
</tr>
{<span style="color: rgba(0, 0, 255, 1)">this</span>.state.students.map((stu) =><span style="color: rgba(0, 0, 0, 1)"> (
</span><tr key={stu.studentId}>
<td>{stu.studentId}</td>
<td>{stu.studentName}</td>
<td>{stu.classId}</td>
<td>
<a href="# " id={stu.studentId} onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleDelete}><span style="color: rgba(0, 0, 0, 1)">
删除
</span></a>
</td>
</tr>
<span style="color: rgba(0, 0, 0, 1)"> ))}
</span></tbody>
</table>
</div>
<span style="color: rgba(0, 0, 0, 1)"> );
}
}</span></pre>
</div>
<p>结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230317152359657-1567161571.png" alt="" loading="lazy"></p>
<h3>3.1.19、统一URL</h3>
<p>react axios设置统一请求地址 / react axios设置开发 & 生产环境不同接口地址 /react 封装axios</p>
<div>
<ul>
<li>思路:
<ul>
<li>
<p>axios设置公共请求头:通过axios配置,设置统一的baseURL;</p>
</li>
<li>
<p>开发环境 & 生产环境接口地址: 通过脚手架提供的环境变量来解决:</p>
</li>
</ul>
</li>
</ul>
<hr>
<ul>
<li>
<p>1.在项目根目录创建 .env.development文件,配置REACT_APP_URL = http://localhost:8080<br>
在开发期间生效,npm start的时候自动读取.env.development文件中的环境变量来设置接口;<br>
在项目根目录创建 .env.production文件,配置REACT_APP_URL = 线上地址,<br>
npm run build的时候自动读取.env.development文件中的环境变量来设置接口</p>
</li>
<li>
<p>2.在该文件中添加环境变量REACT_APP_URL(注意: 环境变量约定以REACT_APP_ 开头)</p>
</li>
<li>
<p>3.设置REACT_APP_URL的值默认为 http://localhost:8080,<br>
并重启脚手架</p>
</li>
<li>
<p>4.创建utils/url.js文件, 在url.js中创建BASE_URL变量,设置其值为process.env.REACT_APP_URL</p>
</li>
<li>
<p>5.在页面中导入url文件,并修改请求路径,图片路径等等;</p>
</li>
</ul>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230317155222456-1907185696.png" alt="" width="839" height="469" loading="lazy"></p>
<div class="image-package">
<div class="image-container">
<div class="image-view" data-width="901" data-height="197"><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230317155245842-1670813101.png" alt="" width="853" height="178" loading="lazy"></div>
</div>
</div>
<p>如果URL为undefined则有可能是以下原因:</p>
<p>检查是否安装依赖process模块,可以使用npm install process命令\</p>
<p>检查创建的.env.文件名是否在根目录下</p>
<p>如果是Vue项目:<br>检查创建的配置文件里属性名是否以VUE_APP_开头,例如VUE_APP_API</p>
<p>如果是React项目:<br>检查创建的配置文件里属性名是否以REACT_APP_开头,例如REACT_APP_URL</p>
<p>注意配置文件里的属性值是string类型</p>
<p>检查使用属性是否写为process.env.属性名,例如process.env.VUE_APP_API</p>
<p>如果以上都没有问题,需要执行npm run serve命令才能使配置文件生效</p>
<p>可以找个页面做控制台打印console.log(process.env);看看当前配置里是否有VUE_APP_API当前你定义的属性</p>
<p>axios官方文档:http://www.axios-js.com/docs/</p>
<div class="image-package">
<div class="image-container">
<div class="image-view" data-width="889" data-height="652"><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230317155338218-861404558.png" alt="" width="919" height="671" loading="lazy"></div>
</div>
</div>
<p>到第6步骤,axios请求接口成功,但实际上每个页面的axios.get(都添加了BASE_URL);<br>
接下来封装一个简易的axios,只需在一个js文件中设置统一请求路径</p>
<div class="image-package">
<div class="image-container">
<div class="image-view" data-width="896" data-height="568"><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230317155403129-1363450655.png" alt="" loading="lazy"></div>
</div>
<div class="image-caption">6 创建api.js,封装axios</div>
</div>
<div class="image-package">
<div class="image-container">
<div class="image-view" data-width="786" data-height="676"><img src="//upload-images.jianshu.io/upload_images/20409039-abed03679ddd0741.png?imageMogr2/auto-orient/strip|imageView2/2/w/786/format/webp" data-original-src="//upload-images.jianshu.io/upload_images/20409039-abed03679ddd0741.png" data-original-width="786" data-original-height="676" data-original-format="image/png" data-original-filesize="54631" data-image-index="4"></div>
</div>
<div class="image-caption">在页面中导入封装好的axios即可</div>
</div>
<h6>扩展: 封装react axios拦截器统一添加token</h6>
<div class="cnblogs_code">
<pre>import axios from 'axios'<span style="color: rgba(0, 0, 0, 1)">
import { BASE_URL } from </span>'./url'
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> import {HashRouter} from 'react-router-dom' //如果使用的是hash路由类型,使用这个</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> const router = new HashRouter()</span>
<span style="color: rgba(0, 0, 0, 1)">
import {BrowserRouter} from </span>'react-router-dom'<span style="color: rgba(0, 0, 0, 1)">
const router </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> BrowserRouter()
const API </span>=<span style="color: rgba(0, 0, 0, 1)"> axios.create({
baseURL: BASE_URL
})
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 添加请求拦截器</span>
API.interceptors.request.use(config=><span style="color: rgba(0, 0, 0, 1)">{
const { url } </span>=<span style="color: rgba(0, 0, 0, 1)"> config;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">当url不是/login 或 不是/register请求路由时(这两个路由不需要token), 请求头添加token </span>
<span style="color: rgba(0, 0, 255, 1)">if</span>(!url.startsWith('/login') ||!url.startsWith('/register'<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)">当url以/user开头的时候,</span>
config.headers.Authorization = localStorage.getItem('token'<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)"> config
})
API.interceptors.response.use(response </span>=><span style="color: rgba(0, 0, 0, 1)"> {
const { status }</span>= response.data; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">这里的response数据结构不一样,直接打印出来看,参照后端返回的结果</span>
<span style="color: rgba(0, 0, 255, 1)">if</span>(status === 400 || status === 401 || status === 402 || 403<span style="color: rgba(0, 0, 0, 1)">){
localStorage.removeItem(</span>'token'<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)">当token超时or失效 403账号无权限的时候直接跳转到/login页重新登录</span>
router.history.push('/login'<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)"> response
})
export { API } </span></pre>
</div>
<p> studentApi.js</p>
<div class="cnblogs_code">
<pre>import axios from "axios"<span style="color: rgba(0, 0, 0, 1)">;
import { BASE_URL } from </span>"../utils/url"<span style="color: rgba(0, 0, 0, 1)">;
axios.defaults.baseURL </span>=<span style="color: rgba(0, 0, 0, 1)"> BASE_URL;
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> testUrl() {
console.log(axios.defaults.baseURL);
}</span></pre>
</div>
<p>studentListAxiosPro.js</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;
import axios from </span>"axios"<span style="color: rgba(0, 0, 0, 1)">;
import testUrl from </span>"./api/studentApi"<span style="color: rgba(0, 0, 0, 1)">;
testUrl();</span></pre>
</div>
<p> </p>
</div>
<p>运行结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230317155634216-923951635.png" alt="" width="471" height="158" loading="lazy"></p>
<h3>3.1.20、封装请求</h3>
<div>
<div><strong>一、下载</strong></div>
<div>npm i axios</div>
<div>yarn add axios</div>
<div><strong>二、创建 api 文件夹</strong></div>
<div>在项目 src 目录中创建一个 api 目录,用来存放所有的请求接口。</div>
<div><strong>三、创建请求模块文件</strong></div>
<div>我们以获取学生数据为例。</div>
<div>在 api 目录中创建一个 studentApi.js 文件,该文件中用来设置所有关于学生的请求:</div>
<div>
<div class="cnblogs_code">
<pre>import axios from "axios"<span style="color: rgba(0, 0, 0, 1)">;
export const getStudentAsync </span>= (params) =><span style="color: rgba(0, 0, 0, 1)">
axios.get(</span>"/api/students"<span style="color: rgba(0, 0, 0, 1)">, {
params,
});
export const deleteStudentAsync </span>= (studentId, config) =><span style="color: rgba(0, 0, 0, 1)">
axios.</span><span style="color: rgba(0, 0, 255, 1)">delete</span>(`/students/${studentId}`, config);</pre>
</div>
</div>
<div><strong>四、封装 axios</strong></div>
<div>在项目 src 目录中创建一个 utils 目录,用来存放工具类的文件,我们将 axios 的封装文件 axios.js 也可以放在里面。</div>
<div>
<div class="cnblogs_code">
<pre>import axios from "axios"<span style="color: rgba(0, 0, 0, 1)">;
import { BASE_URL } from </span>"./url"<span style="color: rgba(0, 0, 0, 1)">;
axios.defaults.baseURL </span>=<span style="color: rgba(0, 0, 0, 1)"> BASE_URL;
</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)">axios.interceptors.response.use(
(res) </span>=> res.data, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 拦截到响应对象,将响应对象的 data 属性返回给调用的地方</span>
(err) =><span style="color: rgba(0, 0, 0, 1)"> Promise.reject(err)
);</span></pre>
</div>
</div>
<div>最后在 index.js 中引入该文件:</div>
<div>import './utils/axios.js';</div>
<div><strong>五、组件中调用 api 接口</strong></div>
<div>例如在学生列表的组件中,我们要调用封装好的接口:</div>
<div>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;
import { getStudentAsync, deleteStudentAsync } from </span>"./api/studentApi"<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 StudentListAxiosPro extends Component {
state </span>=<span style="color: rgba(0, 0, 0, 1)"> {
students: [],
};
componentDidMount() {
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.getStudents();
}
getStudents </span>= async () =><span style="color: rgba(0, 0, 0, 1)"> {
let res </span>=<span style="color: rgba(0, 0, 0, 1)"> await getStudentAsync();
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({
students: res,
});
};
handleDelete </span>= async (e) =><span style="color: rgba(0, 0, 0, 1)"> {
const that </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">;
let id </span>=<span style="color: rgba(0, 0, 0, 1)"> e.target.id;
let res </span>=<span style="color: rgba(0, 0, 0, 1)"> await deleteStudentAsync(id);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (res === 1<span style="color: rgba(0, 0, 0, 1)">) {
let newStudent </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.students.filter(
(item) </span>=> item.studentId !==<span style="color: rgba(0, 0, 0, 1)"> id
);
that.setState({
students: newStudent,
});
}
e.preventDefault();
};
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<h2>学生列表</h2>
<table border="1" width="100%">
<tbody>
<tr>
<th>学号</th>
<th>姓名</th>
<th>性别</th>
<th>操作</th>
</tr>
{<span style="color: rgba(0, 0, 255, 1)">this</span>.state.students.map((stu) =><span style="color: rgba(0, 0, 0, 1)"> (
</span><tr key={stu.studentId}>
<td>{stu.studentId}</td>
<td>{stu.studentName}</td>
<td>{stu.classId}</td>
<td>
<a href="# " id={stu.studentId} onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleDelete}><span style="color: rgba(0, 0, 0, 1)">
删除
</span></a>
</td>
</tr>
<span style="color: rgba(0, 0, 0, 1)"> ))}
</span></tbody>
</table>
</div>
<span style="color: rgba(0, 0, 0, 1)"> );
}
}</span></pre>
</div>
<p> 结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230317162855854-289605482.png" alt="" loading="lazy"></p>
</div>
</div>
<p>统一的错误与数据处理</p>
<div class="cnblogs_code">
<pre>import axios from "axios"<span style="color: rgba(0, 0, 0, 1)">;
axios.defaults.baseURL </span>=<span style="color: rgba(0, 0, 0, 1)"> process.env.REACT_APP_URL;
</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)">axios.interceptors.response.use(
(res) </span>=> res.data, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 拦截到响应对象,将响应对象的 data 属性返回给调用的地方</span>
(err) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(err);
alert(</span>"发生了错误:" + err.message + "\n" +<span style="color: rgba(0, 0, 0, 1)"> err.stack);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Promise.reject(err);
}
);</span></pre>
</div>
<h2>3.6、Fetch</h2>
<h3>3.6.1、基本使用</h3>
<ol>
<li>fetch:原生函数,React内置,不再使用 XMLHttpRequest 对象提交 ajax 请求</li>
<li>老版本浏览器可能不支持</li>
</ol>
<div class="cnblogs_code">
<pre>fetch(url).then(res =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> res.json() <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">res不是需要的请求数据</span>
}).then(data =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(data) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">data是请求数据</span>
}).<span style="color: rgba(0, 0, 255, 1)">catch</span>(e =><span style="color: rgba(0, 0, 0, 1)">{
console.log(e) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">e是异常信息</span>
})</pre>
</div>
<h3>3.6.2、参数说明</h3>
<p>(1)fetch的返回值是一个promise对象</p>
<p>(2)options表示对象格式的参数</p>
<p>method:HTTP请求方式,默认是GET</p>
<p>body:请求的参数</p>
<p>若发送的是json格式参数,则需要使用JSON.stringify({id:'123'})</p>
<div class="cnblogs_code">
<pre>fetch(`${HOST}:${PORT}/bookapi/<span style="color: rgba(0, 0, 0, 1)">books`,{
method:</span>'get'<span style="color: rgba(0, 0, 0, 1)">,
headers:{
</span>'Content-Type':'application/json'<span style="color: rgba(0, 0, 0, 1)">
}
}).then(res</span>=><span style="color: rgba(0, 0, 0, 1)">{
console.log(res)
</span><span style="color: rgba(0, 0, 255, 1)">return</span> res.json()<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">将服务器发送的响应头信息返回给浏览器</span>
}).then(data=>{ <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)"> _this.setState({
data: data
})
}).</span><span style="color: rgba(0, 0, 255, 1)">catch</span>(err=><span style="color: rgba(0, 0, 0, 1)">{
console.log(err)
})
}</span></pre>
</div>
<p>headers:HTTP请求头,可以设置响应头的信息</p>
<p>因为一般使用JSON数据格式,所以设置ContentType为application/json</p>
<p>credentials:默认为omit,忽略的意思,也就是不带cookie还有两个参数,same-origin,意思就是同源请求带cookie;include,表示无论跨域还是同源请求都会带cookie</p>
<h3>3.6.3、总结</h3>
<p>(1)fetch更底层,只处理请求失败的错误,对于400、500等异常它任务是请求成功的</p>
<p>(2)fetch是XMLHttpRequest对象的代理(替代方案),运行与 XMLHttpRequest对象没有关系</p>
<p>(3)axios是XMLHttpRequest对象的封装,底层的实现依然使用的是XMLHttpRequest对象</p>
<h3>3.6.4、案例</h3>
<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 StudentList extends Component {
state </span>=<span style="color: rgba(0, 0, 0, 1)"> {
students: [],
};
componentDidMount() {
fetch(</span>"http://127.0.0.1:8081/api/students"<span style="color: rgba(0, 0, 0, 1)">, {})
.then((res) </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)"> res.json();
})
.then((data) </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({ students: data });
})
.</span><span style="color: rgba(0, 0, 255, 1)">catch</span>((error) =><span style="color: rgba(0, 0, 0, 1)"> alert(error));
}
handleDelete </span>= (e) =><span style="color: rgba(0, 0, 0, 1)"> {
const that </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">;
let id </span>=<span style="color: rgba(0, 0, 0, 1)"> e.target.id;
fetch(`http:</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">127.0.0.1:8081/api/students/${id}`, {</span>
method: "delete"<span style="color: rgba(0, 0, 0, 1)">,
})
.then((res) </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)"> res.json();
})
.then((data) </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)">;
let newStudent </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.students.filter(
(item) </span>=> item.studentId !==<span style="color: rgba(0, 0, 0, 1)"> id
);
</span><span style="color: rgba(0, 0, 255, 1)">debugger</span><span style="color: rgba(0, 0, 0, 1)">;
that.setState({
students: newStudent,
});
})
.</span><span style="color: rgba(0, 0, 255, 1)">catch</span>((error) =><span style="color: rgba(0, 0, 0, 1)"> alert(error));
e.preventDefault();
};
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<h2>学生列表</h2>
<table border="1" width="100%">
<tbody>
<tr>
<th>学号</th>
<th>姓名</th>
<th>性别</th>
<th>操作</th>
</tr>
{<span style="color: rgba(0, 0, 255, 1)">this</span>.state.students.map((stu) =><span style="color: rgba(0, 0, 0, 1)"> (
</span><tr key={stu.studentId}>
<td>{stu.studentId}</td>
<td>{stu.studentName}</td>
<td>{stu.classId}</td>
<td>
<a href="# " id={stu.studentId} onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleDelete}><span style="color: rgba(0, 0, 0, 1)">
删除
</span></a>
</td>
</tr>
<span style="color: rgba(0, 0, 0, 1)"> ))}
</span></tbody>
</table>
</div>
<span style="color: rgba(0, 0, 0, 1)"> );
}
}</span></pre>
</div>
<p>结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230317150414679-257517768.png" alt="" width="976" height="232" loading="lazy"></p>
<p id="waring-log"><strong>href="#"的错误</strong></p>
<table>
<tbody>
<tr>
<td class="gutter">
<pre><span class="line">1<br><span class="line">2<br></span></span></pre>
</td>
<td class="code">
<pre><span class="line">Line 10:33:The href attribute requires a valid value to be accessible. Provide a valid, navigable address as the href value. If you cannot provide a valid href, but still need the element to resemble a<br><span class="line">link, use a button and change it with appropriate styles. Learn more: https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/anchor-is-valid.md jsx-a11y/anchor-is-valid<br></span></span></pre>
</td>
</tr>
</tbody>
</table>
<p id="解决"><strong>解决</strong></p>
<p id="在-eslint-的配置加上的-rules-加上"><strong>在 eslint 的配置加上的 rules 加上</strong></p>
<table>
<tbody>
<tr>
<td class="gutter">
<pre><span class="line">1<br></span></pre>
</td>
<td class="code">
<pre><span class="line">"jsx-a11y/anchor-is-valid": "off"<br></span></pre>
</td>
</tr>
</tbody>
</table>
<p>例如我的 create-react-app 项目的.eslintrc.json 的配置是:</p>
<table>
<tbody>
<tr>
<td class="gutter">
<pre><span class="line">1<br><span class="line">2<br><span class="line">3<br><span class="line">4<br><span class="line">5<br><span class="line">6<br></span></span></span></span></span></span></pre>
</td>
<td class="code">
<pre><span class="line">{<br><span class="line"><span class="attr">"extends": <span class="string">"react-app",<br><span class="line"><span class="attr">"rules": {<br><span class="line"> <span class="attr">"jsx-a11y/anchor-is-valid": <span class="string">"off"<br><span class="line">}<br><span class="line">}<br></span></span></span></span></span></span></span></span></span></span></span></pre>
</td>
</tr>
</tbody>
</table>
<p id="a-标签改成"><strong>a 标签改成</strong></p>
<table>
<tbody>
<tr>
<td class="gutter">
<pre><span class="line">1<br></span></pre>
</td>
<td class="code">
<pre><span class="line"><span class="tag"><<span class="name">a <span class="attr">href=<span class="string">"# ">{text}<span class="tag"></<span class="name">a><br></span></span></span></span></span></span></span></pre>
</td>
</tr>
</tbody>
</table>
<blockquote>
<p>注意:#后面有一个空格</p>
</blockquote>
<p id="重启">重启</p>
<h2>3.6、组件与服务器通信</h2>
<h3>3.6.1、组件挂载阶段通信</h3>
<p>componentDidMount是调用服务器API最安全的地方,也是React官方推荐的进行服务器通信的地方。除了在componentDidMount,在componentWillMount中进行服务器通信也是比较常见的一种方式。</p>
<p>componentWillMount会在组件被挂载前调用,因此从时间上来讲,componentWillMount中执行服务器通信要早于componentDidMount。但两者执行的时间差微乎其微,完全可以忽略不计。</p>
<p>componentDidMount是执行组件与服务器通信的最佳地方,原因:</p>
<p>在componentDidMount执行服务器通信可以保证获取到数据时,组件已经处于挂载状态,此时可以操作DOM</p>
<p>当组件在服务器端渲染时,componentWillMount会执行两次,一个在服务器端,一次在浏览器端,而componentDidMount能保证在任何情况下只会被调用一次,从而不会发送多余的数据请求。</p>
<p>服务器端使用Spring Boot:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230309152255289-943851226.png" alt="" loading="lazy"></p>
<p>接口提供JSON数据:</p>
<p>http://localhost:8081/api/students</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230309152324812-727018180.png" alt="" loading="lazy"></p>
<p> 前后端分离,前端使用React+Axios从服务器获取数据,展示学生信息:</p>
<p>在前端项目中依赖axios</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230309152718322-40562468.png" alt="" loading="lazy"></p>
<p>创建StudentList组件</p>
<p> </p>
<h3>3.6.2、组件更新阶段通信</h3>
<p>例如,组件需要以props中某个属性作为与服务器通信的请求采纳数,当这个属性值发生更新时,组件自然需要重新余服务器通信。</p>
<p>componentWillReceiveProps(nextProps){<br> if(nextProps.category !== this.props.category) {<br> // fetch<br> }<br> }</p>
<p>componentWillReceiveProps的执行并不能说明props一定发生了修改</p>
<p> </p>
<p> </p>
<h1>四、视频</h1>
<p> https://www.bilibili.com/video/BV1Vb411D7qa/?share_source=copy_web&vd_source=475a31f3c5d6353a782007cd4c638a8a</p>
<h1>五、作业</h1>
<p>1、定义一个对话框组件,要求显示在body尾部标签内,使用portal技术,卸载时要求删除容器</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230301110222925-1581217551.png" alt="" width="892" height="376" class="medium-zoom-image" loading="lazy"></p>
<p>2、使用Ant Design Mobile完成如下作业</p>
<p>1、使用React脚手架创建项目或使用Vite创建React项目、项目中安装AntDesignMobile,使用AntDesignMobile完成一个类似论坛页面,效果如下图:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230309163252288-456296047.png" alt=""></p>
<p> 2、要求使用React的列表数据渲染方法渲染组件。Array.map()方法 3、(扩展)根据帖子中的图片数据动态改变图片的显示方式。 </p>
<p>(1) 帖子只有一张图时:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230309163309963-959367093.png" alt="" loading="lazy"></p>
<p> (2) 有两张或四张图时:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230309163335152-200099924.png" alt="" loading="lazy"></p>
<p> (3) 三张或超过六张时:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230309163348763-818370632.png" alt="" loading="lazy"></p>
<p>3、完成radio、file的受控组件示例。</p>
<p>4、完成一个用户注册示例,尽量多的使用到各种表单元素。</p>
<p>5、使用微服务完成一个论坛的后台,2张表,帖子管理,用户管理。</p>
<p>6、使用fetch调用微服务暴露的接口。</p>
<p>7、使用axios调用微服务暴露的接口,要求封装代码。</p><br><br>
来源:https://www.cnblogs.com/best/p/17152046.html
頁:
[1]