2022React学习笔记,欢迎批评和指正。

本文是React自学笔记,探讨了React中的虚拟DOM概念及其优势,包括jsx语法中的注意事项。此外,文章详细阐述了类式组件中的this问题,解释了如何正确处理this以避免在方法中丢失上下文。还介绍了React的state和props属性,以及ref的使用。最后,讨论了React的事件处理机制、生命周期变化以及diffing算法的作用和使用index作为key可能带来的问题。
摘要由CSDN通过智能技术生成

前言

这是一篇自学笔记,帮助自己的React学习,此学习笔记中只记录教程中对我来说比较又触动的点。

观看的视频教程链接如下:

001_尚硅谷react教程_react简介_哔哩哔哩_bilibili,这位老师的讲解非常细致入微,一边学习react一边还能补充到自己之前漏掉的js相关知识,关于新知识的引导做的非常好,推荐各位想学习React的都可以去看看。

最后,小弟博客用的不熟,排版、字体看起来估计难以入目,多有担待。

注:笔记中的代码多为手敲,可执行性未知。

React中的虚拟DOM

react效率高的最大原因就是react有虚拟dom,能够通过自己优秀的diffing算法,计算每次需要渲染的元素,而不是一直重绘html,在jsx语法下使用虚拟dom需要注意以下问题:

1.虚拟dom中使用js表达式需要用花括号包围,例如,要注意的是,花括号中包围的是js表达式 而不是任意的js代码如:。

2.虚拟dom也支持像html标签传递class,以此将样式渲染在元素上,但是因为class是关键字,所以jsx中的虚拟dom的class需要使用className接收,例如:

3.虚拟dom中元素的自定义样式style不能像html标签一样使用字符串传递,而是采用传递对象的形式,例如:<h2 style={{color:‘white’,fontSize:‘18px’}}>,其中,第一层花括号表示接收的是js表达式,

第二层花括号表示的是,我们将对象传递给了style属性,也可以看到,需要对多单词的样式属性名进行小驼峰改写。

4.虚拟dom好在哪?

当我们在控制台中直接将虚拟dom打印出来的时候,我们可以看到,其中的属性相比与html元素大大减少了,如下图,因此在对虚拟dom的操作过程中,损耗是远小于直接操作真实dom的
输出的虚拟dom:                                                
输出的虚拟DOM
控制台捕捉到的真实dom:
真实DOM

this问题

1.基本知识

在js中,如果未开启严格模式,函数中的this是window对象,如果开启了严格模式,那么this就变成了undefined。

2.类式组件

react中有两种组件,一种是函数组件,另一种就是类式组件,该种组件适用于复杂组件的开发,其中的this问题需要搞懂了才算明白了类式组件的开发:

class Person extends React.Component{
    constructor(name){
      this.name = name;
    }

    render(){
      return <h2 onClick={this.speak}>{this.name}</h2>;
    }

    speak(){
      console.log("my name is :"+this.name);
    }
  }

  ReactDOM.render(<Person name="jack" />,document.getElementById("app"));
}

我们需要知道在ReactDOM.render这行代码之后,react帮我们做了什么:

(1)react接收到了Person,发现它是类式组件。

(2)react创建了一个该类的实例,假设react创建出的实例叫做p1。

(3)创建该实例,先会进入构造器,此时构造器中的this,指向的就是组件的实例对象,也就是p1。

(4)创建实例之后,react核心方法需要将该组件转为虚拟dom,也就是类中需要固定的方法:render,react也是通过p1执行了render,所以render中的this也是p1。

(5)当用户点击这个标签的时候,会触发元素的onClick方法,也就是speak方法,因为此时我们是直接触发了onClick方法,没有经过p1,p1是react内部的,所以此时的this是undefined。
  所以,当需要在speak中使用this的时候,需要用以下方法:

(1)把this绑定给speak方法:

    constructor(name){
      this.name = name;
      this.speak = this.speak.bind(this);
    }

