我正在使用react-virtualized库来创建有效的新闻源。图书馆太棒了。我将WindowScroller,AutoSizer和VirtualScroll组件结合起来,以获得无限的滚动行为。问题是当我手动设置VirtualScroll高度而不使用WindowScroller时,所有浏览器的性能都很好。但是,当我添加WindowScroller组件时,性能会显着降低,尤其是在Firefox(v47.0)中。如何优化它以便使用窗口滚动是可行的?
这是新闻组件,其中使用了react-virtualized, 我有两种类型的列表项 - 标题项和简单项,标题项包含一组新闻的日期,因此它有点长。
import React, { PropTypes, Component } from 'react';
import Divider from 'material-ui/Divider';
import Subheader from 'material-ui/Subheader';
import { Grid, Row, Col } from 'react-flexbox-grid';
import NewsItem from '../NewsItem';
import styles from './styles.css';
import CircularProgress from 'material-ui/CircularProgress';
import Paper from 'material-ui/Paper';
import classNames from 'classnames';
import { InfiniteLoader, WindowScroller, AutoSizer, VirtualScroll } from 'react-virtualized';
import shallowCompare from 'react-addons-shallow-compare';
class News extends Component {
componentDidMount() {
this.props.onFetchPage(0);
}
shouldComponentUpdate(nextProps, nextState) {
return shallowCompare(this, nextProps, nextState);
}
getRowHeight({ index }) {
const elementHeight = 200;
const headerHeight = 78;
if (!this.isRowLoaded(index)) {
return elementHeight;
}
return this.props.articles[index].isHeader ?
headerHeight + elementHeight : elementHeight;
}
displayElement(article, isScrolling) {
return (
<Paper
key={article.id}
className={classNames(styles.newsItemContainer, {
[styles.scrolling]: isScrolling
})}
>
<NewsItem {...article} />
<Divider />
</Paper>
);
}
isRowLoaded(index) {
return !this.props.hasNextPage || index < this.props.articles.length;
}
renderRow(index, isScrolling) {
if (!this.isRowLoaded(index)) {
return (
<div className={styles.spinnerContainer}>
{this.props.isFetching ? <CircularProgress /> : null}
</div>
);
}
const { isHeader, date, article } = this.props.articles[index];
if (isHeader) {
return (
<div>
<Subheader
key={date}
className={styles.groupHeader}
>
{date}
</Subheader>
{this.displayElement(article, isScrolling)}
</div>
);
}
return this.displayElement(article, isScrolling);
}
noRowsRenderer() {
return (<p>No articles found</p>);
}
render() {
const {
articles,
onFetchPage,
pageNumber,
isFetching,
hasNextPage
} = this.props;
const loadMoreRows = isFetching ?
() => {} :
() => onFetchPage(pageNumber + 1);
const rowCount = hasNextPage ? articles.length + 1 : articles.length;
return (
<Grid>
<Row>
<Col xs={12} sm={8} smOffset={2}>
<InfiniteLoader
isRowLoaded={({ index }) => this.isRowLoaded(index)}
loadMoreRows={loadMoreRows}
rowCount={rowCount}
>
{({ onRowsRendered, registerChild, isScrolling }) => (
<WindowScroller>
{({ height, scrollTop }) => (
<AutoSizer disableHeight>
{({ width }) => (
<VirtualScroll
autoHeight
ref={registerChild}
height={height}
rowCount={rowCount}
rowHeight={(...args) => this.getRowHeight(...args)}
rowRenderer={({ index }) => this.renderRow(index, isScrolling)}
width={width}
noRowsRenderer={this.noRowsRenderer}
onRowsRendered={onRowsRendered}
overscanRowCount={10}
scrollTop={scrollTop}
/>
)}
</AutoSizer>
)}
</WindowScroller>
)}
</InfiniteLoader>
</Col>
</Row>
</Grid>
);
}
}
News.propTypes = {
articles: PropTypes.array.isRequired,
onFetchPage: PropTypes.func.isRequired,
isFetching: PropTypes.bool.isRequired,
pageNumber: PropTypes.number.isRequired,
hasNextPage: PropTypes.bool.isRequired
};
export default News;
列表项是以下组件:
import React, { PropTypes } from 'react';
import styles from './styles.css';
import { Row, Col } from 'react-flexbox-grid';
import shallowCompare from 'react-addons-shallow-compare';
import pick from 'lodash/pick';
import NewsItemContent from '../NewsItemContent';
class NewsItem extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
return shallowCompare(this, nextProps, nextState);
}
render() {
const contentProps = pick(this.props, [
'title', 'description', 'seedUrl', 'seedCode', 'date'
]);
return (
<div
onClick={() => window.open(this.props.url, '_blank')}
className={styles.newsItem}
>
{this.props.imageUrl ?
<Row>
<Col xs={3}>
<div
role="presentation"
style={{ backgroundImage: `url(${this.props.imageUrl})` }}
className={styles.previewImage}
/>
</Col>
<Col xs={9}>
<NewsItemContent {...contentProps} />
</Col>
</Row> :
<Row>
<Col xs={12}>
<NewsItemContent {...contentProps} />
</Col>
</Row>
}
</div>
);
}
}
NewsItem.propTypes = {
imageUrl: PropTypes.string,
description: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
url: PropTypes.string.isRequired,
date: PropTypes.object.isRequired,
seedUrl: PropTypes.string.isRequired,
seedCode: PropTypes.string.isRequired
};
export default NewsItem;
NewsItemContent这里是一个没有任何逻辑的简单纯组件,所以我不会把它放在这里。
谢谢!
更新: 在窗口滚动和块滚动的情况下,我在firefox中记录了性能时间线:
答案 0 :(得分:0)
我认为这与React setState()
电话有关。
WindowScroller
侦听window
对象的滚动事件。这些发生在React的知识之外,因此setState()
调用由同步的ReactDefaultBatchingStrategy
处理。 Grid
是一个React组件,因此它的onScroll事件发生在React的意识中。结果发生的setState()
次调用可以ReactUpdateQueue
更智能地批量处理。
查看您分享的时间表,setState
Grid
次滚动事件的调用会被ReactUpdateQueue
排队。但是查看帧率最差的WindowScroller
时间线,其setState()
次呼叫会立即传递到ReactDefaultBatchingStrategy
。