前言
之前分享了一篇关于如何学习React的文章,趁手还热乎,分享关于React
中的组件化,来串一串零零碎碎的知识点。
开始之前,聊一下关于Vue
和React
等MVVM
框架都有的特性,就是:
- 声明式:用声明式的方式代替命令式编写UI,无需关注复杂的DOM层
- 数据驱动:通过数据驱动自动更新和渲染组件,从而自动更新页面UI
- 组件化:能够管理自身状态的组件,并通过合适的方式组装成复杂的UI
声明式的特性能够让开发者解放双手不去处理复杂的DOM层操作,而将注意力转移到更为重要的逻辑层,Vue
和React
会将声明式的代码转换为命令式的DOM操作,并且利用各自高效的算法进行按需更新。
数据驱动是让页面活起来的关键,为了能够更加高效响应数据的变化并以最快的速度更新到视图,Vue
和React
各自采用了不通的策略来处理数据。
而组件化能够将企业级大型应用通过分治
思想,切割为一个个小模块(组件)进行管理,各自组件只需维护好自身状态的同时进行自由组合,开发者只需要通过【搭积木】的方式合理拼装组件,最终呈现完整的页面给用户。
了解这些对于学习React
组件有什么关联呢?别急,先从全局的视觉看整体,接下来你会发现,组件的构成就是由这三部分组成。
组件类型
React
天生就支持两种组件编写方式:类组件
和函数式组件
,而这两种组件的差别在哪,何时使用它们?接下来先了解一下
何为类组件
所谓类组件,就是在ES6
以来的class
的写法,它继承于React.Component
类,下面是一个类组件的示例:
js
class chileComponent extends React.Component {
// 维护自身组件的 state
state = {
name: "",
};
// 生命周期方法 didMount
componentDidMount() {
// 省略业务逻辑
}
// 自定义的实例方法(合成事件)
changeName = (newName) => {
// 更新 state
this.setState({
name: newName,
});
};
// 生命周期方法 render
render() {
return (
<div className="box_container">
<p>{this.state.name}</p>
<button onClick={this.changeName}>点我修改名称</button>
</div>
);
}
}
何为函数式组件
顾名思义,就是以函数形式 存在的组件,返回JSX
对象的形式给编译器进行编译。在React
的发展历程中,函数式组件经过了从开始的无状态
组件成长为具有React Hooks
加持的有状态组件,这里讨论Hooks
之前的组件。以下是一个经典的函数式组件:
js
function childComponent(props) {
const { name } = props;
return (
<div className="box_container">
<p>{`该function 组件所接收到来自外界的数据是:[${name}]`}</p>
</div>
);
}
两者对比
从上面的两个例子中,我们可以看出它们存在以下的差异:
- 类组件含有多个生命周期函数如
componentDidUpdate
,而函数式组件没有,意味着它不能够处理执行一些副作用操作 - 类组件需要继承
React.component
,而函数式组件不需要 - 类组件可以通过
state
维护自身的状态,而函数式组件只能接受来自外界的数据 - 类组件中可以拿到自身组件的实例对象
this
,而函数式组件中不需要 - 类组件创建过程具体更大的内存开销,而函数式组件没有实例化过程和生命周期,一定程度上性能会更佳
这样对比看起来是不是觉得类组件的功能比较强大呀,意味着比函数式组件好,应该选择使用类组件。
这样判断就有点过去片面了,凡事都有两面性,类组件虽然能力比较强大,但是也有不足的地方;而函数式组件看起来能力很有限,但是也有它存在的优点。应该怎么样去权衡选择呢,接下来深入认识这两种类型组件。
深入认识类组件
再来看看上面这个类组件的例子:
js
class chileComponent extends React.Component {
// 维护自身组件的 state
state = {
name: "",
};
// 生命周期方法 didMount
componentDidMount() {
// 省略业务逻辑
}
// 自定义的实例方法(合成事件)
changeName = (newName) => {
// 更新 state
this.setState({
name: newName,
});
};
// 生命周期方法 render
render() {
return (
<div className="box_container">
<p>{this.state.name}</p>
<button onClick={this.changeName}>点我修改名称</button>
</div>
);
}
}
可以看到类组件提供了诸如componentDidMount
、getSnapshotBeforeUpdate
、shouldComponentUpdate
等生命周期钩子函数和状态State
,它预制了很多现成的东西让开发者可以根据自己需要进行调度,不管你实际上用没用到我都会给你,至于你怎么使用我不管,这是你的事情,跟我无关!
用过mixins
的朋友就知道,它可以混入很多工具方法或者状态给到子组件,而不管子组件是否真的需要。
也就是说当我用类组件渲染一个静态的节点时,声明周期函数和状态都会挂在到组件的实例上,实际上并不需要。另外,提供众多的API
给到开发者调用,当使用不当的时候,极易造成组件渲染问题或者产生不可预知的bug
,有人会说这是你开发者的问题,跟我React
有半毛钱关系?的确是的,React
无法决定开发者如何使用它,但是作为一个优秀的开源框架来说,在架构设计上减少开发者的心智负担是必须考虑的事情!
所以,在这个层面上得出一个结论是:**类组件它太重了!**在很多场景下,使用类组件会产生不必要的麻烦和性能开销,并且在组件封装层面,想要实现逻辑与组件解耦,需要学习更加高级的技巧如HOC高阶组件
或者Render Props
进一步封装。
深入认识函数式组件
来看看这个普通的函数式组件例子:
js
function childComponent(props) {
const { name } = props;
return (
<div className="box_container">
<p>{`该function 组件所接收到来自外界的数据是:[${name}]`}</p>
</div>
);
}
可以看到,它就是一个普通函数,返回一个JSX
对象,除了可以接受来自外界的Props
数据,没有内置的状态管理能力,拥有轻量、灵活、易维护的特点,在React hooks
诞生之前,它的应用场景非常有限!
总结
类组件拥有强大的生命周期钩子函数和状态管理能力,能够应对复杂的业务场景,但是在组件封装和逻辑管理方面也变的复杂繁琐,同时让维护和测试变的困难;
而类组件轻量、简单,函数一等公民的角色可以让组件变的容易测试,但它无法维护自身状态,很多场景下想用它但是用不了的尴尬地步,为了解决这种鸡肋的情况,React Hooks
应运而生!