虚拟DOM:
虚拟DOM节点属性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D4Ct1ge7-1654314329582)(C:\Users\JZT\AppData\Roaming\Typora\typora-user-images\image-20220525210232894.png)]
部分真实DOM节点属性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dZakPFSi-1654314329583)(C:\Users\JZT\AppData\Roaming\Typora\typora-user-images\image-20220525210757122.png)]
js语法
可以看到用原生 js 创建虚拟 DOM 时,若是有多级嵌套结构,就需要嵌套调用下面的语法,显而易见这是非常麻烦的,而且没有相应的DOM结构。
React.createElement('span',{},'Hello,React')
jsx语法
使用此方法创建虚拟DOM可以保留DOM元素的结构,且更灵活,虽然在由虚拟DOM转为真实DOM的时候也会调用下面的方法,但是这个工作是由React帮我们完成的,留给我们的是更简单的工作。
React.createElement('span',{},'Hello,React')
既然提到了JSX语法,咱们就来聊聊它的语法规则吧。
jsx语法规则:
一定注意区分:js语句(代码)与js表达式
表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方,下面这些都是表达式:
下面这些都是语句(代码):
执行了ReactDOM.render(
之后,发生了什么?
类的特性
类中的构造器不是必须要写的,要对实例进行一些初始化的操作,如添加指定属性时才写。
如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的。
类中所定义的方法,都放在了类的原型对象上,供实例去使用。
构造器中的this是类的实例对象。
类中可以直接写赋值语句,如下代码的含义是:给Person的实例对象添加一个属性,名为name,值为jack。
class Person {
constructor(sex){
this.sex = sex
}
name = 'jack'
}
注意
render是放在MyComponent的原型对象上,供实例使用。
render中的this是MyComponent的实例对象 <=> MyComponent组件实例对象。
执行了ReactDOM.render(
之后,发生了什么?
state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合),组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)
严重注意:状态(state)不可直接更改,下面这行就是直接更改!!!
this.state.isHot = !isHot //这是错误的写法
状态必须通过setState进行更新,且更新是一种合并,不是替换。
this.setState({isHot:!isHot})
还需要注意 changeWeather 函数放在 Weather 的原型对象上,供实例使用,但是由于changeWeather 是作为onClick的回调,如下:
今天天气很{isHot ? '炎热' : '凉爽'},{wind}
所以不是通过实例调用的,是直接调用。并且由于类中的方法默认开启了局部的严格模式,所以 changeWeather 中的 this 为 undefined,所以需要在构造函数中加上这一句解决changeWeather中this指向问题。
this.changeWeather = this.changeWeather.bind(this)
每个组件对象都会有props(properties的简写)属性,每个组件对象都会有props(properties的简写)属性。
通过标签属性从组件外向组件内传递变化的数据。
注意: 组件内部不要修改props数据
方式1:(React v15.5 开始已弃用)
Person.propTypes = {
name: React.PropTypes.string.isRequired,
age: React.PropTypes.number
}
方式2:使用prop-types库进限制(需要引入prop-types库),然后在组建内添加如下代码
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
}
函数式组件使用props
组件内的标签可以定义ref属性来标识自己
使用该方法会产生一个问题,当你点击切换天气按钮后,状态发生改变,由于 react 是状态驱动页面显示,所以当状态改变,视图改变,会重新调用 render 方法,那么此时就会遇到下面代码,因为 ref 是函数式,所以在初始化执行后就释放了,现在再次遇到因为不确定该节点是否发生改变,所以 react 会先执行一次该函数将 c(触发事件的节点) 置为 null,再调用一次该函数将新的 c 传进去,所以在更新是会调用两次该函数。
{this.input1 = c;console.log('@',c);}} type="text"/>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YlEh6bNg-1654314329584)(C:\Users\JZT\AppData\Roaming\Typora\typora-user-images\image-20220526202928163.png)]
React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的。
react重写了一套事件处理。
通过onXxx属性指定事件处理函数(注意大小写)
React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —————— 为了更好的兼容性
React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ————————为了的高效
通过event.target得到发生事件的DOM元素对象 ——————————不要过度使用ref
概念:表单内输入类型的的DOM使用的时候才去取输入的值就为非受控组件。
非受控组件
实现一个登录模拟事件。
受控组件
随着输入的变化就维护了最新的状态就是受控式组件。
高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。
1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
常见的高阶函数有:Promise、setTimeout、arr.map()等等
函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
```js
function sum(a){
return(b)=>{
return (c)=>{
return a+b+c
}
}
}
```
受控组件中可以看到保存用户名和密码到状态中是两个函数的功能和写法都非常相似,那么我们能不能将这两个代码合并成一个呢?此时就可以进行一下转变,通过传入的 datatype 将用户名和密码分别保存进状态里。
//保存表单数据到状态中
saveFormData = (dataType)=>{
return (event)=>{
this.setState({[dataType]:event.target.value})
}
}
注意:这里就用到了函数柯里化,这里 onChange 事件对应的就是this.saveFormData('username')
的返回值,即
// 伪代码,只是为了更好的说明问题
(event)=>{
this.setState({[dataType]:event.target.value})
}
当然这里也是用到了闭包,当 onChange 事件触发时就会执行该函数,并拿到 dataType 更新状态。
这里不用函数柯里化也能实现,只选做如下更改。在绑定事件处理函数时,将当前DOM元素对象也传给函数就可以处理当前元素的值得改变,并将其保存到状态里。
//保存表单数据到状态中
saveFormData = (dataType,event)=>{
this.setState({[dataType]:event.target.value})
}
this.saveFormData('username',event) } type="text" name="username"/>
组件从创建到死亡它会经历一些特定的阶段。React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用。我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-faI1WbnY-1654314329584)(C:\Users\JZT\AppData\Roaming\Typora\typora-user-images\image-20220528203440463.png)]
生命周期的三个阶段(旧)
初始化阶段:由ReactDOM.render()触发—初次渲染
constructor()
componentWillMount()
render()
componentDidMount() =====> 常用
一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
更新阶段:由组件内部this.setSate()或父组件重新render触发
卸载组件:由ReactDOM.unmountComponentAtNode()触发
componentWillUnmount() =====> 常用
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
注意:shouldComponentUpdate函数时控制组件更新的阀门,若该函数返回的是false,那么一般的组件更新是无法完成的,也就说还有特殊的更新咯,当然,forceUpdate函数是强制更新,即使 shouldComponentUpdate 函数返回false,组件也能正常更新。
//控制组件更新的“阀门”
shouldComponentUpdate(){
console.log('Count---shouldComponentUpdate');
return true
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UZHy4Tbk-1654314329585)(file:///C:\Users\JZT\AppData\Local\Temp\ksohtml15052\wps1.jpg)]
生命周期的三个阶段(新)
初始化阶段:由ReactDOM.render()触发—初次渲染
constructor()
getDerivedStateFromProps
render()
componentDidMount() =====> 常用
一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
更新阶段:由组件内部this.setSate()或父组件重新render触发
卸载组件:由ReactDOM.unmountComponentAtNode()触发
componentWillUnmount() =====> 常用
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
效果展示:一个区域,每个一秒钟就在前方插入一个 li ,但是只显示当前显示的几条 li 。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DWfT0zPr-1654314329586)(C:\Users\JZT\Desktop\test.gif)]
虚拟DOM中key的作用:
用index作为key可能会引发的问题: