Young Kbt blog Young Kbt blog
首页
  • java基础

    • Java基础
    • Java集合
    • Java反射
    • JavaJUC
    • JavaJVM
  • Java容器

    • JavaWeb
  • Java版本新特性

    • Java新特性
  • SQL 数据库

    • MySQL
    • Oracle
  • NoSQL 数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • ActiveMQ
    • RabbitMQ
    • RocketMQ
    • Kafka
  • 进阶服务

    • Nginx
  • Spring
  • Spring Boot
  • Spring Security
  • 设计模式
  • 算法
  • 知识
  • 管理

    • Maven
    • Git
  • 部署

    • Linux
    • Docker
    • Jenkins
    • Kubernetes
  • 进阶

    • TypeScript
  • 框架

    • React
    • Vue2
    • Vue3
  • 轮子工具
  • 项目工程
  • 友情链接
  • 本站

    • 分类
    • 标签
    • 归档
  • 我的

    • 收藏
    • 关于
    • Vue2-Admin (opens new window)
    • Vue3-Admin(完善) (opens new window)
GitHub (opens new window)

Shp Liu

朝圣的使徒,正在走向编程的至高殿堂!
首页
  • java基础

    • Java基础
    • Java集合
    • Java反射
    • JavaJUC
    • JavaJVM
  • Java容器

    • JavaWeb
  • Java版本新特性

    • Java新特性
  • SQL 数据库

    • MySQL
    • Oracle
  • NoSQL 数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • ActiveMQ
    • RabbitMQ
    • RocketMQ
    • Kafka
  • 进阶服务

    • Nginx
  • Spring
  • Spring Boot
  • Spring Security
  • 设计模式
  • 算法
  • 知识
  • 管理

    • Maven
    • Git
  • 部署

    • Linux
    • Docker
    • Jenkins
    • Kubernetes
  • 进阶

    • TypeScript
  • 框架

    • React
    • Vue2
    • Vue3
  • 轮子工具
  • 项目工程
  • 友情链接
  • 本站

    • 分类
    • 标签
    • 归档
  • 我的

    • 收藏
    • 关于
    • Vue2-Admin (opens new window)
    • Vue3-Admin(完善) (opens new window)
GitHub (opens new window)
  • 超文本标记语言 - Html

  • 解释编程语言 - JavaScript

  • JS 超集语言 - TypeScript

  • 界面构建框架 - React

    • React - 基础与核心
      • 三大核心
        • state
        • props
        • ref
      • 概念基础
        • 虚拟 DOM
        • 函数组件
        • 类式组件
        • 受控组件
      • 生命周期
        • 旧版生命周期
        • 新版生命周期
    • React - 脚手架及使用
    • React - 路由
    • React - Redux
    • React - 函数类型
    • React - 进阶知识
    • React - 父子传值与循环key
  • 渐进式框架 - Vue2

  • 渐进式框架 - Vue3

  • 前端
  • 界面构建框架 - React
Young Kbt
2022-02-21
目录

React - 基础与核心

  • 三大核心
    • state
    • props
    • ref
  • 概念基础
    • 虚拟 DOM
    • 函数组件
    • 类式组件
    • 受控组件
  • 生命周期
    • 旧版生命周期
    • 新版生命周期

# 三大核心

# state

作用:存储数据的容器。

写在 construct 里

export default class Demo extends Component{
    construct(props){
        super(props)
        this.state = {
            xxx:xxx
        }
    }
}
1
2
3
4
5
6
7
8

写在类里(推荐)

export default class Demo extends Component{
    state = {
        xxx:xxx
    }
}
1
2
3
4
5

其他位置只需保证 this 是指向类的实例对象,即可通过 this.state.xxx 取到数据。

# props

作用

获得父组件传来的值。

  1. 通过便签传值:key=value 传值

  2. 对象传值

    Person 的 props 里有三个属性:

    ReactDOM.render(<Person name="jerry" age={19}  sex="男"/>,document.getElementById('test1'))
    
    1

