写在前面
在最近看了React之后,一直觉得学的懵懵然,虽然很多大佬的手写笔记,写的都很不错,但是我一直没有我想要的那种细无巨细,比如类式组件this指向问题的追根溯源,又比如三大实例属性简写的由来,总之我还是决定做一份事无巨细的笔记。
那就让我们开始吧!
复习类相关的知识
创建实例对象
1
2
3
4
5
6
7
8//创建一个Person类 class Person { } let person1 = new Person(); console.log(person1)
- 输出分解
红色框代表new出来的实例对象
蓝色框代表这个实例对象是由Person这个类new出来的。
目的:为了区别实例是由哪个类new出来的
接收参数
- 使用构造器方法(可以不写)
构造器也是在原型上的
1
2
3
4
5
6
7
8
9
10//创建一个Person类 class Person { //构造器方法 constructor(name,age){ //构造器中的this是谁?—— 类的实例对象 this.name = name this.age = age } }
注意: 构造器中的this是谁?—— 类的实例对象
- 定义方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//创建一个Person类 class Person { //构造器方法 constructor(name,age){ //构造器中的this是谁?—— 类的实例对象 this.name = name this.age = age } //一般方法 speak(){ //speak方法放在了哪里?——类的原型对象上,供实例使用 //通过Person实例调用speak时,speak中的this就是Person实例 console.log(`我叫${this.name},我年龄是${this.age}`); } }
注意:
speak方法放在了哪里?——类的原型对象上,供实例使用
通过Person实例调用speak时,speak中的this就是Person实例
- 继承
书写构造器之后必须书写super、必须放在带有使用this关键字的前面
super:调用父类的构造器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19//创建一个Student类,继承于Person类 class Student extends Person { constructor(name,age,grade){ super(name,age) this.grade = grade this.school = '尚硅谷' } //重写从父类继承过来的方法 speak(){ console.log(`我叫${this.name},我年龄是${this.age},我读的是${this.grade}年级`); this.study() } study(){ //study方法放在了哪里?——类的原型对象上,供实例使用 //通过Student实例调用study时,study中的this就是Student实例 console.log('我很努力的学习'); } }
- 调用方法
如果不重写方法,那么就会调用父类提供的方法,这个方法会通过原型链在person实例的原型对象上找到。
1
2
3
4
5
6
7
8class Student extends Person { constructor(name,age,grade){ super(name,age) this.grade = grade this.school = '尚硅谷' } }
- 重写方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19//创建一个Student类,继承于Person类 class Student extends Person { constructor(name,age,grade){ super(name,age) this.grade = grade this.school = '尚硅谷' } //重写从父类继承过来的方法 speak(){ console.log(`我叫${this.name},我年龄是${this.age},我读的是${this.grade}年级`); this.study() } study(){ //study方法放在了哪里?——类的原型对象上,供实例使用 //通过Student实例调用study时,study中的this就是Student实例 console.log('我很努力的学习'); } }
蓝色框的方法在student实例的原型对象上、红色框的方法在person实例的原型对象上。
调用方法采用就近原则,通过原型链调用最先找到的那个方法。
-总结:
1
2
3
4
5
61.类中的构造器不是必须要写的,要对实例进行一些初始化的操作,如添加指定属性时才写。 2.如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的。 3.类中所定义的方法,都放在了类的原型对象上,供实例去使用。
第11集 类式组件
类名或者函数名就是组件名
创建类式组件
- 必须继承React内置一个类
必须继承React.Component
- 可以省略构造器
传送门
https://react.docschina.org/
- 书写必备的三个条件
- 必须继承React.Component
- 必学书写render方法
- redner必须有返回值
1
2
3
4
5
6
7
8
9
10
11
12
13<script type="text/babel"> //1.创建类式组件 class MyComponent extends React.Component { render(){ //render中的this是谁?—— MyComponent的实例对象 <=> MyComponent组件实例对象。 console.log('render中的this:',this); return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2> } } //2.渲染组件到页面 ReactDOM.render(<MyComponent/>,document.getElementById('test')) </script>
- render是放在哪里的?—— MyComponent的原型对象上,供实例使用。
-
执行了ReactDOM.render(…之后,发生了什么?
复制代码1
2
3
41.React解析组件标签,找到了MyComponent组件。 2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。 3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。
-
render中的this是谁?
MyComponent的实例对象 <=> MyComponent组件实例对象。
console.log('render中的this:',this);
第12集 对state的理解
简单组件、复杂组件
有无“状态”,有就是复杂组件、无就是简单组件
组件实例的三大核心属性
state、prrops、refs
第13集 初始化state
组件三大核心属性:state
- 向state属性传值(书写构造器)
1、构造器传值:接受props
传送门
https://react.docschina.org/
2、必须调用super
1
2super(props)
如果书写构造器不调用就会报错
- 给state赋值是一个对象
1
2
3//初始化状态 this.state = {isHot:false,wind:'微风'}
- 渲染到页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<script type="text/babel"> //1.创建组件 class Weather extends React.Component{ constructor(props){ super(props) //初始化状态 this.state = {isHot:false} } render(){ return <h1 今天天气很{isHot ? '炎热' : '凉爽'}</h1> } } //2.渲染组件到页面 ReactDOM.render(<Weather/>,document.getElementById('test')) </script>
第14集 react中事件绑定
原生事件的绑定
- 3种类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<body> <button id="btn1">按钮1</button> <button id="btn2">按钮2</button> <button onclick="demo()">按钮3</button> </body> <script type="text/javascript" > const btn1 = document.getElementById('btn1') btn1.addEventListener('click',()=>{ alert('按钮1被点击了') }) const btn2 = document.getElementById('btn2') btn2.onclick = ()=>{ alert('按钮2被点击了') } function demo(){ alert('按钮3被点击了') } </script>
- 实现效果
- 在React依然可以使用原生绑定事件(只要能绑定到render方法返回的标签就行)
实际中不会这样使用
- on类api得驼峰命名
1、为onClick赋值一个方法必须使用{} 并且不能带()。
必须使用{}原因:
使用{},是因为这里还是将函数赋值给onClick,不然会被解析为字符串。简单来说就是这里要使用js表达式了。
不能带()原因:这里就为了函数赋值给onClick回调,而不是将函数的返回值传递给onClick来回调
不然一渲染就会直接调用这个函数。而且onClick会回调函数的返回值,而不是函数本身。
第15集 类中方法的this
- babel开启严格模式
this是undefined
-
在render中的this就是组件实例对象,因为是组件实例对象调用的redner方法。
-
使用外作用域定义变量取到组件实例对象的this
- 将函数方法书写到类式组件中
- onClick不能直接调用方法(组件实例对象调用-添加this)
但是这里changeWeather方法中的this指向还是undefined
类中方法的this指向
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<script type="text/javascript" > class Person { constructor(name,age){ this.name = name this.age = age } study(){ //study方法放在了哪里?——类的原型对象上,供实例使用 //通过Person实例调用study时,study中的this就是Person实例 console.log(this); } } const p1 = new Person('tom',18) p1.study() //通过实例调用study方法 const x = p1.study x() // 直接调用study </script>
-
类中定义的所有方法,在局部作用域都开启了严格模式
-
函数中开启严格模式
- onClick是直接回调这个函数(不是组件实例对象调用的)
- 直接强行new一个类式组件的实例对象
这里的this指向就是组件实例对象
-
由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用
-
类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined
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<script type="text/babel"> //1.创建组件 class Weather extends React.Component{ //构造器调用几次? ———— 1次 constructor(props){ console.log('constructor'); super(props) //初始化状态 this.state = {isHot:false,wind:'微风'} //解决changeWeather中this指向问题 this.changeWeather = this.changeWeather.bind(this) } //render调用几次? ———— 1+n次 1是初始化的那次 n是状态更新的次数 render(){ console.log('render'); //读取状态 const {isHot,wind} = this.state return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1> } //changeWeather调用几次? ———— 点几次调几次 changeWeather(){ //changeWeather放在哪里? ———— Weather的原型对象上,供实例使用 console.log('changeWeather'); //获取原来的isHot值 const isHot = this.state.isHot //严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换。 this.setState({isHot:!isHot}) console.log(this); //严重注意:状态(state)不可直接更改,下面这行就是直接更改!!! //this.state.isHot = !isHot //这是错误的写法 } } //2.渲染组件到页面 ReactDOM.render(<Weather/>,document.getElementById('test')) </script>
第16集解决类中this指向问题
使用bind
bind : 生成新函数、改变this指向
1
2
3//解决changeWeather中this指向问题 this.changeWeather = this.changeWeather.bind(this)
解析过程:在组件对象的原型上找到函数,使用bind创建了一个一模一样的函数,但是讲函数的this修改为组件实例对象。并且将这个新函数作为一个值,赋值给了实例对象的一个叫changeWeather的方法。这样实例对象自身就会有一个changeWeather的方法。
注意:左边的changeWeather 在 实例对象身上、右边的changeWeather是原型对象上的。这段代码核心就是将原型对象上的方法加工改变this添加的实例对象上。
第17集 对setState的使用
- 对state对象的值修改必须使用setSate内置api(在React.component)
1
2
3
4
5
6
7console.log('changeWeather'); //获取原来的isHot值 const isHot = this.state.isHot //严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换。 this.setState({isHot:!isHot}) console.log(this);
- 修改对象值是局部修改
严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换。
- 调用次数
构造器调用几次? ———— 1次(创建实现对象时调用)
render调用几次? ———— 1+n次 1是初始化的那次 n是状态更新的次数
changeWeather调用几次? ———— 点几次调几次
第18集 state的简写方式
核心解决手段来源:类中可以直接写赋值语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14class Car { constructor(name,price){ this.name = name this.price = price // this.wheel = 4 } //类中可以直接写赋值语句,如下代码的含义是:给Car的实例对象添加一个属性,名为a,值为1 a = 1 wheel = 4 static demo = 100 } const c1 = new Car('奔驰c63',199) const s1 = new Student('1','2');
简写方法
- 由于自定义函数太多了,大量使用bind。
将函数作为一个值直接赋值到一个属性上。
注意: 还是会报错
解决方法,将function改为箭头函数模式
1
2
3
4
5
6//自定义方法————要用赋值语句的形式+箭头函数 changeWeather = ()=>{ const isHot = this.state.isHot this.setState({isHot:!isHot}) }
原因:在箭头函数中,this引用的是定义箭头函数的上下文(红宝书p300)
- 注意不允许这么写
1
2
3
4
5changeWeather()=>{ const isHot = this.state.isHot this.setState({isHot:!isHot}) }
简写state
直接使用赋值语句
1
2
3//初始化状态 state = {isHot:false,wind:'微风'}
第19集总结state
1. state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
2. 组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)
第20集 props的基本使用
传递组件属性
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<script type="text/babel"> //创建组件 class Person extends React.Component{ render(){ // console.log(this); const {name,age,sex} = this.props return ( <ul> <li>姓名:{name}</li> <li>性别:{sex}</li> <li>年龄:{age+1}</li> </ul> ) } } //渲染组件到页面 ReactDOM.render(<Person name="jerry" age={19} sex="男"/>,document.getElementById('test1')) ReactDOM.render(<Person name="tom" age={18} sex="女"/>,document.getElementById('test2')) const p = {name:'老刘',age:18,sex:'女'} // console.log('@',...p); // ReactDOM.render(<Person name={p.name} age={p.age} sex={p.sex}/>,document.getElementById('test3')) ReactDOM.render(<Person {...p}/>,document.getElementById('test3')) </script>
第21集 批量传递props
展开运算符
传送门
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Spread_syntax
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<script type="text/javascript" > let arr1 = [1,3,5,7,9] let arr2 = [2,4,6,8,10] console.log(...arr1); //展开一个数组 let arr3 = [...arr1,...arr2]//连接数组 //在函数中使用 function sum(...numbers){ return numbers.reduce((preValue,currentValue)=>{ return preValue + currentValue }) } console.log(sum(1,2,3,4)); //构造字面量对象时使用展开语法 let person = {name:'tom',age:18} let person2 = {...person} //console.log(...person); //报错,展开运算符不能展开对象 person.name = 'jerry' console.log(person2); console.log(person); //合并 let person3 = {...person,name:'jack',address:"地球"} console.log(person3); </script>
批量传入props
react+babel 可以允许使用展开运算符展开对象。
- 仅用于标签属性的传递
第22集 对props进行限制
对标签属性传递的类型进行限制
- 在组件上添加propTypes属性
在React每次创建组件实例对象的时候就会访问组件本身的propTypes属性以便于限制props
- 不同版本不同写法
1
2
3
4
5
6
7
8
9
10
11//对标签属性进行类型、必要性的限制 Person.propTypes = { // 15版本 name: React.PropTypes.string, // 16版本 name:PropTypes.string.isRequired, //限制name必传,且为字符串 sex:PropTypes.string,//限制sex为字符串 age:PropTypes.number,//限制age为数值 speak:PropTypes.func,//限制speak为函数,不使用function就是为了防止与关键字发生冲突 }
16版本之后
1
2
3<!-- 引入prop-types,用于对组件标签属性进行限制 --> <script type="text/javascript" src="../js/prop-types.js"></script>
引入一个依赖包就产生一个全局对象,这里会产生一个propTypes全局对象。
属性默认值
1
2
3
4
5
6//指定默认标签属性值 Person.defaultProps = { sex:'男',//sex默认值为男 age:18 //age默认值为18 }
第23集 props的简写方式
props是只读的
简写props
- 将类似限制、默认值书写到类式组件内部
在类的原型上添加属性(使用static关键字)
第24集 类式组件中的构造器与props
- 构造器传送门
https://zh-hans.reactjs.org/docs/react-component.html#constructor
-实际使用
类中的构造器的作用
- 如果不使用构造器接受props和使用super接受props,就无法使用实例对象访问props
1
2
3
4
5
6
7constructor(props){ //构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props // console.log(props); super(props) console.log('constructor',this.props); // super constructor里面缺一个没传props参数都不可,否则就是undefined }
第25集 函数式组件使用props
函数式组件没有实例对象,但是可以接受参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<script type="text/babel"> //创建组件 function Person (props){ const {name,age,sex} = props return ( <ul> <li>姓名:{name}</li> <li>性别:{sex}</li> <li>年龄:{age}</li> </ul> ) } //渲染组件到页面 ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1')) </script>
对props进行限制
- 对组件添加属性
1
2
3
4
5
6
7
8
9
10
11
12Person.propTypes = { name:PropTypes.string.isRequired, //限制name必传,且为字符串 sex:PropTypes.string,//限制sex为字符串 age:PropTypes.number,//限制age为数值 } //指定默认标签属性值 Person.defaultProps = { sex:'男',//sex默认值为男 age:18 //age默认值为18 }
第26集 props总结
- 理解
1. 每个组件对象都会有props(properties的简写)属性
2. 组件标签的所有属性都保存在props中
- propsTypes
propsTypes是React要求的属性名,这个属性用来保存一个对象进而对props进行限制。
PropsTypes是使用prop-types库进限制(需要引入prop-types库)产生的全局对象
第27集 字符串形式的ref
- 使用原生id属性事件绑定
- 使用ref标识
1
2<input ref="input1" type="text" placeholder="点击按钮提示数据"/>
1
2
3
4
5
6//展示左侧输入框的数据 showData = ()=>{ const {input1} = this.refs console.log(input1.value) }
第28集 回调形式的ref
过时APl:String类型的Refs
传送门
https://zh-hans.reactjs.org/docs/refs-and-the-dom.html
- 回调形式的ref
1
2<input ref={c => this.input1 = c } type="text" placeholder="点击按钮提示数据"/>
回调的箭头函数接收的参数正好就是ref属性所在的标签
- 上述代码的执行过程
在创建实例对象之后,实例对象调用render方法直接回调箭头函数。将作为参数的标签本身赋值到实例对象上的一个属性上。
这里就是将input标签赋值给实例对象的input属性上。
第29集 回调ref中调用次数的问题
传送门
https://zh-hans.reactjs.org/docs/refs-and-the-dom.html
- 第一次初始化渲染页面调用render
回调ref中调用1次
- 更新过程
回调ref中调用2次本质:
1.初始化调用的回调函数已经被释放,状态更新后再次调用这个方法时就是一个新的方法。
2.由于不确定之前的函数接受到了什么,做了什么动作,为此就传入了null进行第一次调用。
3.在调用第二次的时候就传入了当前节点。
- 实现结果
- jsx注释方法
1
2{/*<input ref={(c)=>{this.input1 = c;console.log('@',c);}} type="text"/><br/><br/>*/}
- 将 ref 的回调函数定义成 class 的绑定函数的方式
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<script type="text/babel"> //创建组件 class Demo extends React.Component{ state = {isHot:false} showInfo = ()=>{ const {input1} = this alert(input1.value) } changeWeather = ()=>{ //获取原来的状态 const {isHot} = this.state //更新状态 this.setState({isHot:!isHot}) } saveInput = (c)=>{ this.input1 = c; console.log('@',c); } render(){ const {isHot} = this.state return( <div> <h2>今天天气很{isHot ? '炎热':'凉爽'}</h2> {/*<input ref={(c)=>{this.input1 = c;console.log('@',c);}} type="text"/><br/><br/>*/} <input ref={this.saveInput} type="text"/><br/><br/> <button onClick={this.showInfo}>点我提示输入的数据</button> <button onClick={this.changeWeather}>点我切换天气</button> </div> ) } } //渲染组件到页面 ReactDOM.render(<Demo/>,document.getElementById('test')) </script>
第31集 createRef的使用
创建一个专属容器
- 这里的myRef是一个对象,里面的current属性保存的是ref属性所在的标签
第31集 总结 refs
- 一共有4种方式使用refs
第32集 React中的事件处理
1. 通过onXxx属性指定事件处理函数(注意大小写)
1
2
3
41) React使用的是自定义(合成)事件, 而不是使用的原生DOM事件---为了更好的兼容性 2) React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)---为了更高效
2. 通过event.target得到发生事件的DOM元素对象 — 避免过度使用ref
- 事件委托(原理:事件冒泡)
所有的事件都添加到了最外层的标签上
这里就是div
- 避免过度使用ref
发生事件的元素正好就是要操作的元素。就可以使用event.target替代
1
2
3
4
5
6
7//展示右侧输入框的数据 showData2 = (event)=>{ alert(event.target.value); } <input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
最后
以上就是可靠小熊猫最近收集整理的关于React全家桶:类--类式组件--state属性--事件绑定--类中方法的this--props属性--ref--事件处理写在前面复习类相关的知识第11集 类式组件第12集 对state的理解第13集 初始化state第14集 react中事件绑定第15集 类中方法的this第16集解决类中this指向问题第17集 对setState的使用第18集 state的简写方式第19集总结state第20集 props的基本使用第21集 批量传递props第22集 对props进行限制第23集 pro的全部内容,更多相关React全家桶:类--类式组件--state属性--事件绑定--类中方法的this--props属性--ref--事件处理写在前面复习类相关的知识第11集内容请搜索靠谱客的其他文章。
发表评论 取消回复