类式组件与函数式组件
在react中书写的组件主要有两种方式,类式组件与函数式组件。其中概念我们都很清楚了,主要还是两者之间的差别不是很明白,今天来捋一捋。
类式组件
学习类式组件的前提知识点,建议看看我写的一个文章【es6入门】类的声明与继承,class与extends语法糖
<script type="text/babel">
//1.创建类式组件
class MyComponent extends React.Component { // 继承了react组件很多属性
render(){
//render是放在哪里的?—— MyComponent的原型对象上,供实例使用。
//render中的this是谁?—— MyComponent的实例对象 也称为 MyComponent组件实例对象。
console.log('render中的this:',this);
return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
}
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
</script>
分两步去理解:
第一、每个类组件都是继承了React.Component的子类,这样子类也就拥有父类的很多属性或者说是api,例如钩子函数。
第二、执行了ReactDOM.render(<MyComponent/>...)
之后,发生了什么?
- React解析组件标签,找到了MyComponent组件。
- 发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
- 将render函数返回的jsx转虚拟DOM再转为真实DOM,随后呈现在页面中。
函数式组件
<script type="text/babel">
//1.创建函数式组件
function MyComponent(){
console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>, document.getElementById('test'))
</script>
能看到相对于类组件来说,简单了很多,更【轻】了。
只需要看执行了ReactDOM.render(<MyComponent/>...)
之后,发生了什么?
- React解析组件标签,找到了MyComponent组件。
- 发现组件是使用函数定义的,随后调用该函数,将函数返回的jsx转成虚拟DOM再转为真实DOM,随后呈现在页面中。
讨论出结论
相同点
一、可以看到类组件和函数组件最终都是要执行render函数,后面走的流程是一样的。
不同点
一、书写方式不同
二、渲染时获取的状态不同
在类组件中我们获取props的某个值时,是这样获取的this.props.name
,在函数组件中我们是这样获取的props.name
,两者其实是大有区别的。
假设这俩个组件中都有一个方法,点击3s后打印出name的值。当开始点击后,父组件把name这个值修改了,类组件3后s打印出的值是最新的,而函数组件打印出的是旧的。
我的理解是,类组件通过this的方式去获取props,拿到的永远是最新的值。
而函数组件的props是从参数传递进来,每次函数组件render时,props都是当前状态下的数据,那么在点击时的函数组件拿到的name一直就是旧值并等待3s后打印,等父组件的name修改后,函数组件重新render,props的值更新,但是执行函数的还是上一个状态的函数组件,所以3s后打印的是旧值。
这并不是react函数组件的设计缺陷,而是本身设计如此。
那么类组件想模拟函数组件的这种特性咋办,可以利用闭包:
class MyComponent extends React.Component {
render(){
const { name } = this.props
const clickFn = () => {
setTimeOut(()=>{
console.log(name)
}, 3000)
}
return <div onClick={clickFn}>点击</div>
}
}
这样,当点击时,clickFn与name就形成了闭包关系,在内存中不会被销毁。
非受控组件与受控组件
我是这么理解的,非受控是数据发现变化后需要人为去触发组件事件,组件才去处理数据;而受控组件是只要数据有变化,组件就会自动处理数据;
事件久了可能不好记,其实只需要记住是数据控制组件自动更新的就是受控组件,反之。
写个受控组件的例子:
class Demo1 extends Component {
constructor(props) {
super(props);
this.state = {
value: props.value
}
}
handleChange(e) {
this.setState({
value: e.target.value
})
}
render() {
return (
<input value={this.state.value} onChange={e => this.handleChange(e)}/>
)
}
}
思考:
- 一般受控组件优于非受控组件,因为非受控组件会可能使用refs(性能方面的考量)。
- 要用受控还是非受控写法可能更多从代码的设计上去出发的。
- 更多的时候,可能还是二者混用,所以平时写代码的时候多多思考下,是否要做到统一。
简单组件和复杂组件
只需要记住:拥有状态的组件就是复杂组件,就是state、prop等。
所以基本上都是复杂组件,这个概念没必要记。
高阶组件
简称HOC,是用来做组件公共逻辑抽离的一种解决方案,本身是一个函数,入参为一个组件,经过公共逻辑的附加或处理后返回一个新的组件。
个人之前尝试使用过,但对其使用上的概念还是很模糊的,比如它和专门写个公共组件的区别在哪里,有什么优势?
待我以后研究