• React学习笔记


    React学习笔记

    起步

    中文官网:https://react.docschina.org/

    使用脚手架

    安装create-react-app

    npm install -g create-react-app //建议用cnpm
    
    • 1

    确定要创建项目的目录

    npx create-react-app my-app
    cd my-app
    npm start
    
    • 1
    • 2
    • 3

    删除掉新项目中 src/ 文件夹下的所有文件(不要删除src)

    cd my-app
    cd src
    # 如果你使用 Mac 或 Linux:
    rm -f *
    # 如果你使用 Windows:
    del *
    # 然后回到项目文件夹
    cd ..
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    不使用脚手架

    <!--引入react核心库-->
    <script type="text/javascript" src="../js/eact.development.js></script>
    <!--引入react-dom,用于支持react操作DOM -->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!--引入babe1,用于将jsx转为js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <! --引入prop-types,用于对组件标签属性进行限制-->
    <script type="text/javascript" src="../js/prop-types.js"></script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    HelloWorld

    在src下创建一个index.js,添加如下代码:

    import React from 'react'
    import ReactDom from 'react-dom/client'
    
    const root = ReactDom.createRoot(document.getElementById("root"));
    root.render(<h1>Hello World</h1>);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在终端下输入:npm start,就可以看见网页中的HelloWorld了。

    JSX介绍

    JSX,是一个 JavaScript 的语法扩展。我们建议在 React 中配合使用 JSX,JSX 可以很好地描述 UI 应该呈现出它应有交互的本质形式。JSX 可能会使人联想到模板语言,但它具有 JavaScript 的全部功能。

    声明一个变量

    const element = <h1>Hello, world!</h1>;
    
    • 1

    JSX 中嵌入表达式

    const name = 'Josh Perez';
    const element = <h1>Hello, {name}</h1>;
    
    • 1
    • 2

    在 JSX 语法中,你可以在大括号内放置任何有效的JavaScript表达式。

    JSX注释写法

    {/*
    sss
    */
    }
    • 1

    JSX 也是一个表达式

    在编译之后,JSX 表达式会被转为普通 JavaScript 函数调用,并且对其取值后得到 JavaScript 对象。

    function getGreeting(user) {
      if (user) {
        return <h1>Hello, {formatName(user)}!</h1>;  
      }
      return <h1>Hello, Stranger.</h1>;}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    JSX 中指定属性

    你可以通过使用引号,来将属性值指定为字符串字面量:

    const element = <a href="https://www.reactjs.org"> link </a>;
    
    • 1

    也可以使用大括号,来在属性值中插入一个 JavaScript 表达式:

    const element = <img src={user.avatarUrl}></img>;
    
    • 1
    ** 警告:**
    
    因为 JSX 语法上更接近 JavaScript 而不是 HTML,所以 React DOM 使用 camelCase(小驼峰命名)来定义属性的名称,而不使用 HTML 属性名称的命名约定。
    
    例如,JSX 里的 class 变成了 className,而 `tabindex` 则变为 tabIndex 。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    JSX 指定子元素

    假如一个标签里面没有内容,你可以使用 /> 来闭合标签,就像 XML 语法一样:

    const element = <img src={user.avatarUrl} />;
    
    • 1

    JSX 标签里能够包含很多子元素:

    const element = (
      <div>
        <h1>Hello!</h1>
        <h2>Good to see you here.</h2>
      </div>
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    JSX 防止注入攻击

    你可以安全地在 JSX 当中插入用户输入内容

    const title = response.potentiallyMaliciousInput;
    // 直接使用是安全的:
    const element = <h1>{title}</h1>;
    
    • 1
    • 2
    • 3

    React DOM 在渲染所有输入内容之前,默认会进行转义。它可以确保在你的应用中,永远不会注入那些并非自己明确编写的内容。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止XSS攻击。

    JSX 表示对象

    const element = (
      <h1 className="greeting">
        Hello, world!
      </h1>
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5

    元素渲染

    元素是构成 React 应用的最小砖块。

    将一个元素渲染为 DOM

    想要将一个 React 元素渲染到根 DOM 节点中,只需把它们一起传入ReactDOM.createRoot():

    const root = ReactDOM.createRoot(
      document.getElementById('root')
    );
    const element = <h1>Hello, world</h1>;
    root.render(element);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    更新已渲染的元素

    React 元素是不可变对象。一旦被创建,你就无法更改它的子元素或者属性。一个元素就像电影的单帧:它代表了某个特定时刻的 UI。

    根据我们已有的知识,更新 UI 唯一的方式是创建一个全新的元素,并将其传入 root.render()

    考虑一个计时器的例子:

    const root = ReactDOM.createRoot(
      document.getElementById('root')
    );
    
    function tick() {
      const element = (
        

    Hello, world!

    It is {new Date().toLocaleTimeString()}.

    ); root.render(element);} setInterval(tick, 1000);
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    组件和Props

    组件,从概念上类似于 JavaScript 函数。它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素。

    函数组件与 class 组件

    定义组件最简单的方式就是编写 JavaScript 函数:

    function Welcome(props) {
      return 

    Hello, {props.name}

    ; }
    • 1
    • 2
    • 3

    你同时还可以使用 ES6 的 class 来定义组件:

    class Welcome extends React.Component {
      render() {
        return <h1>Hello, {this.props.name}</h1>;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    上述两个组件在 React 里是等效的。

    渲染组件

    当 React 元素为用户自定义组件时,它会将 JSX 所接收的属性(attributes)以及子组件(children)转换为单个对象传递给组件,这个对象被称之为 “props”。

    function Welcome(props) {  
        return <h1>Hello, {props.name}</h1>;
    }
    
    const root = ReactDOM.createRoot(document.getElementById('root'));
    const element = <Welcome name="Sara" />;
    root.render(element);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    让我们来回顾一下这个例子中发生了什么:

    1. 我们调用 root.render() 函数,并传入 作为参数。
    2. React 调用 Welcome 组件,并将 {name: 'Sara'} 作为 props 传入。
    3. Welcome 组件将

      Hello, Sara

      元素作为返回值。
    4. React DOM 将 DOM 高效地更新为

      Hello, Sara

    注意: 组件名称必须以大写字母开头。

    React 会将以小写字母开头的组件视为原生 DOM 标签。

    组合组件

    组件可以在其输出中引用其他组件。这就可以让我们用同一组件来抽象出任意层次的细节。按钮,表单,对话框,甚至整个屏幕的内容:在 React 应用程序中,这些通常都会以组件的形式表示。

    function Welcome(props) {
      return <h1>Hello, {props.name}</h1>;
    }
    
    function App() {
      return (
        <div>
          <Welcome name="Sara" />      
          <Welcome name="Cahal" />      
          <Welcome name="Edite" />    
        </div>
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    提取组件

    将组件拆分为更小的组件。

    function Comment(props) {
      return (
        <div className="Comment">
          <div className="UserInfo">
            <img className="Avatar"
              src={props.author.avatarUrl}
              alt={props.author.name}
            />
            <div className="UserInfo-name">
              {props.author.name}
            </div>
          </div>
          <div className="Comment-text">
            {props.text}
          </div>
          <div className="Comment-date">
            {formatDate(props.date)}
          </div>
        </div>
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    首先,我们将提取 Avatar 组件:

    function Avatar(props) {
      return (
        <img className="Avatar"      
          src={props.user.avatarUrl}      
    	  alt={props.user.name}    
    	/>  
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    我们现在针对 Comment 做些微小调整:

    function Comment(props) {
      return (
        <div className="Comment">
          <div className="UserInfo">
            <Avatar user={props.author} />       
    		<div className="UserInfo-name">
              {props.author.name}
            </div>
          </div>
          <div className="Comment-text">
            {props.text}
          </div>
          <div className="Comment-date">
            {formatDate(props.date)}
          </div>
        </div>
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    接下来,我们将提取 UserInfo 组件,该组件在用户名旁渲染 Avatar 组件:

    function UserInfo(props) {
      return (
        <div className="UserInfo">      
          <Avatar user={props.user} />      
    	  <div className="UserInfo-name">        
        	{props.user.name}      
    	  </div>    
    	</div>  
    	);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    进一步简化 Comment 组件:

    function Comment(props) {
      return (
        <div className="Comment">
          <UserInfo user={props.author} />      
          <div className="Comment-text">
            {props.text}
          </div>
          <div className="Comment-date">
            {formatDate(props.date)}
          </div>
        </div>
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Props 的只读性

    组件无论是使用函数声明还是通过 class 声明,都绝不能修改自身的 props。

    所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。

    在不违反上述规则的情况下,state 允许 React 组件随用户操作、网络响应或者其他变化而动态更改输出内容。

    props的限制

    class Person extends React.Component{
        state = {
            name:"",
            age:"",
            address:""
        }
    }
    // 对Person的属性进行限制
    Person.propTypes={
        name: PropTypes.string.isRequired, // 表示name的value必须为字符串,且必填
    }
    // 函数的默认属性为func 例speak: PropTypes.func
    // 对Person的属性进行默认设置
    Person.defaultProps={
        name: "zhangsan"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    简化写法:

    class Person extends React.Component{
        // 对Person的属性进行限制
    	static propTypes={
        	name: PropTypes.string.isRequired, // 表示name的value必须为字符串,且必填
    	}
        // 函数的默认属性为func 例speak: PropTypes.func
    	// 对Person的属性进行默认设置
    	static defaultProps={
        	name: "zhangsan"
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    State & 生命周期

    State 与 props 类似,但是 state 是私有的,并且完全受控于当前组件。

    正确地使用 State

    关于 setState() 你应该了解三件事:

    1、不要直接修改 State,而是应该使用 setState():

    2、State 的更新可能是异步的

    出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。

    因为 this.propsthis.state 可能会异步更新,所以你不要依赖他们的值来更新下一个状态。

    要解决这个问题,可以让 setState() 接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数:

    this.setState(
    	{data: data},
    	() => { console.log(data) }
    )
    
    • 1
    • 2
    • 3
    • 4

    3、State 的更新会被合并

    当你调用 setState() 的时候,React 会把你提供的对象合并到当前的 state。

    数据是向下流动的

    不管是父组件或是子组件都无法知道某个组件是有状态的还是无状态的,并且它们也并不关心它是函数组件还是 class 组件。

    这就是为什么称 state 为局部的或是封装的的原因。除了拥有并设置了它的组件,其他组件都无法访问。

    组件可以选择把它的 state 作为 props 向下传递到它的子组件中

    this指向问题

    1. 使用箭头函数,外部环境this指向谁,箭头函数中的this就指向谁
    2. .bind(this)方法,利用ES5中的bind方法,将事件处理程序中的this与组件实例绑定到一起
    3. class 的实例方法,利用箭头函数形式的class实例方法

    生命周期

    img

    refs和事件处理

    refs的简介

    不建议过度使用ref

    传统的获取标签属性:

    <input id="input" />
    const input = document.getElemtById("input");
    
    • 1
    • 2

    1、react的refs可以这样写:

    <input ref="input" />
    const {input} = this.refs; // 等同于 const input = this.refs.input
    
    • 1
    • 2

    但实际上,这是已经被淘汰的写法,React官方给出的解释是,字符串形式的ref,会有一定的效率问题,甚至在未来的版本中可能被移除

    2、回调ref的写法:

     {this.input1 = currentNode}} /> // 此时的this指向的是外部组件
    // 省略版写法  this.input1 = c } />
    const {input1} = this;
    
    • 1
    • 2
    • 3

    但是如果ref回调函数是以内联函数的方式定义的,在组件更新的时候,它会被调用两次,第一次传入的参数为null,第二次才会传入DOM,可以将ref的回调函数定义为class的绑定函数的形式避免上述问题,但在大多数情况下,这是无关紧要的。

    3、createRef API写法:

    
    MyRef = React.createRef();
    console.log(this.MyRef) // {current:input}
    
    • 1
    • 2
    • 3

    React.createRef() 调用后返回一个容器,该容器可以存储被ref标识的节点,但仅限一个,如果有相同的ref标识时,最后一个标识的会覆盖前面一个的。

    事件处理

    React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同:

    • React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
    • 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
    <button onClick={activateLasers}>  
        Activate Lasers
    </button>
    
    • 1
    • 2
    • 3

    在 React 中另一个不同点是你不能通过返回 false 的方式阻止默认行为。你必须显式地使用 preventDefault。例如,在 React 中,可能是这样的:

    function Form() {
      function handleSubmit(e) {
        e.preventDefault();    
        console.log('You clicked submit.');
      }
    
      return (
        <form onSubmit={handleSubmit}>
          <button type="submit">Submit</button>
        </form>
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    使用 React 时,你一般不需要使用 addEventListener 为已创建的 DOM 元素添加监听器。事实上,你只需要在该元素初始渲染的时候添加监听器即可。

    class Toggle extends React.Component {
      constructor(props) {
        super(props);
        this.state = {isToggleOn: true};
    
        // 为了在回调中使用 `this`,这个绑定是必不可少的    
        this.handleClick = this.handleClick.bind(this);  
      }
    
      handleClick() {    
          this.setState(prevState => ({      
              isToggleOn: !prevState.isToggleOn    
          }));  
      }
      render() {
        return (
          <button onClick={this.handleClick}>        
            {this.state.isToggleOn ? 'ON' : 'OFF'}
          </button>
        );
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    如果觉得使用 bind 很麻烦,这里有两种方式可以解决。你可以使用 public class fields 语法 :

    class LoggingButton extends React.Component {
      // This syntax ensures `this` is bound within handleClick.  
       handleClick = () => {    
           console.log('this is:', this);  
       };  
       render() {
        return (
          <button onClick={this.handleClick}>
            Click me
          </button>
        );
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    如果你没有使用 class fields 语法,你可以在回调中使用箭头函数:

    class LoggingButton extends React.Component {
      handleClick() {
        console.log('this is:', this);
      }
    
      render() {
        // 此语法确保 `handleClick` 内的 `this` 已被绑定。    
          return (      
             <button onClick={() => this.handleClick()}>        
              Click me
          	</button>
        );
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    向事件处理程序传递参数

    在循环中,通常我们会为事件处理函数传递额外的参数。例如,若 id 是你要删除那一行的 ID,以下两种方式都可以向事件处理函数传递参数:

    <button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
    <button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
    
    • 1
    • 2

    在这两种情况下,React 的事件对象 e 会被作为第二个参数传递。如果通过箭头函数的方式,事件对象必须显式的进行传递,而通过 bind 的方式,事件对象以及更多的参数将会被隐式的进行传递。

    条件渲染

    在 React 中,你可以创建不同的组件来封装各种你需要的行为。然后,依据应用的不同状态,你可以只渲染对应状态下的部分内容。

    观察这两个组件:

    function UserGreeting(props) {
      return <h1>Welcome back!</h1>;
    }
    
    function GuestGreeting(props) {
      return <h1>Please sign up.</h1>;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    再创建一个 Greeting 组件,它会根据用户是否登录来决定显示上面的哪一个组件。

    function Greeting(props) {
      const isLoggedIn = props.isLoggedIn;
      if (isLoggedIn) {    
          return <UserGreeting />;  
      }  
        return <GuestGreeting />;
    }
    const root = ReactDOM.createRoot(document.getElementById('root')); 
    // Try changing to isLoggedIn={true}:
    root.render(<Greeting isLoggedIn={false} />);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    元素变量

    你可以使用变量来储存元素。 它可以帮助你有条件地渲染组件的一部分,而其他的渲染部分并不会因此而改变。

    与运算符 &&

    通过花括号包裹代码,你可以在 JSX 中嵌入表达式。这也包括 JavaScript 中的逻辑与 (&&) 运算符。它可以很方便地进行元素的条件渲染:

    function Mailbox(props) {
      const unreadMessages = props.unreadMessages;
      return (
        

    Hello!

    {unreadMessages.length > 0 &&

    You have {unreadMessages.length} unread messages.

    }
    ); } const messages = ['React', 'Re: React', 'Re:Re: React']; const root = ReactDOM.createRoot(document.getElementById('root')); root.render();
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    三目运算符

    另一种内联条件渲染的方法是使用 JavaScript 中的三目运算符 condition ? true : false。

    阻止组件渲染

    在极少数情况下,你可能希望能隐藏组件,即使它已经被其他组件渲染。若要完成此操作,你可以让 render 方法直接返回 null,而不进行任何渲染。

    function WarningBanner(props) {
      if (!props.warn) {    
          return null;  
      }
      return (
        
    Warning!
    ); } class Page extends React.Component { constructor(props) { super(props); this.state = {showWarning: true}; this.handleToggleClick = this.handleToggleClick.bind(this); } handleToggleClick() { this.setState(state => ({ showWarning: !state.showWarning })); } render() { return (
    ); } } const root = ReactDOM.createRoot(document.getElementById('root')); root.render();
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    列表 & Key

    渲染多个组件

    你可以通过使用 {} 在 JSX 内构建一个元素集合。

    下面,我们使用 Javascript 中的 map() 方法来遍历 numbers 数组。将数组中的每个元素变成

  • 标签,最后我们将得到的数组赋值给 listItems

    const numbers = [1, 2, 3, 4, 5];
    const listItems = numbers.map((number) =>  
  • {number}
  • );
    • 1
    • 2

    然后,我们可以将整个 listItems 插入到

      元素中:

        {listItems}
      • 1

      基础列表组件

      我们可以把前面的例子重构成一个组件,这个组件接收 numbers 数组作为参数并输出一个元素列表。

      function NumberList(props) {
        const numbers = props.numbers;
        const listItems = numbers.map((number) =>    
          
    • {number}
    • ); return (
        {listItems}
      ); } const numbers = [1, 2, 3, 4, 5]; const root = ReactDOM.createRoot(document.getElementById('root')); root.render();
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13

      当我们运行这段代码,将会看到一个警告 a key should be provided for list items,意思是当你创建一个元素时,必须包括一个特殊的 key 属性。

      function NumberList(props) {
        const numbers = props.numbers;
        const listItems = numbers.map((number) =>
          
    • {number}
    • ); return (
        {listItems}
      ); }
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11

      key的简介

      key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。

      const numbers = [1, 2, 3, 4, 5];
      const listItems = numbers.map((number) =>
        
    • {number}
    • );
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

      一个元素的 key 最好是这个元素在列表中拥有的一个独一无二的字符串。通常,我们使用数据中的 id 来作为元素的 key:

      const todoItems = todos.map((todo) =>
        
    • {todo.text}
    • );
      • 1
      • 2
      • 3
      • 4
      • 5

      当元素没有确定 id 的时候,万不得已你可以使用元素索引 index 作为 key:

      const todoItems = todos.map((todo, index) =>
        // Only do this if items have no stable IDs  
        
    • {todo.text}
    • );
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

      用 key 提取组件

      元素的 key 只有放在就近的数组上下文中才有意义。

      比方说,如果你提取出一个 ListItem 组件,你应该把 key 保留在数组中的这个 元素上,而不是放在 ListItem 组件中的

    • 元素上。

      例子:不正确的使用 key 的方式

      function ListItem(props) {
        const value = props.value;
        return (
          // 错误!你不需要在这里指定 key:    
          
    • {value}
    • ); } function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => // 错误!元素的 key 应该在这里指定: ); return (
        {listItems}
      ); }
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21

      例子:正确的使用 key 的方式

      function ListItem(props) {
        // 正确!这里不需要指定 key:  
        return 
    • {props.value}
    • ;} function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => // 正确!key 应该在数组的上下文中被指定 ); return (
        {listItems}
      ); }
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16

      关于一些效率问题

      1. 虚拟DOM中key的作用:
        • 简单的说: key是虚拟DOM对象的标识,在更新显示时key起着极其重要的作用。
        • **详细的说:**当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DoM】。随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:
          • 旧虚拟DOM中找到了与新虚拟DOM相同的key:
            • (1).若虚拟DOM中内容没变,直接使用之前的真实DOM
            • (2).若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
      2. 用index作为key可能会引发的问题:
        • 若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新==>界面效果没问题,但效率低。
        • 如果结构中还包含输入类的DOM:会产生错误DOM更新==>界面有问题。
        • 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
      3. 开发中如何选择key?
        • 最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。
        • 如果确定只是简单的展示数据,用index也是可以的。

      表单

      在 React 里,HTML 表单元素的工作方式和其他的 DOM 元素有些不同,这是因为表单元素通常会保持一些内部的 state。

      受控组件

      在 HTML 中,表单元素(如