虚拟列表视图

此组件支持在可滚动区域内垂直排列的项目列表。列表的可见部分被称为“视口”。该列表是虚拟化的,这意味着项目只有在视口内(或视口上方或下方)时才会被渲染。

与其他列表视图(例如 React Native 中提供的 ListView)不同,此组件支持包含不同高度和完全不同类型的异构项目列表。

列表中的每个项目都由一个 VirtualListViewItemInfo 对象描述。项目列表由通过 itemList 属性传入的有序列表指定。当一个项目进入视图时,它通过 renderItem 回调进行渲染。调用者可以根据需要向 VirtualListViewItemInfo 添加额外的字段。例如,有时包含额外的识别信息(例如项目类型或项目特定的渲染方法)会很有用。

必须为每个项目指定高度。默认情况下,此高度被认为是准确且恒定的。如果您不知道对象的高度或其可能改变,可以在运行时测量高度。此选项开销较大,因此应尽可能避免。

它可选地支持项目在列表中添加、删除或移动时的动画。

当项目在可见区域之前或之后添加时,它会尝试保持可见项目的当前位置,根据需要调整滚动位置和列表高度。

安装方式:npm install reactxp-virtuallistview

性能优化技巧

VirtualListView 采用多种技巧来提高性能。

它使用一种称为“单元格回收”的技术来最大程度地减少挂载和卸载的次数。单元格是列表项的容器。当一个单元格不再可见时,它可以暂时隐藏起来,然后用于下一个变为可见的项目。当回收的单元格及其内容用于内容相似的项目时,这种优化最有效。因此,调用者需要指定一个“模板”字段来识别相似项目。在某些情况下,禁用单元格回收可能是有益的,因为回收的单元格即使在不可见时也会继续其正常的 React 生命周期,这在某些情况下可能导致过度的后台重新渲染。将 VLV 与订阅由组件管理的 React 库(如 ReSub)结合使用时,可能会出现这种行为。要禁用特定单元格的单元格回收,请从项目描述符中排除模板字段。

它还支持“过度绘制”,以渲染视口上方和下方的项目。这最大程度地减少了用户在新的项目可以在该区域渲染之前滚动到列表新部分的可能性。过度绘制仅在视口最初填充后才使用。这减少了渲染的性能影响。

它还限制每次渲染的项目数量,以避免在 JavaScript 线程上消耗过多周期。

它支持一种特殊模式,其中只有当相应的 VirtualListViewItemInfo 发生变化时才重新渲染项目。此模式要求 renderItem 方法仅使用此对象中的信息。要使用此模式,请将 skipRenderIfItemUnchanged 属性设置为 true。

示例

import { VirtualListView, VirtualListViewItemInfo }
    from 'reactxp-virtuallistview';

// Extend VirtualListViewItemInfo to include display text
interface FruitListItemInfo extends VirtualListViewItemInfo {
    text: string;
}

interface FruitListState {
    items: FruitListItemInfo[];
}

const _headerItemHeight = 20;
const _fruitItemHeight = 32;
const _headerItemTemplate = 'header';
const _fruitItemTemplate = 'fruit';

class FruitListView extends RX.Component<null, FruitListState> {
    constructor() {
        super();

        this.state = {
            items: [{
                key: 'header1',
                height: _headerItemHeight,
                text: 'Domstic Fruits',
                template: _headerItemTemplate
            }, {
                key: 'bannana',
                height: _fruitItemHeight,
                text: 'Banana',
                template: _fruitItemTemplate
            }, {
                key: 'apple',
                height: _fruitItemHeight,
                text: 'Apple',
                template: _fruitItemTemplate
            }]
        };
    }

    render() {
        return (
            <VirtualListView
                itemList={ this.state.items }
                renderItem={ this._renderItem }
                animateChanges={ true }
                skipRenderIfItemUnchanged={ true }
            />
        );
    }

    private _renderItem(details: VirtualListViewCellRenderDetails<FruitListItemInfo>) {
        const viewStyle = RX.Styles.createViewStyle({
            height: details.item.height,
            backgroundColor: item.template === _headerItemTemplate ?
                '#ddd' : '#fff',
            alignItems: 'center'
        }, false);

        return (
            <RX.View style={ viewStyle }>
                <RX.Text>
                    { details.item.text }
                </RX.Text>
            </RX.View>
        );
    }
}

接口

interface VirtualListViewItemInfo {
    // A string that uniquely identifies this item.
    key: string;

    // Specifies the known height of the item or a best guess if the
    // height isn't known.
    height: number;

    // Specifies that the height is not known and needs to be measured
    // dynamically. This has a big perf overhead because it requires a
    // double layout (once offscreen to measure the item). It also
    // disables cell recycling. Wherever possible, it should be avoided,
    // especially for perf-critical views.
    measureHeight?: boolean;

    // Specify the same "template" string for items that are rendered
    // with identical or similar view hierarchies. When a template is
    // specified, the list view attempts to recycle cells whose templates
    // match. When an item scrolls off the screen and others appear on
    // screen, the contents of the cell are simply updated rather than
    // torn down and rebuilt.
    template: string;

    // Is the item navigable by keyboard or through accessibility
    // mechanisms?
    isNavigable?: boolean;
}

属性

// Should the list animate additions, removals and moves within the list?
animateChanges?: boolean;

initialSelectedKey?: string;

// Ordered list of descriptors for items to display in the list.
itemList: VirtualListViewItemInfo[];

// Use this if you want to vertically offset the focused item from the 
// top of the viewport when using keyboard nav
keyboardFocusScrollOffset?: number;

// Logging callback to debug issues related to the VirtualListView.
logInfo?: (textToLog: string) => void;

// Callback when an item in the VLV is selected
onItemSelected?: (item: ItemInfo) => void;

// Optional padding around the scrolling content within the list.
padding?: number;

// Callback for rendering item when it becomes visible within view port.
renderItem: (renderDetails: VirtualListCellRenderDetails<ItemInfo>) => 
    JSX.Element | JSX.Element[];

// If true, allows each item to overflow its visible cell boundaries;
// by default, item contents are clipped to cell boundaries.
showOverflow?: boolean;

// By default, VirtualListView re-renders every item during the render.
// Setting this flag to true allows the list view to re-render only
// items from itemList whose descriptor has changed, thus avoiding
// unnecessary rendering. It uses _.isEqual to perform this check. In
// this mode, renderItem should not depend on any external state, only
// on VirtualListViewItemInfo, to render item.
skipRenderIfItemUnchanged?: boolean;

// ID that can be used to identify the instantiated element for testing purposes.
testId: string = undefined;

// Pass-through properties for scroll view.
keyboardDismissMode?: 'none' | 'interactive' | 'on-drag';
keyboardShouldPersistTaps?: boolean;
disableScrolling?: boolean;
scrollsToTop?: boolean; // iOS only, scroll to top when user taps on status bar
disableBouncing?: boolean; // iOS only, bounce override
scrollIndicatorInsets?: { top: number, left: number,
    bottom: number, right: number }; // iOS only
onLayout?: (e: RX.Types.ViewOnLayoutEvent) => void;
scrollEventThrottle?: number;
onScroll?: (scrollTop: number, scrollLeft: number) => void;
scrollXAnimatedValue?: RX.Types.AnimatedValue;
scrollYAnimatedValue?: RX.Types.AnimatedValue;

方法

// Scrolls the view to the specified top value (specified in pixels).
scrollToTop(animated = true, top = 0);

// Sets selection & focus to specified key
selectItemKey(key: string);