Представления списков очень распространены в приложениях.В настоящее время более серьезные проблемы с производительностью React Native сосредоточены в таких местах, как большие списки FlatList.Следующие оптимизации слоя js и даже оптимизация нативного слоя делают производительность сопоставимой с нативной. те.
FlatList
React Native версии 0.43 представил FlatList вместо ListView. Реализация FlatList наследует от VirtualizedList. Базовый VirtualizedList обеспечивает более высокую гибкость, но не так удобен в использовании, как FlatList. Если нет особых требований, FlatList нельзя использовать напрямую. Реализация VirtualizedList наследуется от ScrollView, поэтому FlatList наследует все реквизиты VirtualizedList и ScrollView.При просмотре связанных документов, если соответствующий реквизит или метод не может быть найден во FlatList, можно использовать два других компонента. FlatList в React Native похож на listview для android и uitableview для ios, он перерабатывает компоненты внеэкранного представления для достижения высокой производительности.
Применение
В следующем примере кода используется машинописный текст
основное использование
<FlatList<number>
// 数据数组
data={[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}
// key
keyExtractor={(item, index) => index.toString()}
// item渲染
renderItem={({item: num}) => (
<Text>{num}</Text>
)}
/>
Общий реквизит
extraData
Существуют данные, отличные от данных, используемых в списке, который указан в этом свойстве, иначе интерфейс, скорее всего, не обновится.
horizontal
Установите значение true, чтобы перейти в режим горизонтальной компоновки.
inverted
Измените направление прокрутки, в основном используется для обратного отображения данных, таких как списки чатов.
numColumns
Укажите, сколько элементов отображать в столбце
Общий метод
scrollToEnd
Проведите вниз экрана
scrollToIndex
Проведите в указанную позицию
scrollToOffset
проведите пальцем до указанного пикселя
подтягивающая загрузка
<FlatList
// 上拉回调
onEndReached={() => console.log('上拉加载')}
// 滑动到最后视图内容比例,设置为0-1,例如0.5则表示滑到最后一个视图一半开始回调
onEndReachedThreshold={0.1}
/>
Потяните вниз, чтобы обновить
<FlatList
// true显示刷新组件
refreshing={this.state.refreshing}
// 下拉回调
onRefresh=(async () => {
this.setState({
refreshing: true
});
await 耗时操作
this.setState({
refreshing: false
});
});
/>
событие смахивания
onTouchStart
Нажмите пальцем, чтобы начать скольжение, вызывается один раз, чтобы отслеживать начало взаимодействия.
onTouchMove
Проведите пальцем, чтобы позвонить несколько раз
onTouchEnd
Когда палец отпущен, вызовите его один раз, чтобы начать инерционную прокрутку, которая используется для отслеживания окончания взаимодействия.
onMomentumScrollBegin
Начинается инерционная прокрутка, вызываемая один раз для контроля начала скользящей инерционной анимации.
onMomentumScrollEnd
Инерционная прокрутка заканчивается, вызывается один раз для контроля окончания скользящей инерционной анимации.
onScroll
Во время скольжения вызывается несколько раз для контроля положения скольжения
onScrollBeginDrag
Начать скольжение, вызывается один раз для отслеживания начала скольжения
onScrollEndDrag
Конец слайда, вызываемый один раз, используется для отслеживания конца слайда.
нумерация страниц
Используется для разработки простого представления карусели, постраничного просмотра и прокрутки для просмотра контента и т. д.
// 当前视图索引
private index = 0;
// 必须与this绑定,否则抛出异常
private viewabilityConfig = {viewAreaCoveragePercentThreshold: 100};
handleViewableItemsChanged = (info: { viewableItems: Array<ViewToken>; changed: Array<ViewToken>}) => {
// index为当前可见视图在view的索引
this.index = info.changed[0].index!;
}
<FlatList
// 每次滑动后一个item停留在整个视图
pagingEnabled={true}
// 可见视图设置,1-100,50表示一半可见时回调,100表示全部可见时回调
viewabilityConfig={this.viewabilityConfig}
// 可见视图变更回调
onViewableItemsChanged={this.handleViewableItemsChanged}
// onViewableItemsChanged会多次回调,监听惯性滑动结束判断分页滑动结束,如需要实时判断视图索引显示,则直接使用onViewableItemsChanged
onMomentumScrollEnd={() => console.log('滑动至', this.index)}
/>
оптимизация
removeClippedSubviews
Удалите компоненты за пределами экрана, значение по умолчанию — true, что оказывает наибольшее влияние на производительность, не меняйте на false.
windowSize
Сохраняйте количество представлений, даже если они не удаляются с экрана. Значение по умолчанию – 11. В высокопроизводительных компонентах можно соответствующим образом установить небольшое значение. В представлении, которое быстро скользит, установите большое значение, например 300 во избежание быстрого перемещения. После скольжения текущий вид не визуализировался и отображается пустым.
getItemLayout
Получить высоту.Если высота просмотра фиксирована, установка этого свойства может значительно повысить производительность и избежать необходимости пересчитывать высоту просмотра каждый раз в процессе рендеринга.
getItemLayout={(data, index) => ({length: height, offset: height * index, index})}
key
Разумная настройка клавиш может улучшить повторное использование компонентов с помощью реакции, что может значительно оптимизировать производительность.После того, как компоненты перемещены за пределы экрана, их можно повторно использовать после переработки.
Встроенная оптимизация
В чрезвычайно требовательном представлении списка данные могут достигать тысяч или даже десятков тысяч.В некоторых случаях FlatList больше не может удовлетворить, особенно для устройств Android. Ниже описано, как напрямую использовать собственное представление Android RecyclerView для завершения представления списка с высоким спросом.
нативный код представления
public class MyFlatListManager extends SimpleViewManager<MyFlatListManager.MyRecyclerView> {
// 自定义RecyclerView
public static class MyRecyclerView extends RecyclerView {
// 数据列表
public List<Data> list = new ArrayList<>();
// 适配器
public MyAdapter myAdapter;
// 布局管理器
public LinearLayoutManager mLayoutManager;
public MyRecyclerView(Context context) {
super(context);
myAdapter = new MyAdapter(this, list);
// 设置为垂直方向
mLayoutManager = new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false);
setLayoutManager(mLayoutManager);
// 固定高度避免重新测量,提高性能
setHasFixedSize(true);
// 禁止数据变更时动画,避免闪烁
setItemAnimator(null);
setAdapter(myAdapter);
}
@Override
public void requestLayout() {
super.requestLayout();
// react native android根视图requestLayout为空函数,避免加入新视图无法显示或者高度宽度不正确,手动执行测量
post(measureAndLayout);
}
public final Runnable measureAndLayout = new Runnable() {
@Override
public void run() {
measure(
MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
Log.d(TAG, "measureAndLayout");
layout(getLeft(), getTop(), getRight(), getBottom());
}
};
}
private static class MyViewHolder extends RecyclerView.ViewHolder {
public MyViewHolder(View itemView) {
super(itemView);
}
}
private static class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
private List<MyViewHolder> holders;
private List<Data> list;
private MyRecyclerView recyclerView;
public MyAdapter(MyRecyclerView recyclerView, List<VideoInfo> list) {
this.list = list;
this.holders = new ArrayList<>();
this.recyclerView = recyclerView;
}
// 视图创建
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.movie_list_row, parent, false);
// 手动重新设置高度,match parent
itemView.getLayoutParams().height = parent.getHeight();
itemView.getLayoutParams().width = parent.getWidth();
return new MyViewHolder(itemView);
}
@Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
Data data = list.get(position);
// Log.i(TAG, "setTag " + position);
holder.itemView.setTag(position);
// 绑定视图数据
}
@Override
public int getItemCount() {
return list.size();
}
}
private static final String TAG = "MyFlatListViewManager";
@Override
public String getName() {
return "MyFlatListViewManager";
}
@Override
protected MyRecyclerView createViewInstance(final ThemedReactContext reactContext) {
return new MyRecyclerView(reactContext);
}
@Nullable
@Override
public Map<String, Integer> getCommandsMap() {
Map<String, Integer> commandsMap = new HashMap<>();
commandsMap.put("addData", 1);
return commandsMap;
}
@Override
public void receiveCommand(MyRecyclerView root, int commandId, @Nullable ReadableArray args) {
MyAdapter myAdapter = (MyAdapter) root.getAdapter();
switch (commandId) {
case 1:
if (args == null) return;
Log.i(TAG, "addData size: " + args.size());
Integer position = root.list.size();
for (int i = 0; i < args.size(); i++) {
// 初始化值,getData为从map中获取data的函数,自行根据结构实现
Data data = getData(args.getMap(i));
Log.i(TAG, "add data " + data);
root.list.add(data);
}
Log.i(TAG, "addDatas old position " + position + " size " + args.size());
// 通知变更
myAdapter.notifyItemRangeInserted(position, args.size());
break;
}
}
}
Есть несколько мест, на которые стоит обратить внимание
- setHasFixedSize Если высота представления фиксирована, установка фиксированной высоты может улучшить производительность.
- Анимация setItemAnimator может вызывать мерцание при загрузке изображений и т. д.
- requestLayout должен повторно запускать представление измерений вручную.В Android эта часть механизма заблокирована React Native.
- onCreateViewHolder должен вручную установить высоту и ширину itemView
реагировать на анти-паттерн
Реквизиты передаются между нативными компонентами и слоями js.Если объем данных слишком велик, использовать реквизиты для прямой передачи нецелесообразно, и данные могут достигать нескольких метров или даже больше. Режим реквизита в React уже не подходит для такого сценария, в вебе тоже большой объем данных пересылается каждый раз при изменении единичных данных, что вызовет серьезные проблемы с производительностью. В этом случае использование компонента ref для вызова функции для добавления или удаления связанных массивов один за другим, этих больших объектов, значительно улучшит производительность. В коде Android вместо использования prop для передачи данных FlatList используйте метод add для его добавления, а затем выполните еще один уровень инкапсуляции собственного компонента в слое js, чтобы использование соответствовало другим компонентам.