要注意,this.speak.bind中的this.speak是读的到所写的speak方法的,此时输出this,可以看到原型链中有该方法(proto),而此行代码,将该方法给到了该类的属性上,因此在后续调用中,onClick执行的就是实例对象中的speak,而不是直接执行speak方法。

(2)利用箭头函数:

speak = ()=>{
  console.log("my name is :"+this.name);
}

因为箭头函数没有自身的this,所以this会向外找,也就是类本身,也就是类的实例对象,这种写法,也是将speak作为类的属性,让this.speak能够触发。

react三大属性

1.state

react中的state,用于管理定义的数据,可以在类的构造器中对state进行初始化,在render方法中就可以取出state中的数据,改变state之后,react就会相应数据的修改。

但是要注意,修改state的值要通过this.setSate方法来操作,不能直接改变state中的属性值,同时setState会合并更新state.

constructor(){
  super();
  // this.state = {name:'tom',age:'18'} 在构造器中初始化state,可以直接以组件实例属性的方式使用,省去构造器。
}

state =  {name:'tom',age:'18'}

render(){
  const {name,age} = this.state;
  return <h2 onClick={this.addAge}>我的名字是:{name},年龄{age}</h2>;
}

addAge = ()=>{
  const {age} = this.state;
  age = age+1;
  this.setSate = {
    age :age 
  }
}

2.props

react还提供了props来接收传入的标签参数,只要在标签上传入属性,react就会把这些键值对保存早this.props中

<Person name="jack" age={18} />
render(){
	const {name,age} = this.props;
	return <h2>名字:{name},年龄:{age+1}</h2>
}

在实际的生产开发环境中,普遍需要对传入的属性做限制,如上述例子,如果传入的age不是数字的18而是字符串,那么展示出来的就是“年龄:181”
所以react还提供了对属性类型和必要性进行管理的propTypes和对属性默认值进行管理的defaultProps
// 对标签属性进行类型和必要性的限制:注:使用PropTypes需要调用prop-type库

Person.propTypes = {
  name: PropTypes.string.isRequired, // 限制name必传,且为字符串。
  sex: PropTypes.string, // 限制sex为字符串。
  age: PropTypes.number, // 限制age为数字。
}

// 指定默认标签属性值
Person.defaultProps = {
  sex:'男',
  age:18
}
 
//对于上述两种类本身的属性(区分Person.demo和p.demo)也可以进行简写到类中
class Person extends React.Component{
  static propTypes = {
    name: PropTypes.string.isRequired, // 限制name必传,且为字符串。
    sex: PropTypes.string, // 限制sex为字符串。
    age: PropTypes.number, // 限制age为数字。
  }
 
  static defaultProps = {
    sex:'男',
    age:18
  }
}

注:函数式组件没有this.state this.props this.refs,因为函数没有实例对象,但是函数式组件可以使用props,因为函数可以接收参数
function(props){
  const {name,age} = props;
  …
}
也可以使用propTypes和defaultProps,但此时就不能用static进行简写了,而是要用Peron.propTypes的形式。

3.ref
ref是react用来管理组件中的元素,方便开发过程中对元素的操作。
写法1:
使用时:alert(this.refs.myInput.value)
注意,因为效率问题,不推荐使用直接往ref传递字符串的形式。

写法2:<input ref={currentNode=>this.myInput = c} type=“text” />
使用时:alert(this.myInput.value)
上述写法中,利用回调函数将元素本身的DOM,传递给了组件实例对象:this,因此在使用时就可以通过this.的方式取出元素,这是react赋予ref标签的能力

react中的事件处理

react中为什么要使用onClick代替onclick?——为了更好的兼容性
react中的事件是通过事件委托方式处理的,委托给最外层的元素——为了更高效
react官网要求不要多用ref影响效率,可通过以下方法判断哪些ref可以不使用:
发生事件的元素和要操作的元素是同一个的时候就可以不使用ref,因为事件响应中存在event的参数传递,而event.target就是事件发生的元素本身。