方式

  1. 第一种:key=value 传值

    • 缺点:当值多的时候,便签里的 key=value 代码不优雅
  2. 第二种:对象传值

    • 通过 {...xxx} 传值

    • 前提:对象内的名字和接收的名字一致

      const p = {name:'老刘',age:18,sex:'女'}
      ReactDOM.render(<Person {...p}/>,document.getElementById('test3'))
      
      1
      2
    • Person 组件接收

      const {name,age,sex} = this.props
      
      1

对 props 的内容进行限制

写在类里面,需要使用 static 修饰

class Person extends React.Component{  
    // 对标签属性进行类型、必要性的限制
    static propTypes = {
        name:PropTypes.string.isRequired, // 限制 name 必传,且为字符串
        sex:PropTypes.string, // 限制 sex 为字符串
        age:PropTypes.number, // 限制 age 为数值
    }

    // 指定默认标签属性值
    static defaultProps = {
        sex:'男',// sex 默认值为男
        age:18 // age 默认值为 18
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

写在类外面,通过类名 .xx 修饰

class Person extends React.Component{
    // ......
}
// 对标签属性进行类型、必要性的限制
Person.propTypes = { // 固定
    name:PropTypes.string.isRequired, // 限制 name 必传,且为字符串
    sex:PropTypes.string, // 限制 sex 为字符串
    age:PropTypes.number, // 限制 age 为数值
    speak:PropTypes.func, // 限制 speak 为函数
}
// 指定默认标签属性值
Person.defaultProps = {
    sex:'男', // sex 默认值为男
    age:18 // age 默认值为 18
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

写在函数外面,通过函数名 .xx 修饰

function Person (props){
    const {name,age,sex} = props
}
Person.propTypes = {
    name:PropTypes.string.isRequired, // 限制 name 必传,且为字符串
    sex:PropTypes.string, // 限制 sex 为字符串
    age:PropTypes.number, // 限制 age 为数值
}

// 指定默认标签属性值
Person.defaultProps = {
    sex:'男', // sex 默认值为男
    age:18 // age 默认值为 18
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# ref

作用

存储标签,一般用于表单标签,获取表单标签的值。

使用

  1. 字符串形式(官方不推荐,影响效率)

    在 x 标签内加入 ref="xxx",react 自动把 xxx:x 放入 refs 中,通过 this.refs.xxx 调用 x

    // 展示左侧输入框的数据
    showData = ()=>{
        const {input1} = this.refs
        alert(input1.value)
    }
    
    render(){
        return(
            <div>
                <input ref="input1" type="text" placeholder="点击按钮提示数据"/>&nbsp;
                <button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
            </div>
        )
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  2. 回调形式。开发常用

    1. 在 x 标签使用 ref="e => this.xxx=e",其中 xxx 是一个回调函数,记得使用箭头函数把 this 指向类的实例

    2. e 是 react 自动放入的一个参数,值是当前标签,原理就是在类里创建一个 xxx,通过变量 = 值的方式,让 xxx=e 此时在别的函数调用 this.xxx 即可获得当前标签

    3. 缺点:每次更新状态,调用 render 时,ref 会被调用两次,第一次的参数 e 为 null,第二次才是当前标签

      • 原因:react 在重新调用 render 前,会把 render 内清除掉,此时再清除 xxx 时,发现有一个回调函数,所以把 null 传进去,第二次就是渲染页面,把当前标签传入

      • 总结:状态 state 更新,render 先调用一次进行自我销毁,然后再调用一次进行新的渲染,第一次已经销毁,所以 e 为 null,第二次才是要求

      该缺点官方明确说无关紧要

      showData = ()=>{
          const {input1} = this
          alert(input1.value)
      }
      render(){
          return(
              <div>
                  <input ref={e => this.input1 = e } type="text" placeholder="点击按钮提示数据"/>&nbsp;
                  <button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
              </div>
          )
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
    4. 函数形式

      • 和回调形式一样,只不过把标签只是调用函数名,函数实现方式放到类里进行实现

      • ref="this.xxx",在类里实现 xxx 函数,xxx 函数会传入一个参数,该参数就是 xxx 所在的标签

    5. createRef 创建 ref

      • 官方力推的 API

      • 缺点:创建的 ref 只能存储一个标签,多个标签需要创建多个 ref

      /* 
        React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是「专人专用」的
      */
      myRef = React.createRef()
      // 展示左侧输入框的数据
      showData = ()=>{
          alert(this.myRef.current.value);
      }
      render(){
          return(
              <div>
                  <input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>&nbsp;
                  <button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
                  <input onBlur={this.showData2} ref={this.myRef2} type="text" placeholder="失去焦点提示数据"/>&nbsp;
              </div>
          )
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17

总结

  1. 通过 onXxx 属性指定事件处理函数(注意大小写)

    1. React 使用的是自定义(合成)事件, 而不是使用的原生 DOM 事件:为了更好的兼容性
    2. React 中的事件是通过事件委托方式处理的(委托给组件最外层的元素):为了的高效
  2. 通过 event.target 得到发生事件的 DOM 元素对象:不要过度使用 ref

    1. 如果需要一个标签事件响应另一个标签则使用 ref
    2. 如果事件响应仅在当前标签,该事件的第一个参数就是当前标签的事件对象,而 event.target 就是当前标签

# 概念基础

# 虚拟 DOM

学习完三大基础,现在了解 React 的核心:虚拟 DOM。

虚拟 DOM:React 把虚拟 DOM 渲染为真实 DOM。

举例

假设原生 DOM 是 a 和 b,在次基础上加上 c,则原生 DOM 是把原来的 a 和 b 去掉,重新加入 a 和 b 和 c。

但是虚拟 DOM 会去找是否有 a 和 b,有的话则直接在后面追加 c,无需去掉 a 和 b。

总结

  • 原生 DOM:替换原来的元素:100 个元素变成 101 个元素(加 1 个元素),则前面的 100 个元素先去掉,再加上 101 个元素,即需要渲染 101 次,不判断是否重复,效率低

  • 虚拟 DOM:增加新的元素:100 元素变成 101 元素(加 1 个元素),先判断前面的 100 元素一样,则需要渲染 1 次,因为前面 100 元素已经有了

# 函数组件

创建一个函数,元素在函数里进行 return,最后在 render 渲染。

// 1.创建函数式组件
function MyComponent(){
    console.log(this); // 此处的 this 是 undefined,因为 babel 编译后开启了严格模式
    return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
}
// 2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
/* 
    执行了 ReactDOM.render(<MyComponent/>....... 之后,发生了什么?
    1.React 解析组件标签,找到了 MyComponent 组件。
    2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟 DOM 转为真实 DOM,随后呈现在页面中。
*/
1
2
3
4
5
6
7
8
9
10
11
12

# 类式组件

创建一个类,元素在函数里进行 return,最后在 render 渲染。

class MyComponent extends React.Component {
    render(){
        // render 是放在哪里的?—— MyComponent 的原型对象上,供实例使用。
        // render 中的 this 是谁?—— MyComponent 的实例对象 <=> MyComponent 组件实例对象。
        console.log('render中的this:',this);
        return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
    }
}
// 2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
/* 
    执行了 ReactDOM.render(<MyComponent/>....... 之后,发生了什么?
    1.React 解析组件标签,找到了 MyComponent 组件。
    2.发现组件是使用类定义的,随后 new 出来该类的实例,并通过该实例调用到原型上的 render 方法。
    3.将 render 返回的虚拟 DOM 转为真实 DOM,随后呈现在页面中。
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 受控组件

受控组件:现存 现取 现用,不依赖状态 state。

例子

直接在标签里存入 input 数据,不存放到 state。

直接在表单提交的时候,去获取 input 数据,并上传

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>1_非受控组件</title>
</head>
<body>
   <!-- 准备好一个「容器」 -->
   <div id="test"></div>
   
   <!-- 引入react核心库 -->
   <script type="text/javascript" src="../js/react.development.js"></script>
   <!-- 引入react-dom,用于支持react操作DOM -->
   <script type="text/javascript" src="../js/react-dom.development.js"></script>
   <!-- 引入babel,用于将jsx转为js -->
   <script type="text/javascript" src="../js/babel.min.js"></script>

   <script type="text/babel">
      //创建组件
      class Login extends React.Component{
         handleSubmit = (event)=>{
            event.preventDefault() //阻止表单提交
            const {username,password} = this
            alert(`你输入的用户名是:${username.value},你输入的密码是:${password.value}`)
         }
         render(){
            return(
               <form onSubmit={this.handleSubmit}>
                  用户名:<input ref={c => this.username = c} type="text" name="username"/>
                  密码:<input ref={c => this.password = c} type="password" name="password"/>
                  <button>登录</button>
               </form>
            )
         }
      }
      //渲染组件
      ReactDOM.render(<Login/>,document.getElementById('test'))
   </script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

例子

非受控组件:先存入状态 state 里,然后去状态 state 里获取数据,上传数据

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>2_受控组件</title>
</head>
<body>
   <!-- 准备好一个「容器」 -->
   <div id="test"></div>
   
   <!-- 引入react核心库 -->
   <script type="text/javascript" src="../js/react.development.js"></script>
   <!-- 引入react-dom,用于支持react操作DOM -->
   <script type="text/javascript" src="../js/react-dom.development.js"></script>
   <!-- 引入babel,用于将jsx转为js -->
   <script type="text/javascript" src="../js/babel.min.js"></script>

   <script type="text/babel">
      //创建组件
      class Login extends React.Component{

         //初始化状态
         state = {
            username:'', //用户名
            password:'' //密码
         }

         //保存用户名到状态中
         saveUsername = (event)=>{
            this.setState({username:event.target.value})
         }

         //保存密码到状态中
         savePassword = (event)=>{
            this.setState({password:event.target.value})
         }

         //表单提交的回调
         handleSubmit = (event)=>{
            event.preventDefault() //阻止表单提交
            const {username,password} = this.state
            alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
         }

         render(){
            return(
               <form onSubmit={this.handleSubmit}>
                  用户名:<input onChange={this.saveUsername} type="text" name="username"/>
                  密码:<input onChange={this.savePassword} type="password" name="password"/>
                  <button>登录</button>
               </form>
            )
         }
      }
      //渲染组件
      ReactDOM.render(<Login/>,document.getElementById('test'))
   </script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

# 生命周期

mount(挂载)

react调用render方法,把虚拟DOM解析为真实DOM,挂载到页面上

所以生命周期就是围绕挂载前后进行

unmount(卸载)

如果需要把render挂载的页面清除掉,则需要卸载

# 旧版生命周期

初始化阶段: 由 ReactDOM.render() 触发(初次渲染),依次触发顺序:

  1. constructor():构造器

  2. componentWillMount():组件将要挂载

  3. render():挂载

  4. componentDidMount():组件已经挂载,常用 的钩子函数,did 为 do 的过去式

    一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息。

更新阶段: 由组件内部 this.setSate() 或父组件 render 触发,依次触发顺序

  1. componentWillReceiveProps:组件将收到父组件的参数,注意:第二次以上父组件传参才被调用,第一次指挂载页面的时候,不会触发

  2. shouldComponentUpdate():组件是否更新,在方法里需要只当返回值为布尔类型,false 则不会执行下面操作 234,重写该方法必须指定布尔值

  3. componentWillUpdate():组件将要更新

  4. render():挂载更新,必须使用 的一个钩子函数

  5. componentDidUpdate():组件已经更新,did 为 do 的过去式

卸载组件: 由 ReactDOM.unmountComponentAtNode() 触发,依次触发顺序:

  1. componentWillUnmount():需要一个函数调用该函数,参数为卸载的组件,该组件的所有内容包括自己被清除,常用

    一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息。

    // 卸载组件按钮的回调
    death = ()=>{
       ReactDOM.unmountComponentAtNode(document.getElementById('test'))
    }
    <button onClick={this.death}>卸载组件</button>
    
    1
    2
    3
    4
    5

image-20220226191358402

# 新版生命周期

初始化阶段: 由 ReactDOM.render() 触发(初次渲染),依次触发顺序:

  1. constructor():构造器

  2. getDerivedStateFromProps:从父组件传来的 props 里获取 state,替代类原来的 state

  3. render():挂载

  4. componentDidMount():组件已经挂载,常用 的钩子函数,did 为 do 的过去式

    一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息

更新阶段: 由组件内部 this.setSate() 或父组件重新 render 触发,依次触发顺序:

  1. getDerivedStateFromProps:从父组件传来的 props 里获取 state,替代类原来的 state

  2. shouldComponentUpdate()

  3. render()

  4. getSnapshotBeforeUpdate():更新前从快照获取数据,提供更新原来的数据可以存储,需要返回值,返回给5的第三个参数里

  5. componentDidUpdate():页面更新完成后,有三个参数,默认第一个是更新前的 props,第二个是更新前的 state,第三个是 4 快照传来的值

卸载组件: 由 ReactDOM.unmountComponentAtNode() 触发,依次触发顺序:

  1. componentWillUnmount():常用

    一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息。

新函数钩子举例

// 若 state 的值在任何时候都取决于 props,那么可以使用 getDerivedStateFromProps
static getDerivedStateFromProps(props,state){
   console.log('getDerivedStateFromProps',props,state);
   return null
}
// 在更新之前获取快照
getSnapshotBeforeUpdate(){
   console.log('getSnapshotBeforeUpdate');
   return 'kele'  // 返回给 componentDidUpdate()
}
// 组件更新完毕的钩子
// 更新前的 props,更新前的 state,以及 getSnapshotBeforeUpdate 的返回值
componentDidUpdate(preProps,preState,snapshotValue){
   console.log('Count---componentDidUpdate',preProps,preState,snapshotValue);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

image-20220226191410735

getSnapshotBeforeUpdate 的使用场景:

模拟新闻

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>4_getSnapShotBeforeUpdate的使用场景</title>
   <style>
      .list{
         width: 200px;
         height: 150px;
         background-color: skyblue;
         overflow: auto;
      }
      .news{
         height: 30px;
      }
   </style>
</head>
<body>
   <!-- 准备好一个「容器」 -->
   <div id="test"></div>
   
   <!-- 引入react核心库 -->
   <script type="text/javascript" src="../js/17.0.1/react.development.js"></script>
   <!-- 引入react-dom,用于支持react操作DOM -->
   <script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script>
   <!-- 引入babel,用于将jsx转为js -->
   <script type="text/javascript" src="../js/17.0.1/babel.min.js"></script>

   <script type="text/babel">
      class NewsList extends React.Component{

         state = {newsArr:[]}

         componentDidMount(){
            setInterval(() => {
               //获取原状态
               const {newsArr} = this.state
               //模拟一条新闻
               const news = '新闻'+ (newsArr.length+1)
               //更新状态
               this.setState({newsArr:[news,...newsArr]})
            }, 1000);
         }

         getSnapshotBeforeUpdate(){
            return this.refs.list.scrollHeight
         }

         componentDidUpdate(preProps,preState,height){
            this.refs.list.scrollTop += this.refs.list.scrollHeight - height
         }

         render(){
            return(
               <div className="list" ref="list">
                  {
                     this.state.newsArr.map((n,index)=>{
                        return <div key={index} className="news">{n}</div>
                     })
                  }
               </div>
            )
         }
      }
      ReactDOM.render(<NewsList/>,document.getElementById('test'))
   </script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
编辑此页 (opens new window)
#React
更新时间: 2024/11/02, 09:43:06
TypeScript Office - 声明合并
React - 脚手架及使用

← TypeScript Office - 声明合并 React - 脚手架及使用→

最近更新
01
技术随笔 - Element Plus 修改包名 原创
11-02
02
Reactor - 扩展性
11-02
03
Reactor - 最佳实践
11-02
更多文章>
Theme by Vdoing | Copyright © 2021-2024 Young Kbt | blog
桂ICP备2021009994号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式