面试题答案
一键面试函数式组件和类组件在作用域和变量生命周期处理上的不同
- 函数式组件:
- 作用域:函数式组件基于函数作用域,每次渲染函数重新执行,作用域内变量重新声明和初始化。例如:
import React from'react';
const FunctionalComponent = () => {
let count = 0;
return <div>{count}</div>;
};
这里的count
每次渲染都会重置为0。
- 变量生命周期:没有自身独立的实例,状态和副作用通过钩子(如
useState
、useEffect
)管理。
- 类组件:
- 作用域:基于类的实例,类的属性在实例创建时初始化,实例存在期间属性值持续有效。例如:
import React, { Component } from'react';
class ClassComponent extends Component {
constructor(props) {
super(props);
this.count = 0;
}
render() {
return <div>{this.count}</div>;
}
}
这里的count
在实例创建后,只要实例不销毁,就会保持其值。
- 变量生命周期:通过
componentDidMount
、componentDidUpdate
、componentWillUnmount
等生命周期方法管理。
useState、useEffect等钩子函数模拟类组件生命周期行为
- useState模拟状态更新:
- 类组件中通过
this.setState
更新状态,函数式组件中使用useState
。例如,在类组件中:
- 类组件中通过
import React, { Component } from'react';
class ClassCounter extends Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
increment = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
在函数式组件中:
import React, { useState } from'react';
const FunctionalCounter = () => {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
};
- useEffect模拟生命周期方法:
- 模拟componentDidMount: 在类组件中:
import React, { Component } from'react';
class ClassDidMount extends Component {
componentDidMount() {
console.log('Component mounted');
}
render() {
return <div>Class Component</div>;
}
}
在函数式组件中:
import React, { useEffect } from'react';
const FunctionalDidMount = () => {
useEffect(() => {
console.log('Component mounted');
return () => {
// 模拟componentWillUnmount
console.log('Component will unmount');
};
}, []);
return <div>Functional Component</div>;
};
- 模拟componentDidUpdate: 在类组件中:
import React, { Component } from'react';
class ClassDidUpdate extends Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
componentDidUpdate(prevProps, prevState) {
if (prevState.count!== this.state.count) {
console.log('Count updated');
}
}
increment = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
在函数式组件中:
import React, { useState, useEffect } from'react';
const FunctionalDidUpdate = () => {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('Count updated');
}, [count]);
const increment = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
};
从作用域和变量生命周期角度避免内存泄漏
- 函数式组件:
- 避免闭包导致的内存泄漏:在
useEffect
中,如果依赖数组没有正确设置,可能导致闭包引用旧数据,造成内存泄漏。例如:
- 避免闭包导致的内存泄漏:在
import React, { useState, useEffect } from'react';
const MemoryLeakComponent = () => {
const [count, setCount] = useState(0);
useEffect(() => {
const intervalId = setInterval(() => {
// 这里如果count没有在依赖数组中,可能引用旧的count值
console.log(count);
}, 1000);
return () => {
clearInterval(intervalId);
};
}, [count]);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
- 正确处理副作用清理:在
useEffect
返回的清理函数中,清理定时器、事件监听器等资源。如上述代码中clearInterval
。
- 类组件:
- 在componentWillUnmount清理资源:在类组件中,
componentWillUnmount
方法用于清理定时器、事件监听器等。例如:
- 在componentWillUnmount清理资源:在类组件中,
import React, { Component } from'react';
class ClassMemoryLeakComponent extends Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
componentDidMount() {
this.intervalId = setInterval(() => {
this.setState({ count: this.state.count + 1 });
}, 1000);
}
componentWillUnmount() {
clearInterval(this.intervalId);
}
render() {
return <div>Count: {this.state.count}</div>;
}
}
在实际项目中,比如一个实时聊天组件,在组件销毁时如果没有清理定时器(如心跳检测定时器)或移除事件监听器(如窗口大小变化监听事件),会导致内存泄漏,造成性能问题。通过正确管理作用域和变量生命周期,可以有效避免这些问题。