الحرب على الفرامل. تحسين عدد تجسيدات المكونات في React Native

مرحبا يا هابر! اسمي Kamo Spertsyan ، أنا منخرط في React Native development في Profi.ru. إذا قررت استخدام تقنية React Native لتقديم ميزات المنتج بسرعة والتركيز على سرعة التطوير ، فمن المحتمل أن تواجه مشكلات في الأداء. هذا ما حدث لنا على الأقل. بعد ستة أشهر من التطوير النشط ، انخفض أداء تطبيقنا إلى ما دون المستوى الحرج - كان كل شيء بطيئًا للغاية. لذلك ، تناولنا التحسين - أزلنا جميع "المكابح" أثناء بدء التشغيل ، والانتقالات بين الشاشات ، وشاشات العرض ، وردود الفعل على إجراءات المستخدم. ونتيجة لذلك ، في غضون ثلاثة أشهر ، نقلوا تجربة المستخدم إلى المستوى الأصلي. في هذه المقالة ، أريد أن أتحدث عن كيفية تحسين التطبيق على React Native وحل مشكلة مشكلة المكونات المتعددة.



أقوم بتجميع التوصيات التي ستساعد على تقليل عدد عمليات إعادة رسم المكونات التي لا طائل من ورائها. للتوضيح ، في الأمثلة أقارن بين التنفيذين "السيئ" و "الجيد". ستكون المقالة مفيدة لأولئك الذين يواجهون بالفعل ضعف أداء التطبيق ، وأولئك الذين لا يريدون السماح بذلك في المستقبل.

نستخدم React Native مقترنًا بـ Redux. بعض النصائح تتعلق بهذه المكتبة. أيضًا في المثال ، أستخدم مكتبة Redux-thunk - لمحاكاة العمل مع الشبكة.

متى تفكر في الأداء؟


في الواقع ، يجدر التذكر من بداية العمل على التطبيق. ولكن إذا كان تطبيقك يتباطأ بالفعل - فلا تيأس ، يمكن إصلاح كل شيء.

يعلم الجميع ، ولكن فقط في حالة ، سأذكر: من الأفضل التحقق من الأداء على الأجهزة الضعيفة. إذا كنت تقوم بالتطوير على أجهزة قوية ، فقد لا تكون على دراية بـ "فرامل" المستخدمين النهائيين. حدد لنفسك الأجهزة التي ستسترشد بها. قم بقياس الوقت أو FPS في مخططات التحكم للمقارنة مع النتائج بعد التحسين.

يوفر React Native من خارج الصندوق القدرة على قياس تطبيقات FPS من خلال أدوات المطور → إظهار شاشة مراقبة الأداء. القيمة المرجعية هي 60 لقطة في الثانية. كلما انخفض هذا المؤشر ، كلما كان التطبيق "أبطأ" أقوى - لا يستجيب أو يتفاعل مع التأخير في إجراءات المستخدم. أحد التأثيرات الرئيسية على FPS هو عدد العروض ، التي تعتمد "شدتها" على مدى تعقيد المكونات.

وصف المثال


أعرض جميع التوصيات على سبيل المثال تطبيق بسيط مع قائمة الأخبار. التطبيق يحتوي على شاشة واحدة ، والتي تقع FlatListمع الأخبار. عنصر الأخبار هو مكون NewsItemيتكون من مكونين أصغر - العنوان ( NewsItemTitle) والنص ( NewsItemBody). يمكن رؤية المثال بأكمله هنا . مزيد من النص في الروابط إلى فروع مختلفة من المستودع للحصول على أمثلة محددة. يتم استخدام المستودع لراحة القراء الذين يرغبون في استكشاف الأمثلة بشكل أعمق. لا تدعي الشفرة الموجودة في المستودع والأمثلة أدناه أنها مثالية - فهي مطلوبة فقط لأغراض العرض التوضيحي.

أدناه ، يتم عرض جميع المكونات بشكل تخطيطي مع الروابط والدعائم.


في طريقة التجسيد لكل مكون ، أضفت الإخراج إلى وحدة التحكم بمعلومات فريدة عنه:

SCREEN
ITEM_{no}
ITEM_TITLE_{no}
ITEM_BODY_{no}

أين {no}هو الرقم التسلسلي للأخبار من أجل التمييز بين العروض الإخبارية المختلفة عن العروض المتعددة نفسها.

للاختبار في كل refreshقائمة أخبار ، تتم إضافة أخبار إضافية إلى بدايتها. في نفس الوقت ، يتم عرض الرسالة التالية في وحدة التحكم:

--------------[ REFRESHING ]--------------

ستساعد هذه السجلات على فهم ما إذا كانت هناك مشكلة في أي مكون معين ، وبعد ذلك تحديد ما إذا كان من الممكن تحسينها.

إذا تم التنفيذ بشكل صحيح ، فيجب أن يبدو سجلنا بعد الإطلاق والعديد من التحديثات كما يلي:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3
--------------[ REFRESHING ]--------------
SCREEN
ITEM_4
ITEM_TITLE_4
ITEM_BODY_4

في البداية ، يتم رسم الشاشة نفسها والأخبار الأولية. عند تحديث اللوحة ، يتم عرض الشاشة مرة أخرى ، لأن بياناتها قد تغيرت بالفعل. يأتي المزيد من الأخبار. لم يتم إعادة رسم جميع الأخبار السابقة ، حيث لم يكن هناك تغيير في بياناتهم.

متى يتم تقديم مكون؟


في React و React Native ، هناك شرطان لعرض المكون:

  1. تغيير دعائمه / حالته ،
  2. تقديم المكون الرئيسي.

يمكن إعادة تعريف الوظيفة في أحد المكونات shouldComponentUpdate- فهي تتلقى دعائم وحالة جديدة كمدخل وتخبر ما إذا كان يجب تقديم المكون. في كثير من الأحيان ، لتجنب إعادة العرض غير الضرورية ، تكفي المقارنة الضحلة بين الدعائم وأشياء الدولة. على سبيل المثال ، يؤدي ذلك إلى التخلص من عمليات العرض غير الضرورية عندما يتغير المكون الأصلي ، إذا لم يؤثر على المكون الفرعي. لكي لا تكتب مقارنة سطحية يدويًا في كل مرة ، يمكنك أن ترث مكونًا من React.PureComponentهذا يغلف هذا الفحص.

عندما نستخدم وظيفة ربط الاتصال ، تقوم مكتبة Redux بإنشاء مكون جديد "متصل" بالحالة العالمية. التغييرات في هذه الحالة تؤدي إلى طريقةmapStateToPropsالذي يعيد الدعائم الجديدة. بعد ذلك ، تبدأ مقارنة الدعائم القديمة والجديدة ، بغض النظر عما إذا تم الإعلان عن المكون على أنه PureComponentأم لا.

تأمل هذه الفروق الدقيقة في مثالنا.

وسوف NewsItemتسمح لل مكون المار connect، NewsItemTitleوراثة من React.Componentو NewsItemBody- من React.PureComponent.

كود المثال الكامل

export class NewsItemTitle extends React.Component
export class NewsItemBody extends React.PureComponent

إليك الشكل الذي سيبدو عليه السجل بعد تحديث لوحة واحدة:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3
ITEM_2
ITEM_TITLE_2
ITEM_1
ITEM_TITLE_1

يمكنك أن ترى أنه تم إعادة رسم الأخبار ومكونات العنوان. سننظر فيها بدورها.

NewsItemأعلن باستخدام connect. كدعائم ، يتلقى هذا المكون معرفًا ، يتلقى بعد ذلك الأخبار في mapStateToProps:

const mapStateToProps = (state, ownProps) => ({
  item: state.newsMap[ownProps.itemKey],
});

منذ تحديث اللوحة يتم تنزيل جميع الأخبار مرة أخرى ، سيتم itemتحديث الكائن وبعد ذلك سيشير إلى خلايا الذاكرة المختلفة. بمعنى آخر ، ستكون كائنات مختلفة ، حتى لو كانت جميع الحقول المتضمنة هي نفسها. لذلك ، تعود مقارنة مكون State'ov السابق والجديد false. سيتم إعادة عرض المكون ، على الرغم من حقيقة أن البيانات لم تتغير.

NewsItemTitleموروث من React.Component، لذلك يتم إعادة تقديمه في كل مرة يتم فيها عرض المكون الرئيسي. يحدث هذا بغض النظر عن قيم الدعائم القديمة والجديدة.

NewsItemBodyالموروثة من React.PureComponent، لذلك يقارن الدعائم القديمة والجديدة. في الأخبار 1 و 2 ، تكون قيمها متكافئة ، لذلك يتم تقديم المكون للأخبار 3.

لتحسين عمليات الأداءNewsItemTitleفقط أعلن أنه React.PureComponent. في حالة ، يجب عليك NewsItemإعادة تعريف الوظيفة shouldComponentUpdate:

