我有一个导致内存泄漏的组件。基本上是产品缩略图的网格。如果单击一个,则打开产品详细信息视图。 打开详细信息视图,让我们让chrome中的gpu进程消耗更多的内存。卸下组件,几乎没有任何余地。该应用程序可以非常快速地分配多个演出。 在Firefox中影响不那么大。开启费用约为4mb,卸载费用为2mb。 我不知道发生了什么事。我将非常感谢您提供一些提示和提示。
编辑:
嗨, 最初的答复。我制作了一个兼容codesandbox的版本。内存泄漏也适用于codeandbox标签。
codesandbox链接: https://codesandbox.io/s/confident-panini-6r9tp
这是产品的缩略图网格:
class Topic extends React.Component {
constructor(props) {
super(props);
this.state = {
currentProduct: null,
productVisible: false,
uiLocked: false
};
this.onSelectProduct = this.onSelectProduct.bind(this);
this.onCloseProduct = this.onCloseProduct.bind(this);
this.resetTimer = this.resetTimer.bind(this);
}
componentDidMount() {
this.initializeTimer();
}
componentWillUnmount() {
this.clearTimersAndListeners();
}
initializeTimer() {
window.addEventListener("click", this.resetTimer);
window.addEventListener("scroll", this.resetTimer, true);
this.resetTimer();
}
clearTimersAndListeners() {
window.removeEventListener("click", this.resetTimer);
window.removeEventListener("scroll", this.resetTimer);
if (this.timer) {
window.clearTimeout(this.timer);
}
}
resetTimer() {
const { resetDelay } = this.context;
if (this.timer) {
window.clearTimeout(this.timer);
}
this.timer = window.setTimeout(this.onCloseProduct, resetDelay * 1000);
}
onSelectProduct(selectedProduct) {
this.setState({
currentProduct: selectedProduct.slug,
productVisible: true
});
}
onCloseProduct() {
this.setState({ productVisible: false, uiLocked: true });
setTimeout(
() =>
this.setState({
currentProduct: null,
uiLocked: false
}),
300
);
}
render() {
const context = this.context;
const { topicSlug } = this.props.match.params;
const { currentProduct, productVisible, uiLocked } = this.state;
const topic = context.topics.find(topic => topic.slug === topicSlug);
const { products, title, longtitle, subtitle, icon } = topic;
const productObject = products.find(
product => product.slug === currentProduct
);
const lang = context.language;
return (
<AppContext.Consumer>
{context => {
const { enableTopicNavigation } = context;
return (
<>
<GlobalStyle locked={uiLocked} />
<Frame
title={title[lang]}
backButtonVisible={productVisible}
onNavigateBack={this.onCloseProduct}
topicColor={topic.color}
backToTopicNavigationLinkVisible={enableTopicNavigation && !currentProduct}
isProduct={currentProduct}
>
<Wrapper topic={topic}>
<TopicHeader
icon={icon}
title={longtitle[lang]}
subtitle={subtitle ? subtitle[lang] : null}
/>
<TileContainerScrollWrapper>
<TileContainer>
{products.map(product => (
<ProductTile
onClick={e => this.onSelectProduct(product)}
key={product.slug}
title={product.name[lang]}
image={product.thumbnail}
enlargeImage={product.enlargeThumbnail}
color={topic.color}
/>
))}
</TileContainer>
</TileContainerScrollWrapper>
{currentProduct && (
<Product
key={productObject.slug}
onClose={this.onCloseProduct}
product={productObject}
visible={productVisible}
topic={topic}
/>
)}
</Wrapper>
</Frame>
</>
);
}}
</AppContext.Consumer>
);
}
}
Topic.contextType = AppContext;
export default Topic;
这是产品
class Product extends React.Component {
constructor(props) {
super(props);
this.state = {
mounted: false,
mediaFullscreen: false,
currentSlideIndex: 0
};
this.player = [];
this.latestTap = new Date(0);
this.toggleMediaFullScreen = this.toggleMediaFullScreen.bind(this);
this.scrollToMedia = this.scrollToMedia.bind(this);
this.checkForDoubleTap = this.checkForDoubleTap.bind(this);
}
componentDidMount() {
console.log("mount")
const { product } = this.props;
setTimeout(
() =>
this.setState({
mounted: true
}),
0
);
if (product.media) {
this.embla.on("select", () => {
const currentSlideIndex = this.embla.selectedScrollSnap();
this.setState({ currentSlideIndex });
this.player.forEach(currentPlayer => currentPlayer.handleStop());
if(this.player[currentSlideIndex]) {
this.player[currentSlideIndex].handlePlay();
}
});
}
}
componentWillUnmount() {
}
scrollToMedia(index) {
this.embla.scrollTo(index);
}
toggleMediaFullScreen() {
this.setState({ mediaFullscreen: !this.state.mediaFullscreen });
}
checkForDoubleTap() {
var now = new Date().getTime();
var timeSince = now - this.latestTap;
if (timeSince < 500 && timeSince > 0) {
this.toggleMediaFullScreen();
}
this.latestTap = new Date().getTime();
}
render() {
const { mounted, mediaFullscreen, currentSlideIndex } = this.state;
const { product, visible, topic } = this.props;
const componentIsVisible = mounted && visible;
return (
<AppContext.Consumer>
{context => {
const lang = context.language;
const { i18n } = context;
return (
<>
<GlobalStyle overrideBackground={componentIsVisible} />
<Wrapper visible={componentIsVisible} color={topic.color}>
{product.media && (
<>
<EmblaWrapper
fullscreen={mediaFullscreen}
>
<EmblaCarouselReact
emblaRef={c => (this.embla = c)}
options={{
loop: false,
containerSelector: ".slide-wrapper",
speed: 5
}}
style={{ }}
>
<SlideWrapper className="slide-wrapper">
{product.media.map((media, index) => {
const isCurrentSlide = index === currentSlideIndex;
return (
<MediaDetail
key={index}
fullscreen={mediaFullscreen}
onClick={this.checkForDoubleTap}
>
<CurrentMedia>
{media.type === "image" && (
<CurrentImage
src={process.env.PUBLIC_URL + media.source}
/>
)}
{media.type === "video" && media.turntable && (
<VideoPlayer
source={process.env.PUBLIC_URL + media.source}
thumbnail={media.thumbnail}
interactable={false}
fullscreen={mediaFullscreen}
color={topic.color}
/>
)}
{media.type === "video" && !media.turntable && (
<PlyrPlayer
ref={x => {this.player[index] = x }}
key={process.env.PUBLIC_URL + media.source}
source={process.env.PUBLIC_URL + media.source}
thumbnail={media.thumbnail}
interactable={!media.turntable}
inFullscreen={mediaFullscreen}
color={topic.color}
autoPlay={isCurrentSlide}
/>
)}
</CurrentMedia>
</MediaDetail>
);
})}
</SlideWrapper>
</EmblaCarouselReact>
</EmblaWrapper>
<FullscreenButton
visible={!mediaFullscreen}
color={topic.color}
onClick={this.toggleMediaFullScreen}
>
<FullScreenIcon
src={
process.env.PUBLIC_URL +
"/images/icons/ico-full-screen.svg"
}
/>
<FullScreenText>{i18n.fullscreen[lang]}</FullScreenText>
</FullscreenButton>
<FullscreenButton
visible={mediaFullscreen}
color={topic.color}
onClick={this.toggleMediaFullScreen}
>
<FullScreenIcon
src={
process.env.PUBLIC_URL + "/images/icons/ico-close.svg"
}
/>
<FullScreenText>
{i18n.fullscreenClose[lang]}
</FullScreenText>
</FullscreenButton>
</>
)}
<MediaOverview visible={!mediaFullscreen}>
<ThumbnailGrid>
{"media" in product &&
product.media.length > 0 &&
product.media.map((media, index) => (
<MediaThumbnail
key={index}
media={media}
onClick={e => this.scrollToMedia(index, media)}
/>
))}
</ThumbnailGrid>
</MediaOverview>
<Overlay color={topic.color} visible={!mediaFullscreen}>
<ScrollWrapper>
<Scrollable>
<HeadingContainer>
<Heading>{product.name[lang]}</Heading>
<FormattedText
content={product.subtitle && product.subtitle[lang]}
wrapperElement={Subtitle}
/>
</HeadingContainer>
{"content" in product &&
product.content.length > 0 &&
product.content.map((contentItem, index) => (
<OverlayContent key={index} content={contentItem} />
))}
</Scrollable>
</ScrollWrapper>
</Overlay>
</Wrapper>
</>
);
}}
</AppContext.Consumer>
);
}
}
export default Product;
我剪切了样式化的组件并导入