componentWillUpdate
生命周期在shouldComponentUpdate
返回true后被触发。在这两个生命周期只要视图更新就会触发,因此不能再这两个生命周期中使用setState。否则会导致死循环
在shouldComponentUpdate 这个方法中,这个方法主要用来判断是否需要调用render方法重绘DOM
因为DOM的描绘非常消耗性能,如果能够在shouldComponentUpdate方法中能写出更优化的 diff算法,极大的提高性能
类组件中的优化手段
PureComponent
作为基类。React.memo
高阶函数包装组件。shouldComponentUpdate
生命周期函数来自定义渲染逻辑。方法组件中的优化手段
useMemo
。useCallBack
。其他方式
Suspense
和 lazy 进行懒加载,例如:import React, { lazy, Suspense } from "react";
export default class CallingLazyComponents extends React.Component {
render() {
var ComponentToLazyLoad = null;
if (this.props.name == "Mayank") {
ComponentToLazyLoad = lazy(() => import("./mayankComponent"));
} else if (this.props.name == "Anshul") {
ComponentToLazyLoad = lazy(() => import("./anshulComponent"));
}
return (
<div>
<h1>This is the Base User: {this.state.name}</h1>
<Suspense fallback={<div>Loading...</div>}>
<ComponentToLazyLoad />
</Suspense>
</div>
)
}
}
componentWillMount
:在渲染之前执行,用于根组件中的 App 级配置。componentDidMount
:在第一次渲染之后执行,可以在这里做AJAX请求,DOM 的操作或状态更新以及设置事件监听器。componentWillReceiveProps
:在初始化render
的时候不会执行,它会在组件接受到新的状态(Props)时被触发,一般用于父组件状态更新时子组件的重新渲染shouldComponentUpdate
:确定是否更新组件。默认情况下,它返回true
。如果确定在 state
或 props
更新后组件不需要在重新渲染,则可以返回false
,这是一个提高性能的方法。componentWillUpdate
:在shouldComponentUpdate
返回 true
确定要更新组件之前件之前执行。componentDidUpdate
:它主要用于更新DOM以响应props
或state
更改。componentWillUnmount
:它用于取消任何的网络请求,或删除与组件关联的所有事件监听器。在 React中,组件负责控制和管理自己的状态。
如果将HTML中的表单元素( input、 select、 textarea等)添加到组件中,当用户与表单发生交互时,就涉及表单数据存储问题。根据表单数据的存储位置,将组件分成约東性组件和非约東性组件。
约束性组件( controlled component)就是由 React控制的组件,也就是说,表单元素的数据存储在组件内部的状态中,表单到底呈现什么由组件决定。
如下所示, username没有存储在DOM元素内,而是存储在组件的状态中。每次要更新 username时,就要调用 setState更新状态;每次要获取 username的值,就要获取组件状态值。
class App extends Component {
//初始化状态
constructor(props) {
super(props);
this.state = {
username: "有课前端网",
};
}
//查看结果
showResult() {
//获取数据就是获取状态值
console.log(this.state.username);
}
changeUsername(e) {
//原生方法获取
var value = e.target.value;
//更新前,可以进行脏值检测
//更新状态
this.setState({
username: value,
});
}
//渲染组件
render() {
//返回虚拟DOM
return (
<div>
<p>
{/*输入框绑定va1ue*/}
<input type="text" onChange={this.changeUsername.bind(this)} value={this.state.username} />
</p>
<p>
<button onClick={this.showResult.bind(this)}>查看结果</button>
</p>
</div>
);
}
}
非约束性组件( uncontrolled component)就是指表单元素的数据交由元素自身存储并处理,而不是通过 React组件。表单如何呈现由表单元素自身决定。
如下所示,表单的值并没有存储在组件的状态中,而是存储在表单元素中,当要修改表单数据时,直接输入表单即可。有时也可以获取元素,再手动修改它的值。当要获取表单数据时,要首先获取表单元素,然后通过表单元素获取元素的值。
注意:为了方便在组件中获取表单元素,通常为元素设置ref属性,在组件内部通过refs属性获取对应的DOM元素。
class App extends Component {
//查看结果
showResult() {
//获取值
console.log(this.refs.username.value);
//修改值,就是修改元素自身的值
this.refs.username.value = "专业前端学习平台";
//渲染组件
//返回虚拟DOM
return (
<div>
<p>
{/*非约束性组件中,表单元素通过 defaultvalue定义*/}
<input type="text" ref=" username" defaultvalue="有课前端网" />
</p>
<p>
<button onClick={this.showResult.bind(this)}>查看结果</button>
</p>
</div>
);
}
}
虽然非约東性组件通常更容易实现,可以通过refs直接获取DOM元素,并获取其值,但是 React建议使用约束性组件。主要原因是,约東性组件支持即时字段验证,允许有条件地禁用/启用按钮,强制输入格式等。
useImperativeHandle
:const { forwardRef, useRef, useImperativeHandle } = React;
const Child = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
getAlert() {
alert("getAlert from Child");
}
}));
return <h1>Hi</h1>;
});
const Parent = () => {
const childRef = useRef();
return (
<div>
<Child ref={childRef} />
<button onClick={() => childRef.current.getAlert()}>Click</button>
</div>
);
};
>= react@16.4
),可以使用 createRef:const { Component } = React;
class Parent extends Component {
constructor(props) {
super(props);
this.child = React.createRef();
}
onClick = () => {
this.child.current.getAlert();
};
render() {
return (
<div>
<Child ref={this.child} />
<button onClick={this.onClick}>Click</button>
</div>
);
}
}
class Child extends Component {
getAlert() {
alert('getAlert from Child');
}
render() {
return <h1>Hello</h1>;
}
}
该函数会在
setState
函数调用完成并且组件开始重渲染的时候被调用,我们可以用该函数来监听渲染是否完成:
this.setState(
{ username: 'tylermcginnis33' },
() => console.log('setState has finished and the component has re-rendered.')
)
this.setState((prevState, props) => {
return {
streak: prevState.streak + props.count
}
})
相同点: 组件是 React 可复用的最小代码片段,它们会返回要在页面中渲染的 React 元素。也正因为组件是 React 的最小编码单位,所以无论是函数组件还是类组件,在使用方式和最终呈现效果上都是完全一致的。
我们甚至可以将一个类组件改写成函数组件,或者把函数组件改写成一个类组件(虽然并不推荐这种重构行为)。从使用者的角度而言,很难从使用体验上区分两者,而且在现代浏览器中,闭包和类的性能只在极端场景下才会有明显的差别。所以,基本可认为两者作为组件是完全一致的。
不同点:
Store 就是把它们联系到一起的对象。Store 有以下职责:
要注意以下几点。
通常,使用 Webpack的 DefinePlugin方法将 NODE ENV设置为 production。这将剥离 propType验证和额外的警告。除此之外,还可以减少代码,因为 React使用 Uglify的dead-code来消除开发代码和注释,这将大大减少包占用的空间。
React-Redux
提供connect
方法联系起来我们应当将AJAX 请求放到
componentDidMount
函数中执行,主要原因有下
React
下一代调和算法 Fiber
会通过开始或停止渲染的方式优化应用性能,其会影响到 componentWillMount
的触发次数。对于 componentWillMount
这个生命周期函数的调用次数会变得不确定,React
可能会多次频繁调用 componentWillMount
。如果我们将 AJAX
请求放到 componentWillMount
函数中,那么显而易见其会被触发多次,自然也就不是好的选择。AJAX
请求放置在生命周期的其他函数中,我们并不能保证请求仅在组件挂载完毕后才会要求响应。如果我们的数据请求在组件挂载之前就完成,并且调用了setState
函数将数据添加到组件状态中,对于未挂载的组件则会报错。而在 componentDidMount
函数中进行 AJAX
请求则能有效避免这个问题key可以帮助 React跟踪循环创建列表中的虚拟DOM元素,了解哪些元素已更改、添加或删除。
每个绑定key的虚拟DOM元素,在兄弟元素之间都是独一无二的。在 React的和解过程中,比较新的虛拟DOM树与上一个虛拟DOM树之间的差异,并映射到页面中。key使 React处理列表中虛拟DOM时更加高效,因为 React可以使用虛拟DOM上的key属性,快速了解元素是新的、需要删除的,还是修改过的。如果没有key,Rεat就不知道列表中虚拟DOM元素与页面中的哪个元素相对应。所以在创建列表的时候,不要忽略key。
一、更容易复用代码
二、清爽的代码风格+代码量更少
缺点
状态不同步
不好用的useEffect,
因为不能保证 props. children将是一个数组。
以下面的代码为例。
<Parent>
<h1>有课前端网</h1>
</Parent>
在父组件内部,如果尝试使用 props.children. map映射子对象,则会抛出错误,因为props. children是一个对象,而不是一个数组。
如果有多个子元素, React会使 props.children成为一个数组,如下所示。
<Parent>
<h1>有课前端网</h1>
<h2>前端技术学习平台</h2>
</Parent>;
//不建议使用如下方式,在这个案例中会抛出错误。
class Parent extends Component {
render() {
return <div> {this.props.children.map((obj) => obj)}</div>;
}
}
建议使用如下方式,避免在上一个案例中抛出错误。
class Parent extends Component {
render() {
return <div> {React.Children.map(this.props.children, (obj) => obj)}</div>;
}
}
(1)共同点
(2)不同点
在未来的趋势上,两个 API 是会长期共存的,暂时没有删减合并的计划,需要开发者根据场景去自行选择。React 团队的建议非常实用,如果实在分不清,先用 useEffect,一般问题不大;如果页面有异常,再直接替换为 useLayoutEffect 即可。
**
diff 算法是指生成更新补丁的方式,主要应用于虚拟 DOM 树变化后,更新真实 DOM。所以 diff 算法一定存在这样一个过程:触发更新 → 生成补丁 → 应用补丁。
React 的 diff 算法,触发更新的时机主要在 state 变化与 hooks 调用之后。此时触发虚拟 DOM 树变更遍历,采用了深度优先遍历算法。但传统的遍历方式,效率较低。为了优化效率,使用了分治的方式。将单一节点比对转化为了 3 种类型节点的比对,分别是树、组件及元素,以此提升效率。
以上是经典的 React diff 算法内容。自 React 16 起,引入了 Fiber 架构。为了使整个更新过程可随时暂停恢复,节点与树分别采用了 FiberNode 与 FiberTree 进行重构。fiberNode 使用了双链表的结构,可以直接找到兄弟节点与子节点。整个更新过程由 current 与 workInProgress 两株树双缓冲完成。workInProgress 更新完成后,再通过修改 current 相关指针指向新节点。
Vue 的整体 diff 策略与 React 对齐,虽然缺乏时间切片能力,但这并不意味着 Vue 的性能更差,因为在 Vue 3 初期引入过,后期因为收益不高移除掉了。除了高帧率动画,在 Vue 中其他的场景几乎都可以使用防抖和节流去提高响应性能。
componentWillMount生命周期发生在首次渲染前,一般使用的小伙伴大多在这里初始化数据或异步获取外部数据赋值。初始化数据,react官方建议放在constructor里面。而异步获取外部数据,渲染并不会等待数据返回后再去渲染
class Example extends React.Component {
state = {
value: ''
};
componentWillMount() {
this.setState({
value: this.props.source.value
});
this.props.source.subscribe(this.handleChange);
}
componentWillUnmount() {
this.props.source.unsubscribe(this.handleChange );
}
handleChange = source => {
this.setState({
value: source.value
});
};
}
UNSAFE_componentWillMount
替换componentWillMount
了吧componentWillReceiveProps生命周期是在props更新时触发。一般用于props参数更新时同步更新state参数。但如果在componentWillReceiveProps生命周期直接调用父组件的某些有调用setState的函数,会导致程序死循环
// 如下是子组件componentWillReceiveProps里调用父组件改变state的函数示例
class Parent extends React.Component{
constructor(){
super();
this.state={
list: [],
selectedData: {}
};
}
changeSelectData = selectedData => {
this.setState({
selectedData
});
}
render(){
return (
<Clild list={this.state.list} changeSelectData={this.changeSelectData}/>
);
}
}
...
class Child extends React.Component{
constructor(){
super();
this.state={
list: []
};
}
componentWillReceiveProps(nextProps){
this.setState({
list: nextProps.list
})
nextProps.changeSelectData(nextProps.list[0]); //默认选择第一个
}
...
}
componentWillUpdate生命周期在视图更新前触发。一般用于视图更新前保存一些数据方便视图更新完成后赋值。 案例三:如下是列表加载更新后回到当前滚动条位置的案例
class ScrollingList extends React.Component {
listRef = null;
previousScrollOffset = null;
componentWillUpdate(nextProps, nextState) {
if (this.props.list.length < nextProps.list.length) {
this.previousScrollOffset = this.listRef.scrollHeight - this.listRef.scrollTop;
}
}
componentDidUpdate(prevProps, prevState) {
if (this.previousScrollOffset !== null) {
this.listRef.scrollTop = this.listRef.scrollHeight - this.previousScrollOffset;
this.previousScrollOffset = null;
}
}
render() {
return (
`<div>` {/* ...contents... */}`</div>`
);
}
setListRef = ref => { this.listRef = ref; };
每一种节点类型有自己的属性,也就是prop,每次进行diff的时候,react会先比较该节点类型,假如节点类型不一样,那么react会直接删除该节点,然后直接创建新的节点插入到其中,假如节点类型一样,那么会比较prop是否有更新,假如有prop不一样,那么react会判定该节点有更新,那么重渲染该节点,然后在对其子节点进行比较,一层一层往下,直到没有子节点
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。