shouldComponentUpdate(nextProps) {
  return !shallowEqual(this.props.item, nextProps.item);
}

رمز المثال الكامل

هنا shallowEqualميزة للمقارنة السطحية للكائنات التي يوفرها Redux. يمكنك الكتابة هكذا:

shouldComponentUpdate(nextProps) {
  return (
    this.props.item.title !== nextProps.item.title ||
    this.props.item.body !== nextProps.item.body
  );
}

إليك ما سيبدو عليه سجلنا بعد ذلك:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3

ملحوظة
shouldComponentUpdate NewsItem , NewsItemTitle . . NewsItemTitle - NewsItem, .

مذكرة تفاعلية ومكونات وظيفية


shouldComponentUpdateلا يمكن تجاوز المكون الوظيفي. ولكن هذا لا يعني أنه من أجل تحسين مكون وظيفي ، يجب عليك إعادة كتابته إلى فئة واحدة. في مثل هذه الحالات ، يتم توفير وظيفة مذكرة React.memo . يقبل إدخال مكون ووظيفة مقارنة اختيارية areEqual. عندما يتم استدعاؤه ، فإنه areEqualيحصل على الدعائم القديمة والجديدة ويجب أن يعيد نتيجة المقارنة. الفرق مع shouldComponentUpdateما areEqualيجب أن يعود trueإذا كانت الدعائم متساوية وليس العكس.

على سبيل المثال ، NewsItemTitleقد تبدو الحفظ كما يلي:

areEqual(prevProps, nextProps) {
  return shallowEqual(prevProps, nextProps);
}
export OptimizedNewsItemTitle = React.memo(NewsItemTitle, areEqual)

إذا كنت لم تمر areEqualفي React.memo، ثم مقارنة سطحية من الدعائم وستبذل، لذلك يمكن تبسيط مثالنا:

export OptimizedNewsItemTitle = React.memo(NewsItemTitle)

وظائف لامدا في الدعائم


لمعالجة أحداث المكوِّن ، يمكن تمرير الوظائف إلى أدواتها. إن المثال الأبرز هو التنفيذ onPress. غالبًا ما يتم استخدام وظائف لامدا المجهولة لهذا الغرض. دعنا نقول في NewsItemBodyأننا نريد إظهار المعاينة فقط ، وإذا نقرت عليها - النص بالكامل. للقيام بذلك ، عند التقديم NewsItem، NewsItemBodyسنقوم بتمرير الدعامة التالية:

<NewsItemBody
  ...
  onPress={() => this.props.expandBody()}
  ...
/>

وهنا ما يبدو سجل مثل هذا التنفيذ عندما طريقة shouldComponentUpdateو NewsItemحذف:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1

يتم تقديم الهيئات أخبار 1 و 2، على الرغم من أن بياناتهم لم تتغير، ولكن NewsItemBodyهو PureComponent. ويرجع ذلك إلى حقيقة أن NewsItemقيمة الدعائم onPressيتم إنشاؤها من جديد لكل عرض. من الناحية الفنية ، onPressمع كل تقديم ، يشير إلى منطقة جديدة في الذاكرة ، لذا فإن مقارنة سطحية للدعائم في NewsItemBodyإرجاع خاطئة. تم إصلاح المشكلة عن طريق الإدخال التالي:

<NewsItemBody
  ...
  onPress={this.props.expandBody}
  ...
/>

سجل:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3
ITEM_2
ITEM_TITLE_2
ITEM_1
ITEM_TITLE_1

رمز المثال الكامل

لسوء الحظ ، لا يمكن دائمًا إعادة كتابة وظيفة مجهولة كطريقة أو حقل فئة لمثل هذا السجل. الحالة الأكثر شيوعًا هي أنه عندما يتم استخدام متغيرات نطاق الوظيفة التي تم التصريح عنها داخل دالة لامدا.

النظر في هذه الحالة في مثالنا. للتبديل من القائمة العامة إلى شاشة الأخبار ، نضيف معالجة النقر على نص الأخبار. ستبدو طريقة renderItemالمكون على النحو التالي FlatList:

const renderItem = ({item}) => (
  <NewsItem
    itemKey={item}
    onBodyPress={() => this.onItemBodyPress(item)}
  />
);

onBodyPressلا يمكن التصريح عن دالة مجهولة في الفصل ، لأن المتغير itemالمطلوب للانتقال إلى أخبار معينة سيختفي من النطاق .

