تحتوي هذه المقالة على تسع نصائح حول كيفية تحسين أداء تطبيق Vue ، وزيادة سرعة العرض وتقليل حجم الحزمة.
العمل مع المكونات
مكونات وظيفية
لنفترض أن لدينا مكونًا بسيطًا وصغيرًا. كل ما يفعله هو عرض علامة معينة حسب القيمة التي تم تمريرها:
<template> <div> <div v-if="value"></div> <section v-else></section> </div> </template> <script> export default { props: ['value'] } </script>
يمكن تحسين هذا المكون بإضافة السمة
الوظيفية . مكون وظيفي يجمع في وظيفة بسيطة وليس لديه حالة محلية. نتيجة لهذا ، يكون أدائها أعلى بكثير:
<template functional> <div> <div v-if="props.value"></div> <section v-else></section> </div> </template> <script> export default { props: ['value'] } </script>
مثال على المكونات الوظيفية التي قد تبدو في Vue v3.0فصل إلى مكونات تابعة
تخيل مكونًا لعرضه تحتاج إلى القيام ببعض المهام المعقدة:
<template> <div :style="{ opacity: number / 300 }"> <div>{{ heavy() }}</div> </div> </template> <script> export default { props: ['number'], methods: { heavy () { } } } </script>
المشكلة هنا هي أن Vue ستنفذ طريقة () الثقيلة في كل مرة يتم فيها إعادة تقديم المكون ، أي في كل مرة تتغير فيها قيمة الدعائم.
يمكننا تحسين مثل هذا المكون بسهولة إذا فصلنا الطريقة الثقيلة إلى مكون فرعي:
<template> <div :style="{ opacity: number / 300 }"> <ChildComp/> </div> </template> <script> export default { props: ['number'], components: { ChildComp: { methods: { heavy () { } }, render (h) { return h('div', this.heavy()) } } } } </script>
لماذا؟ تتخطى Vue تلقائيًا تقديم المكونات التي لم تتغير فيها البيانات التابعة. وبالتالي ، عندما يتم تغيير الدعائم في المكون الأصل ، سيتم إعادة استخدام الطفل ولن يتم إعادة تشغيل طريقة () الثقيلة.
لاحظ أن هذا منطقي فقط إذا لم يكن لدى المكون الفرعي تبعية للبيانات في المكون الأصل. وإلا ، سيتم إعادة إنشاء الطفل مع الوالد ، ومن ثم فإن هذا التحسين لا معنى له.
Getter ذاكرة التخزين المؤقت المحلية
يحتوي المكون التالي على نوع من الخصائص المحسوبة استنادًا إلى الخاصية المحسوبة الثانية:
<template> <div :style="{ opacity: start / 300 }">{{ result }}</div> </template> <script> export default { props: ['start'], computed: { base () { return 42 }, result () { let result = this.start for (let i = 0; i < 1000; i++) { result += this.base } return result } } } </script>
الشيء المهم هنا هو أن الخاصية الأساسية تسمى في حلقة ، مما يؤدي إلى مضاعفات. في كل مرة يتم فيها الوصول إلى البيانات التفاعلية ، يدير Vue بعض المنطق لتحديد كيف وما هي البيانات التي تقوم بالوصول إليها لبناء التبعيات وما إلى ذلك. يتم تلخيص هذه النفقات العامة الصغيرة إذا كان هناك الكثير من المكالمات ، كما في مثالنا.
لإصلاح ذلك ، ما عليك سوى الوصول إلى الأساس مرة واحدة وتوفير القيمة إلى متغير محلي:
<template> <div :style="{ opacity: start / 300 }">{{ result }}</div> </template> <script> export default { props: ['start'], computed: { base () { return 42 }, result () { const base = this.base </script>
إعادة استخدام DOM مع v-show
ألقِ نظرة على المثال التالي:
<template functional> <div> <div v-if="props.value"> <Heavy :n="10000"/> </div> <section v-else> <Heavy :n="10000"/> </section> </div> </template>
هنا لدينا مكون مجمّع يستخدم v-if و v-else للتبديل بعض المكونات الفرعية.
من المهم أن نفهم كيف تعمل v-if. في كل مرة ، عندما يتم تبديل الحالة ، سيتم إتلاف مكون فرعي بالكامل (يسمى الخطاف المدمر) ، وسيتم حذف جميع العقد من DOM) ، وسيتم إنشاء الثاني وتثبيته بالكامل مرة أخرى. وإذا كانت هذه المكونات "ثقيلة" ، فستحصل على تعليق للواجهة في وقت التبديل.
حل أكثر إنتاجية - استخدم العرض الخامس
<template functional> <div> <div v-show="props.value"> <Heavy :n="10000"/> </div> <section v-show="!props.value"> <Heavy :n="10000"/> </section> </div> </template>
في هذه الحالة ، سيتم إنشاء وتثبيت المكونين الفرعيين على الفور وتواجدهما في وقت واحد. وبالتالي ، لا يحتاج Vue إلى تدمير وإنشاء مكونات عند التبديل. كل ما يفعله هو إخفاء واحد وإظهار الثاني باستخدام CSS. لذا فإن تبديل الحالة سيكون أسرع بكثير ، لكن يجب أن تفهم أن هذا سيؤدي إلى ارتفاع تكاليف الذاكرة.
استخدم <حفظ>
لذلك ، مكون بسيط هو مكون المجمع على
جهاز التوجيه .
<template> <div id="app"> <router-view/> </div> </template>
تشبه المشكلة المثال السابق - سيتم إنشاء جميع المكونات الموجودة في جهاز التوجيه الخاص بك وتثبيتها وتدميرها أثناء الانتقال بين المسارات.
والحل هنا مشابه - لإخبار Vue بعدم التدمير بل لتخزين المكونات وإعادة استخدامها. يمكنك القيام بذلك باستخدام المكون
<keep-alive> المدمج في:
<template> <div id="app"> <keep-alive> <router-view/> </keep-alive> </div> </template>
سيؤدي هذا التحسين إلى زيادة استهلاك الذاكرة ، حيث ستدعم Vue المزيد من المكونات "على قيد الحياة". لذلك ، يجب ألا تستخدم هذا النهج بدون تفكير في جميع المسارات ، خاصة إذا كان لديك الكثير منها. يمكنك استخدام سمات التضمين والاستبعاد لتعيين القواعد التي يجب أن يتم تخزينها مؤقتًا في المسارات والتي لا:
<template> <div id="app"> <keep-alive include="home,some-popular-page"> <router-view/> </keep-alive> </div> </template>
تقديم المؤجلة
المثال التالي هو أحد المكونات التي تحتوي على عدة عناصر تابعة ثقيلة جدًا:
<template> <div> <h3>I'm an heavy page</h3> <Heavy v-for="n in 10" :key="n"/> <Heavy class="super-heavy" :n="9999999"/> </div> </template>
المشكلة هي أن معالجة المكونات هي عملية متزامنة في الخيط الرئيسي. وفي هذا المثال ، لن يعرض المستعرض أي شيء حتى يتم الانتهاء من معالجة جميع المكونات ، الوالد والطفل. وإذا تطلب الأمر معالجة الكثير من الشركات التابعة ، فستتأخر في الواجهة أو شاشة فارغة لبعض الوقت.
يمكنك تحسين الموقف عن طريق تأخير تقديم المكونات الفرعية:
<template> <div> <h3>I'm an heavy page</h3> <template v-if="defer(2)"> <Heavy v-for="n in 10" :key="n"/> </template> <Heavy v-if="defer(3)" class="super-heavy" :n="9999999"/> </div> </template> <script> import Defer from '@/mixins/Defer' export default { mixins: [ Defer() ] } </script>
هنا ، ترجع الدالة defer (n) إطارات n غير صحيحة. بعد ذلك يعود دائما صحيح. يتم استخدامه لتأجيل معالجة جزء من القالب بواسطة عدة إطارات ، وبالتالي إعطاء المستعرض القدرة على رسم الواجهة.
كيف يعمل؟ كما كتبت أعلاه ، إذا كانت الحالة الموجودة في توجيه v-if خاطئة ، فسيتجاهل Vue جزءًا من القالب تمامًا.
في الإطار الأول من الرسوم المتحركة نحصل على:
<template> <div> <h3>I'm an heavy page</h3> <template v-if="false"> <Heavy v-for="n in 10" :key="n"/> </template> <Heavy v-if="false" class="super-heavy" :n="9999999"/> </div> </template>
تقوم أداة Vue ببساطة بتركيب الرأس ، ويقوم المستعرض بعرضه ويطلب إطارًا ثانيًا. في هذه المرحلة ، سيبدأ التكرار (2) في العودة إلى حقيقة
<template> <div> <h3>I'm an heavy page</h3> <template v-if="true"> <Heavy v-for="n in 10" :key="n"/> </template> <Heavy v-if="false" class="super-heavy" :n="9999999"/> </div> </template>
ستعمل Vue على إنشاء 10 مكونات فرعية وتركيبها. سيقوم المستعرض بعرضها وطلب الإطار التالي ، والذي سيعود به تأجيل (3).
وبالتالي ، أنشأنا مكونًا ثقيلًا تتم معالجته تدريجيًا ، مما يمنح المتصفح القدرة على عرض الأجزاء المثبتة بالفعل من القالب ، والتي ستحسن UX كثيرًا نظرًا لأن الواجهة لن تبدو معلقة.
رمز تأجيل الخلاط:
export default function (count = 10) { return { data () { return { displayPriority: 0 } }, mounted () { this.runDisplayPriority() }, methods: { runDisplayPriority () { const step = () => { requestAnimationFrame(() => { this.displayPriority++ if (this.displayPriority < count) { step() } }) } step() }, defer (priority) { return this.displayPriority >= priority } } } }
تحميل عنصر كسول
الآن دعنا نتحدث عن استيراد المكونات الفرعية:
import Heavy from 'Heavy.js' export default { components: { Heavy } }
في عملية الاستيراد التقليدية ، يتم تحميل المكون الفرعي فورًا ، حيث يصل المستعرض إلى عبارة الاستيراد. أو إذا كنت تستخدم أداة التجميع ، فسيتم تضمين المكون التابع لك في الحزمة العامة.
ومع ذلك ، إذا كان المكون الفرعي كبير جدًا ، فمن المنطقي تحميله بشكل غير متزامن.
من السهل جدًا القيام بما يلي:
const Heavy = () => import('Heavy.js') export default { components: { Heavy } }
هذا كل ما تحتاجه . يمكن Vue العمل مع مكونات التحميل كسول من خارج منطقة الجزاء. سيقوم بتنزيل المكون بنفسه عند الحاجة وعرضه بمجرد أن يكون جاهزًا. يمكنك استخدام هذا النهج للتحميل الكسول في أي مكان.
إذا كنت تستخدم أداة التجميع ، فسيتم إخراج كل شيء عن Heavy.js في ملف منفصل. وبالتالي ، ستقلل من وزن الملفات أثناء التنزيل الأولي وتزيد من سرعة العرض.
كسل تحميل المشاهدات
التحميل الكسول مفيد للغاية في حالة المكونات المستخدمة للطرق. نظرًا لأنك لست بحاجة إلى تنزيل جميع المكونات للطرق في نفس الوقت ، نوصيك
دائمًا باستخدام هذا النهج:
const Home = () => import('Home.js') const About = () => import('About.js') const Contacts = () => import('Contacts.js') new VueRouter({ routes: [ { path: '/', component: Home }, { path: '/about', component: About }, { path: '/contacts', component: Contacts }, ] })
سيتم تحميل كل مكون من هذه المكونات فقط عندما يطلب المستخدم أولاً المسار المحدد. وليس قبل ذلك.
مكونات ديناميكية
وبالمثل ، يمكنك بسهولة استخدام التحميل البطيء مع المكونات الديناميكية:
<template> <div> <component :is="componentToShow"/> </div> </template> <script> export default { props: ['value'], computed: { componentToShow() { if (this.value) { return () => import('TrueComponent.js') } return () => import('FalseComponent.js') } } } </script>
مرة أخرى ، سيتم تحميل كل مكون فقط عند الحاجة.
العمل مع Vuex
تجنب ارتكاب كبير
تخيل أنك بحاجة إلى تخزين مجموعة كبيرة من البيانات في التخزين:
fetchItems ({ commit }, { items }) { commit('clearItems') commit('addItems', items)
المشكلة هي هذا. جميع ارتكاب عمليات متزامنة. هذا يعني أن معالجة مجموعة كبيرة ستحجب واجهتك طوال مدة العمل.
لحل هذه المشكلة ، يمكنك تقسيم الصفيف إلى أجزاء وإضافتها واحدة تلو الأخرى ، مما يتيح للمتصفح وقتًا لرسم إطار جديد:
fetchItems ({ commit }, { items, splitCount }) { commit('clearItems') const queue = new JobQueue() splitArray(items, splitCount).forEach( chunk => queue.addJob(done => {
الأطراف الأصغر التي تضيفها البيانات إلى وحدة التخزين ، ستظل واجهتك أكثر سلاسة وكلما طال الوقت الإجمالي لإكمال المهمة.
بالإضافة إلى ذلك ، مع هذا النهج ، لديك القدرة على عرض مؤشر تنزيل للمستخدم. والتي سوف تحسن بشكل كبير تجربته مع التطبيق.
قم بإيقاف التفاعل عندما لا تكون هناك حاجة لذلك
والمثال الأخير لهذا اليوم. لدينا مهمة مماثلة: نضيف مجموعة من الكائنات الكبيرة جدًا إلى التخزين مع مجموعة من مستويات التعشيش:
const data = items.map( item => ({ id: uid++, data: item,
والحقيقة هي أن Vue ستعمل بشكل متكرر على اجتياز جميع الحقول المتداخلة وجعلها تفاعلية.إذا كان لديك الكثير من البيانات ، فقد يكون هذا مكلفًا ، لكنه أكثر أهمية بكثير - غير ضروري.
إذا كان التطبيق الخاص بك مبنيًا بحيث يعتمد فقط على كائن المستوى الأعلى ولا يشير إلى البيانات التفاعلية في مكان ما عدة مستويات أدناه ، فيمكنك تعطيل التفاعل ، وبالتالي حفظ Vue من مجموعة من الأعمال غير الضرورية:
const data = items.map( item => optimizeItem(item) ) function optimizeItem (item) { const itemData = { id: uid++, vote: 0 } Object.defineProperty(itemData, 'data', {