React生命周期

旧版:
组件挂载时:constructor => componentWillMount=> render=> => componentDidMount => componentWillUnmount
组件更新时:componentWillReceiveProps => shouldComponentUpdate => componentWillUpdate => render = >componentDidUpdate => componentWillUnmount

其中,父组件执行render后,子组件会执行componentWillReceiveProps方法,setState方法会触发shouldComponentUpdate ,forceUpdate会触发componentWillUpdate ,如下图:
react生命周期(旧版本)
新版:
组件挂载时:contructor=>getDerivedStateFromProps=>render=>componentDidMount
组件更新时:static getDerivedStateFromProps=>shouldComponentUpdate=>render=>getSnapshotBeforeUpdate=>componentDidUpdate
react生命周期(新版本)
相比旧版,新版本的react生命周期删除了三个钩子函数:componentWillMount、componentWillReceiveProps、componentWillUpdate,
因为后续版本react会发布异步式更新的内容,react预测这三个钩子函数会被滥用,新版本其实还是可以使用上述三个函数,但是函数名前需要增加UNSAFE_前缀。

新增的两个钩子:
static getDerivesStateFromProps:从props中获取派生的state,返回null则不对state做任何更新,该方法使用的情况很少,除非组件中的state完全基于props。
getSnapshotBeforeUpdate:在更新前获取一次快照,该方法能够配合componentDidUpdate来判断更新前后组件的一些数据,比如得到更新前后的高度以此算出高度差,componentDidUpdate可以接收到三个参数:prevProps、prevState、snapShotValue,也就是组件更新前的属性、状态和快照信息。

diffing算法以及用index作为key会产生的问题

1.验证diffing算法
已知react在组件更新后会通过虚拟DOM的比较来判断怎么更新页面上的元素,如果新的虚拟DOM和之前的一致,那么不会触发更新,如果不一致,react就会生成新的页面元素并渲染到页面上,可以用以下例子证明diffing算法在更新时确实只更新了不一致的元素。

		// 创建组件
        class Time extends React.Component {
            state = { time: new Date() }

            componentDidMount() {
                this.timer = setInterval(() => {
                    this.setState({
                        time: new Date()
                    })
                }, 1000);
            }

            render() {
                const { time } = this.state;
                // 如果diffing算法更新了input的元素,那么在输入框中输入文本后下一秒就会被清空,然而并没有。
                return (
                    <div>
                        <h1>hello</h1>
                        <input id="input1" type="text" />
                        <span>现在的时间是{time.toTimeString()} <input id="input2" type="text" /> </span>
                    </div>
                );
            }
        }

可以看出,Time组件每秒钟更新一次状态,并将最新的时间展示在页面,如果此时input1中有用户输入的数据,状态更新时input1中输入的数据不会改变,由此可以看出,state更新了时间之后,react只更新了span元素,那么span中的输入框input2呢?
其实也没有改变,因为diffing算法是会深层遍历的,时间更新不会影响到input2,所以input中的内容也不会改变。

2.用index作为key会产生的问题
当我们在用数组循环遍历出虚拟DOM的时候,react都会要求我们传递key属性作为元素的标识,简单的情况下我们会用数组的下标作为被遍历元素的key,但是这会导致以下问题:
(1)效率问题:数组头部添加/删除元素时,会导致数组中所有元素的下标值都改变,这就造成了明明只需要在头部新增/删除一次元素的操作变成了每个数组中的元素都要被更新,随着数组长度的增大,引起的效率问题会越来越严重。
(2)页面展示问题:当被遍历的数组中保护输入类元素时,因为虚拟DOM中没有value属性值,所以在问题(1)的情况下,元素中的内容被更新了,但是元素中的输入类元素并没有被更新,这就会导致牛头不对马嘴的情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值