إن أبسط حل للمشكلة هو تغيير توقيع الدعائم onBodyPressالمكونة NewsItemبحيث يتم تمرير المعلمة المطلوبة إلى الوظيفة عند استدعاؤها. في هذه الحالة ، هذا هو معرف الأخبار.

const renderItem = ({item}) => (
  <NewsItem
    itemKey={item}
    onBodyPress={item => this.onItemBodyPress(item)}
  />
);

في هذه الحالة ، يمكننا بالفعل إخراج الدالة المجهولة في طريقة فئة المكونات.

const renderItem = ({item}) => (
  <NewsItem
    itemKey={item}
    onBodyPress={this.onItemBodyPress}
  />
);

ومع ذلك ، فإن مثل هذا الحل يتطلب منا تغيير المكون NewsItem.

class NewsItemComponent extends React.Component {
render() {
  ...
  return (
      ...
      <NewsItemBody
        ...
        onPress={() => this.props.onBodyPress(this.props.item)}
        ...
      />
      ...
  );
}

ومرة أخرى نعود إلى المشكلة المشار إليها - ننقل وظيفة لامدا جديدة إلى المكون الفرعي لكل تقديم للوالد. الآن فقط هبطنا إلى مستوى. سجل:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1

للتخلص من هذه المشكلة في الجذر ، يمكنك استخدام ربط useCallback . يسمح بتدوين استدعاء دالة بتمرير وسيطة. إذا لم تتغير وسيطة الوظيفة ، فستشير نتيجة المكالمة useCallbackإلى نفس منطقة الذاكرة. في مثالنا ، هذا يعني أنه عند إعادة رسم نفس الأخبار ، لن يتغير onPressعنصر الدعامة NewsItemBody. يمكن استخدام الخطافات فقط في المكونات الوظيفية ، لذلك سيكون المظهر النهائي للمكون NewsItemكما يلي:

function NewsItemComponent(props) {
  ...
  const {itemKey, onBodyPress} = props.item;
  const onPressBody = useCallback(() => onBodyPress(itemKey), [itemKey, onBodyPress]);
  return (
    <View>
      ...
      <NewsItemBody
        ...
        onPress={onPressBody}
        ...
      />
    </View>
  );
}

والسجل:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3
ITEM_2
ITEM_TITLE_2
ITEM_1
ITEM_TITLE_1

كود المثال الكامل

المصفوفات والأشياء


في JavaScript ، يتم تمثيل الدوال ككائنات ، مع المصفوفات. لذلك ، فإن المثال من الكتلة السابقة هو حالة خاصة لإنشاء كائن جديد في الدعائم. إنه أمر شائع جدًا ، لذلك أضعه في فقرة منفصلة.

يؤدي أي إنشاء لوظائف أو صفائف أو كائنات جديدة في الدعائم إلى إعادة عرض المكون. خذ بعين الاعتبار هذه القاعدة في المثال التالي. دعنا نمر بأسلوب NewsItemBodyمشترك من قيمتين:

<NewsItemBody
  ...
  style={[styles.body, styles.item]}
  ...
/>

ومرة أخرى ، يعرض السجل أجهزة تقديم المكونات الإضافية:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1

لحل هذه المشكلة، يمكنك تحديد نمط مستقل من شأنها أن تجمع bodyو item، أو، على سبيل المثال، حرك إعلان مجموعة [styles.body, styles.item]إلى متغير العالمي.

كود المثال الكامل

مخفضات الصفيف


ضع في اعتبارك مصدرًا آخر شائعًا للفرامل المرتبطة بالاستخدام FlatList. تطبيق كلاسيكي يحتوي على قائمة طويلة من العناصر من الخادم ينفذ ترقيم الصفحات. أي أنها تقوم بتحميل مجموعة محدودة من العناصر في شكل الصفحة الأولى ، عندما تنتهي قائمة العناصر الحالية ، يتم تحميل الصفحة التالية ، وهكذا. قد يبدو مخفض قائمة العناصر كما يلي:

const newsIdList = (state = [], action) => {
  if (action.type === 'GOT_NEWS') {
    return action.news.map(item => item.key);
  } else if (action.type === 'GOT_OLDER_NEWS') {
    return [...state, ...action.news.map(item => item.key)];
  }
  return state;
};

عند تحميل كل صفحة تالية بأسلوب التطبيق ، يتم إنشاء مجموعة جديدة من المعرفات. إذا مررنا هذا الصفيف إلى الدعائم لاحقًا FlatList، فإليك ما ستبدو عليه سجلات تقديم المكون:

SCREEN
ITEM_<1..10>
--------------[ LOADING NEXT PAGE ]--------------
SCREEN
ITEM_<1..10>
ITEM_<1..20>
--------------[ LOADING NEXT PAGE ]--------------
SCREEN
ITEM_<1..20>
ITEM_<1..30>

لهذا المثال ، قمت ببعض التغييرات في تطبيق الاختبار.

