مرحبا يا هابر! اسمي 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 ، هناك شرطان لعرض المكون:- تغيير دعائمه / حالته ،
- تقديم المكون الرئيسي.
يمكن إعادة تعريف الوظيفة في أحد المكونات 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. الآن من الصعب تمييزه في الأداء عن أداء مماثل ، يتم تنفيذه محليًا. أدى العرض الزائد إلى إبطاء تحميل الشاشات الفردية ورد فعل المستخدم. الأهم من ذلك كله ، كان ملحوظًا في القوائم ، حيث يتم رسم عشرات المكونات في وقت واحد. لم نحسن كل شيء ، لكن الشاشات الرئيسية للتطبيق لم تعد تتباطأ.يتم سرد النقاط الرئيسية للمقالة بإيجاز أدناه.- React Native : Props/State- .
- ,
React.PureComponent
, , . - ,
shouldComponentUpdate
React.Memo
. - - . , (shallow compare). , .
- يمكن أن يؤدي دعم المكتبات على مستوى العرض التقديمي إلى إهدار غير متوقع للموارد. من الجدير أن نكون حذرين في تطبيقهم.
هذا كل شئ. اتمنى ان تجد المعلومة مفيدة. سأكون سعيدا بأي ملاحظات!مصادر مفيدة
- فهم التقديم في React + Redux
- مقارنة الكائنات في جافا سكريبت
- تحسين الأداء في المكونات الوظيفية للتفاعل باستخدام React.memo ()
- كيف يحقق Discord أداء iOS الأصلي مع React Native