怎样申请免费网站域名/广州网站建设方案优化
React知识点整理
- 入门
- 相关js库
- 入门示例
- 创建虚拟DOM的两种方式
- jsx语法创建虚拟DOM
- js语法创建虚拟DOM
- 如果需要嵌套标签呢? ---那么jsx的优势就出来了,下面看对比
- 小总结
- JSX语法规则
- 1.全称: JavaScript XML
- 2.react定义的一种类似于XML的JS扩展语法: JS + XML本质是
- 3.作用: 用来简化创建虚拟DOM
- 4.标签名任意: HTML标签或其它标签
- 5.标签属性任意: HTML标签属性或其它
- 6.基本语法规则
- jsx里面如果想使用class属性,不要写class,改用className
- jsx里面内联样式要使用style={{key:value}}的形式去写
- 虚拟dom必须只有一个根标签
- 标签必须闭合
- 标签首字母
- 小案例
- 当我们传递给react数组时,react会自动帮助我们进行遍历操作,给js对象,不好使
- 区分js语句和js表达式
- 模块与组件、模块化与组件化的理解
- JS模块
- 组件
- 模块化
- 组件化
- React面向组件编程
- 使用React开发者工具调试
- 定义组件
- 函数式组件
- babel编译后,会开启严格模式,因此函数组件中的this指向undefined
- 原理
- 注意
- Class类复习
- 类式组件
- 原理
- 组件实例三大核心属性----state属性,和class组件
- react中的事件绑定
- 需求: 定义一个展示天气信息的组件
- const { xxx } = this.state解释
- 类中定义的方法,已经在局部(方法体内部)开启了严格模式
- react中的this问题
- 解决react的类中this指向问题---bind
- bind不会执行方法,而是返回改变this指向后的新方法
- 实现点击切换效果
- 严重注意,React中的状态state不可直接更改
- 调用react里面的setState方法对state属性进行更新,且更新是一种合并,同名替换,不同名合并
- 类组件总结
- state的简写方式
- 类中可以直接写赋值语句,相当于给实例对象增添了一个属性
- 类中属性是放在实例对象身上的,而方法是放在原型对象身上的
- 箭头函数没有自己的this, 它的this是继承而来; 默认指向在定义它时所处的对象(宿主对象),箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。
- 简写代码
- state总结
- 强烈推荐
- 组件三大核心属性2: props
- props理解
- 作用
- ES6语法中的展开运算符
- 展开运算符在react中的应用---批量传递props属性
- 限制标签里面传递属性的类型,非空限制,默认值等...
- 需要引入prop-types.js文件
- react中的props是只读的,修改会报错
- 如何给class类自身加上属性
- props的简写方式
- 类式组件中的构造器与props
- 函数式组件使用props
- props总结
- 组件三大核心属性3: refs与事件处理
- 字符串形式的ref
- 写在标签里面的是ref,收集出来后形成的属性叫refs
- 回调函数形式的ref
- 回调ref中调用次数问题
- createRef的使用
- React中的事件处理
- 受控和非受控组件
- 非受控组件---现用现取
- 受控组件---省略ref
- 函数柯里化
- []方式读取对象的属性
- 不使用函数柯里化的写法
入门
相关js库
1.react.js:React核心库。
2.react-dom.js:提供操作DOM的react扩展库。
3.babel.min.js:解析JSX语法代码转为JS代码的库。
注意: 核心库必须在扩展库之前引入
入门示例
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Hello_React</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><!-- 此处一定要写babel,不写默认是text/javascript --><!-- 使用babel用于将jsx转换为js --><script type="text/babel">//1.创建虚拟dom//jsx语法const VDOM=<h1>大忽悠</h1>//此处一定不要写引号,因为不是字符串//2.渲染虚拟dom到页面ReactDOM.render(VDOM,document.getElementById("test"));</script></body>
</html>
创建虚拟DOM的两种方式
jsx语法创建虚拟DOM
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Hello_React</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><!-- 此处一定要写babel,不写默认是text/javascript --><!-- 使用babel用于将jsx转换为js --><script type="text/babel">//1.创建虚拟domconst VDOM=<h1 id="test">大忽悠</h1>//此处一定不要写引号,因为不是字符串//2.渲染虚拟dom到页面ReactDOM.render(VDOM,document.getElementById("test"));</script></body>
</html>
js语法创建虚拟DOM
js创建虚拟dom,就不需要引入babel的js库来解析jsx语法了
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Hello_React</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,不写默认是text/javascript --><!-- 使用babel用于将jsx转换为js --><script type="text/javascript">//1.创建虚拟dom//标签名,标签属性,标签内容const VDOM=React.createElement('h1',{id:'title'},'大忽悠');//2.渲染虚拟dom到页面ReactDOM.render(VDOM,document.getElementById("test"));</script></body>
</html>
如果需要嵌套标签呢? —那么jsx的优势就出来了,下面看对比
js写法:
<script type="text/javascript">//1.创建虚拟dom//标签名,标签属性,标签内容const VDOM=React.createElement('h1',{id:'title'},React.createElement('span',{id:'heihei'},'大忽悠'));//2.渲染虚拟dom到页面ReactDOM.render(VDOM,document.getElementById("test"));</script>
jsx写法:
<!-- 引入babel,用于将jsx转换为js --><script type="text/javascript" src="./js/babel.min.js"></script><script type="text/babel">//1.创建虚拟dom//标签名,标签属性,标签内容const VDOM=(<h1 id='test'><span id='heihei'>大忽悠</span></h1>)//2.渲染虚拟dom到页面ReactDOM.render(VDOM,document.getElementById("test"));</script>
babel会把jsx语法写的html标签内容,翻译为js写法,相当于一种语法糖
小总结
1.React提供了一些API来创建一种 “特别” 的一般js对象
const VDOM = React.createElement('xx',{id:'xx'},'xx')
上面创建的就是一个简单的虚拟DOM对象
2.虚拟DOM对象最终都会被React转换为真实的DOM
3.我们编码时基本只需要操作react的虚拟DOM相关数据, react会转换为真实DOM变化而更新界。
- 虚拟DOM本质是一个Object类型的对象(一般对象)
- 虚拟DOM比较轻,真实DOM比较重,因为虚拟DOM是React内部在用,无需真实DOM上那么多的属性
- 虚拟DOM最终会被React转换为真实DOM,呈现在页面上
JSX语法规则
1.全称: JavaScript XML
2.react定义的一种类似于XML的JS扩展语法: JS + XML本质是
React.createElement(component, props, ...children)方法的语法糖
3.作用: 用来简化创建虚拟DOM
1)写法:var ele = < h1 >Hello JSX!< /h1 >
2)注意1:它不是字符串, 也不是HTML/XML标签
3)注意2:它最终产生的就是一个JS对象
4.标签名任意: HTML标签或其它标签
5.标签属性任意: HTML标签属性或其它
6.基本语法规则
1)遇到 <开头的代码, 以标签的语法解析: html同名标签转换为html同名元素, 其它标签需要特别解析
2)遇到以 { 开头的代码,以JS语法解析: 标签中的js表达式必须用{ }包含
<script type="text/babel">const id="dhy";const text="大忽悠和小朋友"const VDOM=(<h1 id={id}><span id='heihei'>{text}</span></h1>)//2.渲染虚拟dom到页面ReactDOM.render(VDOM,document.getElementById("test"));</script>
jsx里面如果想使用class属性,不要写class,改用className
class是ES6语法里面定义类的关键字
const VDOM=(<h1 id={id}><span class='heihei'>{text}</span></h1>)
const VDOM=(<h1 id={id}><span className='heihei'>{text}</span></h1>)
jsx里面内联样式要使用style={{key:value}}的形式去写
const VDOM=(<h1 id={id}><font style={{color:'pink',fontSize:'29px'}}>小朋友</font></h1>)
虚拟dom必须只有一个根标签
此时最外层的div作为根标签
const VDOM=(<div><h1 id={id}><font style={{color:'pink',fontSize:'29px'}}>小朋友</font></h1><h1 id={id+'1'}><font style={{color:'pink',fontSize:'29px'}}>大忽悠</font></h1></div>)
标签必须闭合
错误,input标签没有闭合
const VDOM=(<input type='text'>)
正确,标签都闭合了
const VDOM=(<input type='text'/>)
标签首字母
(1):若小写字母开头,则将标签转换为html中同名标签元素,若html中无该标签对应的同名元素,则爆错
(2):若大写字母开头,react就去渲染对应的组件,若组件没有定义,则爆错
小案例
当我们传递给react数组时,react会自动帮助我们进行遍历操作,给js对象,不好使
区分js语句和js表达式
1.表达式: 一个表达式会产生一个值,可以放在任何一个需要值的地方
下面这些都是表达式:
(1) a
(2) a+b
(3) demo(1)
(4) arr.map()
(5) function test(){}
2.语句(代码):
(1)if(){}
(2) for(){}
(3)switch(){case:xxx}
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Hello_React</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">//模拟一些数据const data=['dhy','xpy']//创建虚拟DOMconst VDOM=(<div><h1>前端js框架列表</h1><ul>{//返回得到一个新数组//由react遍历当前得到的新数组data.map((item,index)=>{//读取遍历当前数组的变量值//每个li必须有自己的唯一标识,即key对应的值,并且key值不可以重复return <li key={index}>{item}</li>})}</ul></div>)//2.渲染虚拟dom到页面ReactDOM.render(VDOM,document.getElementById("test"));</script></body>
</html>
模块与组件、模块化与组件化的理解
JS模块
1.理解:向外提供特定功能的js程序, 一般就是一个js文件
2.为什么要拆成模块:随着业务逻辑增加,代码越来越多且复杂。
3.作用:复用js, 简化js的编写, 提高js运行效率
组件
1.理解:用来实现局部功能效果的代码和资源的集合(html/css/js/image等等)
2.为什么要用组件: 一个界面的功能更复杂
3.作用:复用编码, 简化项目编码, 提高运行效率
模块化
当应用的js都以模块来编写的, 这个应用就是一个模块化的应用
组件化
当应用是以多组件的方式实现, 这个应用就是一个组件化的应用
React面向组件编程
使用React开发者工具调试
定义组件
函数式组件
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Hello_React</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">//创建函数式组件//函数组件的名字,首字母大写function Demo(){//babel编译后,会开启严格模式,因此函数组件中的this指向undefinedconsole.log(this);//必须有返回值return <h1>函数组件</h1> }//渲染组件到页面// 函数组件的标签首字母要大写,自闭合ReactDOM.render(<Demo/>,document.getElementById('test'))</script></body>
</html>
babel编译后,会开启严格模式,因此函数组件中的this指向undefined
原理
1.React解析组件标签,找到了MyComponent组件
2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转换为真实DOM,随后呈现在页面中
注意
- 函数组件的名字,首字母必须大写
- 函数组件必须有返回值
- render渲染时,第一个参数填的是函数组件标签,而不是函数组件的名字
Class类复习
<script type="text/javascript">//创建一个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}`);}}//创建一个Student类,继承自Personclass Stu extends Person{constructor(name,age,grade) {//此处必须调用父类的super方法super(name,age);this.grade=grade;this.friend='小朋友';}//重写从父类继承过来的方法speak(){console.log(`姓名:${this.name},年龄:${this.age},年级:${this.grade}`);}}</script>
总结:
- 类中的构造器不是必须写的,要对实例进行一些初始化的操作,入添加指定属性时才需要写
- 如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须调用的
- 类中定义的方法,都是放在了类的原型对象上,供实例去使用
类式组件
//创建类式组件---继承React.Componentclass MyComponent extends React.Component{//render是放在MyComponent的原型对象上的,供实例使用//this是MyComponent 的实例对象//<==>MyComponent组件实例对象render(){return <h2>类组件</h2>}}//渲染组件到页面ReactDOM.render(<MyComponent/>,document.getElementById('test'));
原理
1.React解析组件标签,找到了MyComponent组件
2.发现组件是使用类定义的,随后调用该类的实例,并通过该实例调用到原型上的render方法
3.将render返回的虚拟DOM转换为真实DOM,随后呈现在页面中
组件实例三大核心属性----state属性,和class组件
react中的事件绑定
需求: 定义一个展示天气信息的组件
1.默认展示天气炎热 或 凉爽
2.点击文字切换天气
常用的两种写法:
class Weather extends React.Component{constructor(props){super(props)//初始化状态this.state={isHot:false}}render(){//读取状态const {isHot}=this.statereturn <h2 id="w">今天天气{isHot?'炎热':'凉爽'}</h2>}}//渲染组件到页面ReactDOM.render(<Weather/>,document.getElementById('test'));const w1=document.getElementById('w');w1.addEventListener('click',()=>{console.log('标题被点击了');})const w2=document.getElementById('w');w2.onclick=()=>{console.log('标题被点击了');}
推荐写法:
class Weather extends React.Component{constructor(props){super(props)//初始化状态this.state={isHot:false}}render(){//读取状态const {isHot}=this.statereturn <h2 onClick={demo}>今天天气{isHot?'炎热':'凉爽'}</h2>}}//渲染组件到页面ReactDOM.render(<Weather/>,document.getElementById('test'));function demo(){console.log('标题点击')}
const { xxx } = this.state解释
const { xxx } = this.state;
上面的写法是es6的写法,其实就相当于:
const xxx = this.state.xxx
类中定义的方法,已经在局部(方法体内部)开启了严格模式
开启了严格模式,那么方法里面的this为undefined
class A{s(){//s方法放在了类的原型对象上,供实例使用//通过A的实例对象调用s方法时,s中的this就是A的实例//否则为undefinedconsole.log(this)}}const a=new A();let b=a.sb()
直接调用函数,函数中的this默认指向window,如果全局开启了严格模式,或者在函数体内部局部开启了严格模式,那么函数中的this为undefined
例如:
function a(){'use strict'console.log(this);}a();function b(){console.log(this);}b();
react中的this问题
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Hello_React</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">// babel也会默认开启严格默认class Weather extends React.Component{constructor(props){super(props)//初始化状态this.state={isHot:false}}render(){//读取状态const {isHot}=this.statereturn <h2 onClick={this.changeWeather}>今天天气{isHot?'炎热':'凉爽'}</h2>}changeWeather(){//changeWeather放在weather的原型对象上,供实例使用//由于changeWeather是作为onClick的回调//所以不是通过实例调用的,是直接调用//类中的方法默认开启了局部的严格模式//所以changeWeather中的this为undefinedconsole.log(this)}}//渲染组件到页面ReactDOM.render(<Weather/>,document.getElementById('test'));</script></body>
</html>
解决react的类中this指向问题—bind
// babel也会默认开启严格默认class Weather extends React.Component{//构造函数里面的this指向调用的实例对象constructor(props){super(props)//初始化状态this.state={isHot:false}//解决changeWeather中的this指向问题this.changeWeather=this.changeWeather.bind(this)}render(){//读取状态const {isHot}=this.state//现在当前类上寻找changeWeather方法,找不到再去原型上找return <h2 onClick={this.changeWeather}>今天天气{isHot?'炎热':'凉爽'}</h2>}changeWeather(){//changeWeather放在weather的原型对象上,供实例使用//由于changeWeather是作为onClick的回调//所以不是通过实例调用的,是直接调用//类中的方法默认开启了局部的严格模式//所以changeWeather中的this为undefinedconsole.log(this)}}
bind不会执行方法,而是返回改变this指向后的新方法
实现点击切换效果
严重注意,React中的状态state不可直接更改
//严重注意,状态state不可直接更改
//下面这行就是直接更改,下面是错误的写法
this.state.isHot=!isHot;
调用react里面的setState方法对state属性进行更新,且更新是一种合并,同名替换,不同名合并
// babel也会默认开启严格默认class Weather extends React.Component{//构造函数里面的this指向调用的实例对象//构造器调用一次constructor(props){super(props)//初始化状态this.state={isHot:false,wind: '微风'}//解决changeWeather中的this指向问题this.changeWeather=this.changeWeather.bind(this)}//render调用1+n次//1是初始化的那次//n是状态更新的次数render(){//读取状态const {isHot,wind}=this.state//现在当前类上寻找changeWeather方法,找不到再去原型上找return <h2 onClick={this.changeWeather}>今天天气{isHot?'炎热':'凉爽'},{wind}</h2>}//事件触发几次,就调用几次changeWeather(){//获取原来的isHot的值const isHot=this.state.isHot;//状态必须通过setState进行更新,且更新时一种合并,不是替换this.setState({isHot:!isHot})}}//渲染组件到页面ReactDOM.render(<Weather/>,document.getElementById('test'));
点击后
类组件总结
- 类组件中的构造器用来初始化状态和解决this指向问题
- 改变state属性的值,必须调用setState的方法
state的简写方式
类中可以直接写赋值语句,相当于给实例对象增添了一个属性
class A{constructor(name) {this.name=name;}//类中可以直接写赋值语句,相当于给实例对象增添了一个属性//名为a,值为1a=1}const a1=new A("大忽悠");
类中属性是放在实例对象身上的,而方法是放在原型对象身上的
箭头函数没有自己的this, 它的this是继承而来; 默认指向在定义它时所处的对象(宿主对象),箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。
简写代码
<script type="text/babel">class Weather extends React.Component{//初始化状态this.state={isHot:false,wind: '微风'}render(){const {isHot,wind}=this.statereturn <h2 onClick={this.changeWeather}>今天天气{isHot?'炎热':'凉爽'},{wind}</h2>}//自定义方法---要用赋值语句的形式+箭头函数//箭头函数里面的this就是实例对象//changeWeather此时是一个属性,指向一个方法,放在了实例对象上changeWeather=()=>{const isHot=this.state.isHot;this.setState({isHot:!isHot})}}ReactDOM.render(<Weather/>,document.getElementById('test'));</script>
state总结
1.state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
2.组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)
强烈推荐
1.组件中render方法中的this为组件实例对象
2.组件自定义的方法中this为undefined,如何解决?
a)强制绑定this: 通过函数对象的bind()
b)箭头函数
3.状态数据,不能直接修改或更新
组件三大核心属性2: props
效果:
class Person extends React.Component{render(){const {name,age}=this.props; return (<ul><li>姓名:{name}</li><li>年龄:{age}</li></ul>) }}ReactDOM.render(<Person name="bob" age="18"/>,document.getElementById('test'));
props理解
1.每个组件对象都会有props(properties的简写)属性
2.组件标签的所有属性都保存在props中
作用
1.通过标签属性从组件外向组件内传递变化的数据
2.注意: 组件内部不要修改props数据
ES6语法中的展开运算符
//展开数组let arr1=[1,2,3]let arr2=[4,5,6]console.log(...arr1)//连接数组let arr3=[...arr1,...arr2]//在函数中使用--只能作为最后一个参数使用function sum(...nums){return nums.reduce((preValue,curValue)=>{return preValue+curValue },0)//0是初始累加值}console.log(sum(1,2,3))//构造字面量对象时使用展开语法let person={name: 'tom',age: 18}//展开对象必须在{}中使用let per={...person}console.log(per)//合并对象let per3={...person,name: 'jack'}console.log(per3)
等同写法:
//展开对象必须在{}中使用console.log({...person})//合并对象console.log({...person,name: 'jack'})
展开运算符在react中的应用—批量传递props属性
class Person extends React.Component{render(){const {name,age}=this.props; return (<ul><li>姓名:{name}</li><li>年龄:{age}</li></ul>) }}const p={name:'大忽悠',age: '18'}//...p只能在标签里面使用,外部使用会报错//babel可以解析标签里面的...pReactDOM.render(<Person {...p}/>,document.getElementById('test'));
限制标签里面传递属性的类型,非空限制,默认值等…
需要引入prop-types.js文件
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Hello_React</title></head><body><!-- 准备好一个容器 --><div id="test1"></div><div id="test2"></div><div id="test3"></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><!-- 引入prop-types,用于对组件标签属性进行限制 --><script type="text/javascript" src="./js/prop-types.js"></script><script type="text/babel">class Person extends React.Component{render(){const {name,age,speak}=this.props; return (<ul><li>姓名:{name}</li><li>年龄:{age}</li></ul>) }}//对标签属性进行类型,必要性的限制Person.propTypes={//限制name必须传,且为字符串name:PropTypes.string.isRequired,//限制年龄为数字类型age:PropTypes.number,//限制speak必须为函数类型//这里用func表示函数类型,而不是function//因为function是js中的关键字speak: PropTypes.func}//指定默认标签属性值//如果标签里面没传对应的属性,那么赋予属性默认值Person.defaultProps={age: 18}function speak(){return "dhy like xpy";}ReactDOM.render(<Person name="小盆友" age={3} speak={speak}/>,document.getElementById('test1'));ReactDOM.render(<Person name="超级大忽悠"/>,document.getElementById('test2'));const p={name:'大忽悠',age:18}//...p只能在标签里面使用,外部使用会报错ReactDOM.render(<Person {...p}/>,document.getElementById('test3'));</script></body>
</html>
react中的props是只读的,修改会报错
class Person extends React.Component{render(){const {name,age,speak}=this.props; //props是只读的,下面写法会报错this.props.name="匿名"return (<ul><li>姓名:{name}</li><li>年龄:{age}</li></ul>) }}
如何给class类自身加上属性
实例对象身上没有b属性,类身上才有
class A{constructor(a) {this.a=a; }//给实例对象上身上添加属性 a=1; static b=2}const a=new A(10);console.log(a);
如何给类身上添加属性:
方法一:
class A{//给实例对象上身上添加属性 a=1; }//给class类自身加上属性A.b=2;console.log(A); console.log(A.b)
方法二: static方法
class A{constructor(a) {this.a=a; }//给实例对象上身上添加属性 a=1; static b=2}console.log(A);console.log(A.b);
props的简写方式
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Hello_React</title></head><body><!-- 准备好一个容器 --><div id="test1"></div><div id="test2"></div><div id="test3"></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><!-- 引入prop-types,用于对组件标签属性进行限制 --><script type="text/javascript" src="./js/prop-types.js"></script><script type="text/babel">class Person extends React.Component{render(){const {name,age}=this.props; return (<ul><li>姓名:{name}</li><li>年龄:{age}</li></ul>) }static propTypes={name:PropTypes.string.isRequired,age:PropTypes.number}static defaultProps={age: 18}}ReactDOM.render(<Person name="小盆友" age={3}/>,document.getElementById('test1'));ReactDOM.render(<Person name="超级大忽悠"/>,document.getElementById('test2'));</script></body>
</html>
类式组件中的构造器与props
如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。
在 React 组件挂载之前,会调用它的构造函数。在为 React.Component 子类实现构造函数时,应在其他语句之前前调用 super(props)。否则,this.props 在构造函数中可能会出现未定义的 bug。
constructor(props){//构造器是否接受props,是否传递给super,取决于是否希望构造器中调用//this访问propssuper(props);//如果super(),那么this.props是undefinedconsole.log(this.props)}
函数式组件使用props
//创建函数式组件function Per(props){const {name,age}=propsreturn (<ul><li>{name}</li><li>{age}</li></ul>) }Per.propTypes={name:PropTypes.string.isRequired,age:PropTypes.number }Per.defaultProps={age: 18}//渲染组件到页面ReactDOM.render(<Per name="大忽悠"/>,document.getElementById('test'))
props总结
1.内部读取某个属性值
this.props.name
2.对props中的属性值进行类型限制和必要性限制
第一种方式(React v15.5 开始已弃用):
Person.propTypes = {name: React.PropTypes.string.isRequired,age: React.PropTypes.number
}
第二种方式(新):使用prop-types库进限制(需要引入prop-types库)
Person.propTypes = {name: PropTypes.string.isRequired,age: PropTypes.number.
}
3.扩展属性: 将对象的所有属性通过props传递
<Person {...person}/>
4.默认属性值:
Person.defaultProps = {age: 18,sex:'男'
}
5.组件类的构造函数
constructor(props){super(props)console.log(props)//打印所有属性
}
6.props为只读,不可以修改
7.函数式组件可以通过参数获取到props对象,然后进行操作,也可以进行类型和默认值操作
8.PropTypes大写的这个是导入js包后,全局新增的一个对象,而Person.propTypes是我们需要给类上添加的一个属性,react底层会去寻找当前类上名字相同的属性,然后进行遍历,设置对应的类型限制和默认值
组件三大核心属性3: refs与事件处理
- 组件内的标签可以定义ref属性来标识自己
字符串形式的ref
写在标签里面的是ref,收集出来后形成的属性叫refs
//创建组件class Demo extends React.Component{showData=()=>{//refs里面的input1属性对应input标签的dom对象const{input1}=this.refs;input1.value="大忽悠到此一游";}showData2=()=>{const{input2}=this.refs;input2.value="呜呜呜";}render(){return (<div><input ref="input1" type="text" placeholder='点击按钮提示数据'/> <button onClick={this.showData}>点我提示左侧数据</button> <input ref="input2" onBlur={this.showData2} type="text" placeholder='失去焦点,提示数据'/></div>)}}//渲染组件到页面ReactDOM.render(<Demo/>,document.getElementById('test'));
回调函数形式的ref
//创建组件class Demo extends React.Component{showData=()=>{const{input1}=this;input1.value="大忽悠到此一游";}showData2=()=>{const{input2}=this;input2.value="呜呜呜";}// c就是当前的input标签的dom对象//将其挂载到当前实例上的input1属性上render(){return (<div><input ref={c=>this.input1=c} type="text" placeholder='点击按钮提示数据'/> <button onClick={this.showData}>点我提示左侧数据</button> <input ref={c=>this.input2=c} onBlur={this.showData2} type="text" placeholder='失去焦点,提示数据'/></div>)}}//渲染组件到页面ReactDOM.render(<Demo/>,document.getElementById('test'));
回调ref中调用次数问题
如下的内联函数,每次渲染都会创建一个新的,并且先清空之前的ref,传入null,第二次传入dom元素
<input ref={c=>this.input1=c} type="text" placeholder='点击按钮提示数据'/>
如果是class的绑定函数,那么react就知道当前绑定函数被调用过,不会新创建函数实例,也就不会在更新的时候调用对应的函数了
React 初学 - 回调ref中调用次数的问题 - 个人笔记26
createRef的使用
createRef创建出来的容器,只能放一个dom元素,后放进去的dom元素会覆盖之前放入的dom元素
//创建组件class Demo extends React.Component{//React.createRef调用后返回一个容器//该容器可以存储ref所标识的节点,该容器是专人专用//后来的覆盖之前的myref=React.createRef()showData=()=>{console.log(this.myref)}//将input标签放入myref容器中//如果将button也放入myref容器中//那么会覆盖之前的inputrender(){return (<div><input ref={this.myref} type="text" placeholder='点击按钮提示数据'/> <button ref={this.myref} onClick={this.showData}>点我提示左侧数据</button> </div>)}}//渲染组件到页面ReactDOM.render(<Demo/>,document.getElementById('test'));
React中的事件处理
1.通过onXxx属性指定事件处理函数(注意大小写)
1)React使用的是自定义(合成)事件, 而不是使用的原生DOM事件----为了更好的兼容性
2)React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)---为了高效
2.通过event.target得到发生事件的DOM元素对象
当发生事件的元素正好是我们需要操作的元素时,ref可以不写,通过event可以获取到对应的dom元素
//创建组件class Demo extends React.Component{showData=(event)=>{console.log(event.target)}render(){return (<div><input ref={this.myref} type="text" placeholder='点击按钮提示数据'/> <button onClick={this.showData}>点我提示左侧数据</button> </div>)}}//渲染组件到页面ReactDOM.render(<Demo/>,document.getElementById('test'));
受控和非受控组件
非受控组件—现用现取
//创建组件class Demo extends React.Component{handleSubmit=(event)=>{//阻止表单提交event.preventDefault()const{nameInput,pwdInput}=this//模板字符串alert(`输入的用户名:${nameInput.value},输入的密码:${pwdInput.value}`)}render(){return(<form onSubmit={this.handleSubmit}>用户名: <input ref={c=>this.nameInput=c} type="text" name="username"/><br/>密码: <input ref={c=>this.pwdInput=c} type="password" name="password"/><br/><button>登录</button></form>)}}//渲染组件到页面ReactDOM.render(<Demo/>,document.getElementById('test'));
受控组件—省略ref
//创建组件class Demo extends React.Component{//初始化状态state={name: '',pwd: ''}//保存用户名到状态中saveName=(event)=>{this.setState({name:event.target.value})}//保存密码到状态中savePwd=(event)=>{this.setState({pwd:event.target.value})}handleSubmit=(event)=>{//阻止表单提交event.preventDefault()const{name,pwd}=this.statealert(`输入的用户名:${name},输入的密码:${pwd}`)}render(){return(<form onSubmit={this.handleSubmit}>用户名: <input onChange={this.saveName} type="text" name="username"/><br/>密码: <input onChange={this.savePwd} type="password" name="password"/><br/><button>登录</button></form>)}}//渲染组件到页面ReactDOM.render(<Demo/>,document.getElementById('test'));
函数柯里化
高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数
- 若A函数,接收的参数是一个函数,那么A就可以成为高阶函数
- 若A函数,调用的返回值依然是一个函数,那么A就可以成为高阶函数
- 函数柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式
[]方式读取对象的属性
let a='name'let obj={name:"大忽悠"}console.log(obj[a])//下面是错误写法console.log(obj.a)
//创建组件class Demo extends React.Component{//初始化状态state={name: '',pwd: ''}//保存表单数据到状态中saveFormData=(dataType)=>{return (event)=>{//[]是读取变量里面的值this.setState({[dataType]:event.target.value})}}handleSubmit=(event)=>{//阻止表单提交event.preventDefault()const{name,pwd}=this.statealert(`输入的用户名:${name},输入的密码:${pwd}`)}render(){return(<form onSubmit={this.handleSubmit}>用户名: <input onChange={this.saveFormData('name')} type="text" name="username"/><br/>密码: <input onChange={this.saveFormData('pwd')} type="password" name="password"/><br/><button>登录</button></form>)}}//渲染组件到页面ReactDOM.render(<Demo/>,document.getElementById('test'));
onChange事件这里接收到的是saveFormData函数的返回值,因为saveFormData函数的返回值还是一个函数,onChange调用返回的函数时,会往里面传入一个event对象
不使用函数柯里化的写法
//创建组件class Demo extends React.Component{//初始化状态state={name: '',pwd: ''}//保存表单数据到状态中saveFormData=(dataType,event)=>{//[]是读取变量里面的值this.setState({[dataType]:event.target.value})}handleSubmit=(event)=>{//阻止表单提交event.preventDefault()const{name,pwd}=this.statealert(`输入的用户名:${name},输入的密码:${pwd}`)}render(){return(<form onSubmit={this.handleSubmit}>用户名: <input onChange={(event)=>{this.saveFormData('name',event)}} type="text" name="username"/><br/>密码: <input onChange={(event)=>{this.saveFormData('pwd',event)}} type="password" name="password"/><br/><button>登录</button></form>)}}//渲染组件到页面ReactDOM.render(<Demo/>,document.getElementById('test'));