// react 元素 jsx对象
const el = <div>jsx </div>
// 函数组件
const App = props => <div>app</div>
// 类组件
class RenderTextNode extends React.Component {
render() {
return <span>类组件--- class </span>
}
}
不同点
● 类组件需要继承class.Component - 函数组件不需要
● 类组件可以访问周期方法 - 函数组件不能
● 类组件中可以获取到实例化化后的this,基于this做各种操作 - 函数组件不能
● 类组件中可以定义并维护state - 函数组件不能
● 类组件可以结合生命周期,做一系列关于组件从挂载,到销毁的操作是一套很清晰的流程,- 函数组件不能
react是声明式的JavaScript库,可以将一些简短独立的代码片段组合成复杂的UI界面,定位就是拿到代码片段,输出页面UI。函数式组件的简介更符合声明式的JavaScript库这一理念
组件
组件是页面中UI视觉中的一部分, 可以复用的UI视图
可以使用简短、独立的代码片段组合成视图中的一部分。组件就是用来描述UI的一部分代码片段,拥有自己的状态
组件的拆分
减少代码量、抽离各个状态之间的关联关系
比如一个应用,很多个页面组成,相互跳转,而且没有加路由,可能一个文件就几百上千行,十几个state,不好维护。这个时候拆分组件就是为了优化逻辑,减少单文件的代码量
拆分成多个小组件,单个组件内部维护自己的状态,选择自己的props,组件内部就不需要关注其他组件的实现
根组件只负责数据的公用容器,各个组件之间的通信,保证数据的正常流通性
逻辑和组件的复用性
一个页面相同的视图,或者一同重复的逻辑拆份组件可能下一个组件中也会用到。不用再去从头写,拿过来直接用。
class类组件时候我们可以去使用HOC去实现逻辑或者UI的复用性。有了hooks,让我们的可以去复用逻辑,而不仅仅是复用组件UI,也更为方便。
如果不是专门的去写公共组件,业务组件很难作到复用性。而发现了一段相同类似的逻辑或者UI,可能会把当前组件往公共组件的方向靠拢
状态分离,减少更新量
react更新是从上到下的,如果一个组件内部更新,那么子组件也会更新。如果只有一个变更导致整个组件更新,这就过分了,状态之间不需要更新的也会参与。
拆分组件后,单个更新对没有关联的state不会产生影响,只会将状态发生变更的组件更新
比如:
import { useState } from "react";
function Child() {
/*
fetch ...
compute... 8s
*/
return <p>{num}</p>
}
function App() {
const [ num, changeNum ] = useState(0);
return (
<div>
<input value={num} onChange={evl => changeNum(evl.target.value)} />
<p>num: {num} </p>
<Child />
</div>
);
}
这种情况下Child组件会在input输入时都会参与更新,所以就可以拆分出来,比如:
function Input(props) {
const {} = props;
const [ num, changeNum ] = useState(0);
return (<div>
<input value={num} onChange={evl => changeNum(evl.target.value)} />
<p>num: {num} </p>
</div>);
}
这样input的更新就不会牵扯到Child的更新
再或者就是把Child组件提升到父级, 使用children的方式插入,也不会更新
容器组件和UI组件
容器组件是用来提供数据并且管理状态。然后再把状态传到UI组件的props当中去渲染. 而合理的拆分出来,使得容器组件只考虑组件数据关联的状态。视图组件只负责去渲染UI。
拆分出来,使得逻辑更加清晰,UI的变化真正取决去数据。UI组件不会看到各种逻辑,使得后续维护不需要关注的一部分
也是更难的,我们往往很难的去彻底拆离两种组件,二者都会相互掺杂一点
- UI组件
ui代表用户页面,拿到数据渲染到页面
如果不是可复用的组件数据当然是props或者订阅本地状态管理器里的数据
ui组件不参与页面的逻辑只负责渲染当前的数据
- 容器组件
拿到开发者写的页面中的逻辑代码,把数据整理好,放出去,给redux或者Children.用数据改变视图
容器组件不参与ui渲染,只关心数据的生成。
- 受控组件
如果一个组件接收的props影响了组件内部的状态。就是受控组件
比如一个表单数据,在一个受控组件中,表单的数据是由react组件提供。推荐使用
而UI组件应该时纯粹的受控组件,展示的视图通过props来决定
- 非受控组件
由组件内部维护的状态,不受props的影响就是非受控组件
如果拆分完组件,你应该知道当前组件为什么会被拆出来,拆出来又需要做什么事。此时组件的状态就不在需要考虑做受控还是非受控的纠结,(除公共组件外)
组合
React有十分强大的组合模式。我们推荐使用组合而非继承来实现组件间的代码重用 复制官网的一句话 组合还是mixins
组件可以接受任意 props,包括基本数据类型,React 元素以及函数来渲染到页面上
如果是非ui功能的,建议提取出来,封装成模块。导入使用就可以了。而不是使用继承的方式,而且继承的方式那么多,选一种能纠结死
一个页面,或者一个大型主组件可以有很多个子组件构建而成,或者说这本就是组合而成。而子组件也可以被其他页面去使用,虽然,继承更很专业,但组合更灵活。
HOC
高阶组件 Higher-Order Components
HOC高阶组件是一个共用逻辑达到可复用性的一种高级技巧,是一种react的组合特性而形成的设计模式。
HOC是纯函数,没有副作用。不会修改传入的参数。HOC不关心使用数据的方式,只关注自己要做什么功能。
高阶组件的参数是组件,返回值为一个新组件的函数。
hoc和mixin不同,mixin会改变原对象,而hoc不会侵入,只会在外部包裹一层,只做一个函数该做的事,提供一个或多个功能。像下面这样…
解决的问题
比如我们两个组件毫不相关的组件,只是为了展示页面中的同一种视图。而去向后端接口发送请求。这个请求,返回的数据还要取自己做处理,这个时候就可以使用高阶组件。
当然也可以把这个处理的函数单独抽出来,或者写成对于页面的方法。但,这些方法是属于单个接口的返回值而言,可以,但没必要。这个时候就可以使用高阶组件。还可以加点其他公共的样式进去,更为方便。
- 可以抽取重复代码。实现组件的复用,
- 条件渲染。控制组件的渲染逻辑。
- 捕获/劫持被处理组件的生命周期。
使用
将不相关的props传递给被包裹的组件,不要保留与当前操作不相关的props。不要做可能会影响包括的组件的操作
最大化组合的方式,不要去关联很多与当前函数操作不想关的数据,当前的操作只能是一个功能,不要去改变hoc的执行结果
不要在组件的render函数中使用HOC,第一会影响性能,第二,重新挂载组件的会导致改组件及其所有的子组件的状态丢失
不要保留组件的静态方法,比如ContetxtProps或者其他…。
refs不会被传递下去,如果你想可以通过其他的方式去解决
// 包裹组件
function wrapComponent(Component) {
/* ... 做点什么,比如一次网络请求 */
return class Example extends React.Component {
componentDidMount() {
/* 做点什么... */
}
render() {
return (
<Component
{...Object.assign({}, this.props, { data: "响应数据" })}
/>
);
}
};
}
export default wrapComponent(function App({ data }) {
return <div className="App">{data}</div>; // 响应数据
});
// 反向继承
const HOC = (WrappedComponent) =>
class extends WrappedComponent {
render() {
return super.render();
}
}
// 渲染劫持
const HOC = (WrappedComponent) =>
class extends WrappedComponent {
render() {
if (this.props.isRender) {
return super.render();
} else {
return null;
}
}
}