  • اضبط حجم الصفحة على 10 أخبار.
  • item NewsItem FlatList-, connect. NewsItem React.Component .
  • .
  • . №1 .

يوضح المثال أنه عند تحميل كل صفحة تالية ، يتم عرض جميع العناصر القديمة مرة أخرى ، ثم يتم عرض العناصر القديمة وعناصر الصفحة الجديدة مرة أخرى. لعشاق الرياضيات: إذا كان حجم الصفحة متساويًا X، فعند تحميل iالصفحة ith ، بدلاً من تقديم Xعناصر جديدة فقط ، يتم تقديم العناصر (i - 1) * X + i * X.

تقول "حسنًا" ، "أتفهم سبب رسم جميع العناصر بعد إضافة صفحة جديدة: قام المخفض بإعادة مصفوفة جديدة ، ومساحة جديدة من الذاكرة ، وكل ذلك. ولكن لماذا نحتاج إلى تقديم القائمة القديمة قبل إضافة عناصر جديدة؟ " "سؤال جيد" ، سأجيبك. هذا نتيجة للعمل مع حالة المكون VirtualizedListعلى أساسهFlatList. لن أخوض في التفاصيل ، لأنها تسحب مقال منفصل. من يهتم ، أنصحك بالتعمق في الوثائق والمصدر.

كيف تتخلص من عدم المثالية؟ نعيد كتابة المخفض حتى لا يعيد مصفوفة جديدة لكل صفحة ، ولكنه يضيف عناصر إلى القائمة الحالية:

انتباه! أنتيباتيرن!
. , , , PureComponent, . , . . Redux.

const newsIdList = (state = [], action) => {
  if (action.type === 'GOT_NEWS') {
    return action.news.map(item => item.key);
  } else if (action.type === 'GOT_OLDER_NEWS') {
    action.news.forEach(item => state.push(item.key));
    return state;
    // return [...state, ...action.news.map(item => item.key)];
  }
  return state;
};

بعد ذلك ، سيبدو سجلنا كما يلي:

SCREEN
ITEM_<1..10>
--------------[ LOADING NEXT PAGE ]--------------
SCREEN
ITEM_<1..20>
--------------[ LOADING NEXT PAGE ]--------------
SCREEN
ITEM_<1..30>

لقد تخلصنا من عرض العناصر القديمة قبل إضافة العناصر إلى صفحة جديدة ، ولكن العناصر القديمة لا تزال مرسومة بعد تحديث القائمة. عدد الاداءات للصفحة التالية متساو الآن i * X. أصبحت الصيغة أبسط ، لكننا لن نتوقف عند هذا الحد. لدينا فقط Xعناصر جديدة ، ونريد فقط تقديم صور Xجديدة. سنستخدم الحيل المألوفة بالفعل لإزالة عرض الأخبار التي لم تغير الدعائم. عودة الاتصال بـ NewsItem:

SCREEN
ITEM_<1..10>
--------------[ LOADING NEXT PAGE ]--------------
SCREEN
ITEM_<11..20>
--------------[ LOADING NEXT PAGE ]--------------
SCREEN
ITEM_<21..30>

غرامة! الآن يمكننا أن نكون راضين عن أنفسنا. لا يوجد مكان لتحسينه.

كود المثال الكامل

سيشير القارئ اليقظ أنه بعد تطبيق الاتصال NewsItemبالسجل ، سيبدو في المثال الأخير ، بغض النظر عن كيفية تنفيذ المخفض. وسوف يكون ذلك صحيحًا - إذا تحقق مكون الأخبار من أدواته قبل العرض ، فلا يهم إذا تم استخدام المصفوفة القديمة من قبل المخفض أو قام بإنشاء مجموعة جديدة. يتم رسم عناصر جديدة ومرة ​​واحدة فقط. ومع ذلك ، فإن تغيير الصفيف القديم بدلاً من إنشاء صفيف جديد يحفظنا من العروض غير الضرورية للمكون FlatListالمستخدم فيه VirtualizedListوالتكرارات غير الضرورية لفحوصات معادلة الدعائم NewsItem. مع وجود عدد كبير من العناصر ، يؤدي هذا أيضًا إلى زيادة الأداء.

يجب أن يكون استخدام المصفوفات والأشياء القابلة للتغيير في المخفضات بحذر شديد. في هذا المثال ، هذا مبرر ، ولكن إذا كان لديك ، على سبيل المثال ، عادي PureComponent، عند إضافة عناصر إلى الصفيف القابل للتغيير ، فلن يتم عرض المكونات. في الواقع ، تظل دعائمها دون تغيير ، منذ قبل وبعد تحديث نقاط الصفيف إلى نفس منطقة الذاكرة. هذا يمكن أن يؤدي إلى عواقب غير متوقعة. لا عجب في أن المثال الموصوف ينتهك مبادئ Redux .

وشيء آخر ...


إذا كنت تستخدم مكتبات على مستوى العرض التقديمي ، أنصحك بالتأكد من أنك تفهم بالتفصيل كيفية تنفيذها. في تطبيقنا ، نستخدم مكونًا Swipeableمن المكتبة react-native-gesture-handler. يسمح لك بتنفيذ مجموعة من الإجراءات الإضافية عند تمرير بطاقة من القائمة.

في التعليمات البرمجية ، يبدو كما يلي:

<Swipeable
  ...
  renderRightActions={this.renderRightActions}
  ...
>

الطريقة renderRightActionsأو renderLeftActionsإرجاع المكون المعروض بعد السحب. لقد حددنا وغيّرنا ارتفاع اللوحة أثناء تغيير المكونات لتناسب المحتوى الضروري. هذه عملية تستهلك الكثير من الموارد ، ولكن إذا حدثت أثناء حركة التمرير ، فلن يرى المستخدم أي تداخل.


تكمن المشكلة في أن المكون Swipeableيستدعي الطريقة renderRightActionsفي وقت تقديم المكون الرئيسي. جميع العمليات الحسابية وحتى عرض شريط الإجراءات ، الذي لا يكون مرئيًا قبل التمرير ، يتم مقدمًا. لذلك ، يتم تنفيذ جميع هذه الإجراءات لجميع البطاقات في القائمة في نفس الوقت. تسبب هذا في "فرامل" كبيرة عند تمرير اللوحة.

تم حل المشكلة بالطريقة التالية. إذا تم رسم لوحة الإجراءات مع المكون الرئيسي ، وليس كنتيجة للتمرير السريع ، فستقوم الطريقة renderRightActionsبإرجاع عنصر فارغ Viewبحجم المكون الرئيسي. خلاف ذلك ، نرسم لوحة الإجراءات الإضافية كما كان من قبل.
أعطي هذا المثال لأن المكتبات الداعمة لا تعمل دائمًا كما تتوقع. وإذا كانت هذه مكتبات على مستوى العرض التقديمي ، فمن الأفضل التأكد من أنها لا تهدر الموارد غير الضرورية.

الموجودات


بعد التخلص من المشكلات الموضحة في المقالة ، قمنا بتسريع التطبيق بشكل كبير على React Native. الآن من الصعب تمييزه في الأداء عن أداء مماثل ، يتم تنفيذه محليًا. أدى العرض الزائد إلى إبطاء تحميل الشاشات الفردية ورد فعل المستخدم. الأهم من ذلك كله ، كان ملحوظًا في القوائم ، حيث يتم رسم عشرات المكونات في وقت واحد. لم نحسن كل شيء ، لكن الشاشات الرئيسية للتطبيق لم تعد تتباطأ.

يتم سرد النقاط الرئيسية للمقالة بإيجاز أدناه.

  1. React Native : Props/State- .
  2. , React.PureComponent, , .
  3. , shouldComponentUpdate React.Memo .
  4. - . , (shallow compare). , .
  5. يمكن أن يؤدي دعم المكتبات على مستوى العرض التقديمي إلى إهدار غير متوقع للموارد. من الجدير أن نكون حذرين في تطبيقهم.

هذا كل شئ. اتمنى ان تجد المعلومة مفيدة. سأكون سعيدا بأي ملاحظات!

مصادر مفيدة


  1. فهم التقديم في React + Redux
  2. مقارنة الكائنات في جافا سكريبت
  3. تحسين الأداء في المكونات الوظيفية للتفاعل باستخدام React.memo ()
  4. كيف يحقق Discord أداء iOS الأصلي مع React Native

All Articles