تواجهنا جميعًا الإحباط من الانتظار أمام شاشات التحميل الطويلة، فقط لنجد أنفسنا عالقين مع صفحات لا تستجيب. تشاهد أيضًا عدادات التحميل في كل مكان، ولكن لا يبدو أن أي شيء يتحرك إلى الأمام. دعوني أصور لكم صورة أوضح:
هذا يحدث عادة لأن الموقع يحاول جلب جميع البيانات الضرورية بمجرد وصولك إلى الصفحة. قد يكون السبب في ذلك إنشاء طلب API يتم معالجته، أو أن APIs متعددة تجلب البيانات بترتيب، مما يسبب تأخير في تحميل الصفحة.
النتيجة؟ تجربة مستخدم سيئة. قد تفكر، “كيف لشركة كبيرة تلك لا تعطي الأولوية لتجربة المستخدم؟ هذا محبط.” ونتيجة لذلك، يغادر المستخدمون الصفحة غالبًا، مؤثرين على المؤشرات الرئيسية وقد يؤثرون على الأرباح.
ولكن ماذا لو استطعت جلب البيانات لهذه الصفحات الثقيلة مسبقًا، بحيث يكون بموقع وصول المستخدم إلى الصفحة يمكنه التفاعل معها على الفور؟
هنا يأتي مفهوم الإستعلام المسبق، وهذا بالضبط ما سنغوص فيه في هذه المقالة البログ. إذن، دون أي تأخير أخير، دعونا نبدأ!
جدول المحتويات
التوافر الزائد كحل للمشكلة
هذه هي النسخة المنقحة بمجرد التصحيح النصوي والتأليف اللغوي:
للمشكلة المعروفة أعني أننا نريد أخذ البيانات لصفحة معينة قبل تحميلها على الموقع لكي لا يتوجب على المستخدم تحميل البيانات حين تحميل الصفحة. هذا يدعى التوافر الزائد. من المنظور التقني ، تعريفه يبدو كما يلي:
هو طريقة للحصول على البيانات المطلوبة من قبل لكي لا يتوجب على المكون الرئيسي أن ينتظر البيانات، مما يحسن التجربة.
هذا يمكن أن يحسن التجربة للمستخدمين، ويقوم بتعزيز ثقة المستخدم في موقعك.
التوافر الزائد هو حل بسيط وجميل يعتمد أكثر على المستخدم من أي عملية قياسية. لتنفيذ التوافر الزائد، يتوجب علينا فهم تصرفات المستخدم في الموقع. أي أياً كانت هي الصفحات الأكثر زيارة، أو المكونات التي تحصل على البيانات في تفاعلات صغيرة (مثل المؤشر).
تتم إجراء تحليل لتلك السيناريوهات، ومن المنطقي تطبيق التحميل المسبق عليها. ومع ذلك، يجب أن نكون كمبرمجون متحذرين من استخدام هذه المفهوم. الكثير من التحميل المسبق يمكن أيضًا أن يبطئ موقعك الإلكتروني لأنك تحاول إحضار الكثير من البيانات للسيناريوهات المستقبلية، والذي قد يحجب التحميل من البيانات للصفحة الرئيسية.
كيف يحسن التحميل المسبق تجربة المستخدم
لننظر إلى بعض السيناريوهات التي تحتمل فيها فوائد التحميل المسبق:
-
تحميل البيانات/الصفحة مبكراً للروابط الأكثر زيارة من صفحتك الواقعة. على سبيل المثال، لو أن لديك رابط “اتصل بنا”. دعنا نفترض أن هذا هو الرابط الذي يقوم العديد من المستخدمين بالتحقق به ويحتوي على الكثير من البيانات عندما يتم تحميله. بدلاً من تحميل البيانات عند تحميل صفحة “اتصل بنا”، يمكنك ببساطة جعل التحميل يحدث في الصفحة الرئيسية حتى لا يتوجب عليك الانتظار في صفحة “اتصل بنا” لتحميل البيانات. يمكنك القراءة المزيد عن تحميل صفحات المسبق هنا.
-
تحميل بيانات الجدول للصفحات اللاحقة.
-
استقطاب البيانات من العنصر الأصلي وتحميلها في العنصر الفرعي.
-
استقطاب البيانات التي يتوجب عرضها في مربع النافذة المنبثقة.
هذه بعض الطرق لتحقيق الاستقطاب في تطبيقك وكيف يساعد ذلك في تحسين تجربة المستخدم.
في هذه المقالة سنتحدث عن السيناريو الأخير: “استقطاب البيانات التي يتوجب عرضها في النافذة المنبثقة”. هذا مثال كلاسيكي حيث يمكن أن يكون الاستقطاب مفيداً ويوفر تجربة أكثر سلاسة للمستخدم.
فهم المشكلة
دعوني أحدد المشكلة هنا. تخيل المشهد التالي:
-
لديك عنصر يعرض معلومات محددة.
-
هناك عنصر داخل هذا العنصر يظهر نافذة/تلميحة أخرى عندما تتمركز عليه.
-
تحميل البيانات عندما تحمل النافذة.
الآن تخيل أن المستخدم يتمركز على العنصر ويجب أن ينتظر لتحميل البيانات وعرضها في النافذة. أثناء هذه الانتظار، سيرى العارض الخاص بالبيانات المحملة.
ستبدو السيناريو كما يلي:
يحدث التأنيب الطويل لما يبقى على وجه المسألة عندما يتم توفير الصورة،
لحل هذه المشكلة يوجد بضعة حلول ستساعدك على البدء وتخصيص الحل وفقاً لما تحتاج.
حل #1: تحميل البيانات من قبل في المكون الوالد
هذا الحل ملهم من مدونة Martin Fowler. يسمح لك بالحصول على البيانات قبل ظهور النافذة، بدلاً من الحصول عند تحميل المكون.
تظهر النافذة عندما تتم توفيرها عند المؤشر فوقها. يمكننا أن نحصل على البيانات عندما تأتي الفأرة إلى المكون الوالد. قبل أن يتم توفير المكون الفعال —الصورة — سيكون لدينا البيانات للنافذة الإضافية وسنتقلها إلى مكون النافذة.
هذا الحل لا يقلل بالكامل من حالة التحميل ولكنه يساعد بشكل كبير على خفض أحتمالية رؤية هذه الحالة بشكل كبير.
حل #2: تحميل البيانات في التحميل الرئيسي للصفحة
هذا الحل ملهم من x.com حيث يجري لقدم البيانات الجزئي في التحميل الرئيسي للصفحة ويجري البيانات الباقية عند تثبيت المكون.
كما ترون من الفيديو السابق، يمكنك رؤية تفاصيل بيانات المستخدم في النافذة الإضافية. إذا نظرت بدقة، يتم جلب البيانات المتعلقة بالمتابعين في الوقت المناسب.
هذه التقنية فائقة الكفاءة عندما يكون لديك الكثير من البيانات التي يتم عرضها في النافذة المنبثقة ولكن إسترجاعها قد يكون باهظ الثمن عند تمرير النافذة أو عند تحميل الصفحة الرئيسية.
حل أفضل سيكون تحميل جزء من البيانات المطلوبة على الصفحة الرئيسية وتحميل بقية البيانات عندما تتم موضع الكمبوننت.
في مثالنا، قمنا بإسترجاع البيانات للنافذة المنبثقة عندما يدخل المؤشر في العنصر الأب للصورة. الآن تخيل أنك تحتاج إلى إسترجاع تفاصيل إضافية بمجرد تحميل بيانات النافذة. وبناءً على طريقة x.com التي ذكرناها، يمكننا إسترجاع بيانات إضافية عند تحميل النافذة. هاهو نتيجة ذلك:
هنا، نقوم بعمل الأشياء التالية:
-
نسترجع البيانات الرئيسية التي يكفي فقط لرender النافذة المنبثقة عند دخول المؤشر إلى الكمبوننت الأب للصورة.
-
هذا يعطينا الوقت الكافي لإسترجاع البيانات الرئيسية.
-
عند تحميل النافذة، نسترجع بيانات أخرى، وهي عدد الألبومات. بينما يقرأ المستخدم البيانات مثل الاسم والبريد الإلكتروني، سيكون لدينا البيانات التالية جاهزة للإطلاع.
بهذه الطريقة، يمكننا إجراء تغييرات صغيرة وذكية لتقليل النظر إلى محمل البيانات على الشاشة 😊.
كيفية
في هذا المقطع سنقوم بالمرور سريع على كيفية تنفيذ مثال التحاليل المسبقة أعلاه.
تأسيس المشروع
للبدء في إنشاء تطبيق التحاليل المسبقة، تتبع عملية أدناه:
يمكنك استخدام vitejs (هذا ما أستخدمته) أو create-react-app لإنشاء تطبيقك. ألصق أوامر الأداة التالية في محررك اللغة:
yarn create vite prefetch-example --template react-ts
بمجرد إنشاء التطبيق، يجب أن يكون لديك هيكل المجلد التالي عندما تفتح مجلد prefetch-example مع VS Code.
حالياً سنغوص في المكونات التي سنبنيها لهذا التطبيق.
المكونات
في هذا المثال سنستخدم 3 مكونات:
-
PopoverExample
-
UserProfile
-
UserProfileWithFetching
PopoverExample
مكون
دعونا نبدأ بالمكون الأول وهو PopoverExample
. هذا المكون يعرض تصوير تمثالي وبعض النصوص على الجانب الأيمن منه. ينبغي أن يبدو مثل هذا:
الغرض من هذا المكون هو أن يخدم مثالًا يشبه الحالات الحقيقية. الصورة في هذا المكون تحمل مكون النافذة الإضافية عندما يتم تحويل المؤشر إليها.
هذا الكود للمكون:
import { useState } from "react";
import { useFloating, useHover, useInteractions } from "@floating-ui/react";
import ContentLoader from "react-content-loader";
import UserProfile from "./UserProfile";
import UserProfileWithFetching from "./UserProfileWithFetching";
export const MyLoader = () => (
<ContentLoader
speed={2}
width={340}
height={84}
viewBox="0 0 340 84"
backgroundColor="#d1d1d1"
foregroundColor="#fafafa"
>
<rect x="0" y="0" rx="3" ry="3" width="67" height="11" />
<rect x="76" y="0" rx="3" ry="3" width="140" height="11" />
<rect x="127" y="48" rx="3" ry="3" width="53" height="11" />
<rect x="187" y="48" rx="3" ry="3" width="72" height="11" />
<rect x="18" y="48" rx="3" ry="3" width="100" height="11" />
<rect x="0" y="71" rx="3" ry="3" width="37" height="11" />
<rect x="18" y="23" rx="3" ry="3" width="140" height="11" />
<rect x="166" y="23" rx="3" ry="3" width="173" height="11" />
</ContentLoader>
);
export default function PopoverExample() {
const [isOpen, setIsOpen] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [data, setData] = useState({});
const { refs, floatingStyles, context } = useFloating({
open: isOpen,
onOpenChange: setIsOpen,
placement: "top",
});
const hover = useHover(context);
const { getReferenceProps, getFloatingProps } = useInteractions([hover]);
const handleMouseEnter = () => {
if (Object.keys(data).length === 0) {
setIsLoading(true);
fetch("https://jsonplaceholder.typicode.com/users/1")
.then((resp) => resp.json())
.then((data) => {
setData(data);
setIsLoading(false);
});
}
};
return (
<div
id="hover-example"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
textAlign: "left",
}}
onMouseEnter={handleMouseEnter}
>
<span
style={{
padding: "1rem",
}}
>
<img
ref={refs.setReference}
{...getReferenceProps()}
style={{
borderRadius: "50%",
}}
src="https://cdn.jsdelivr.net/gh/alohe/avatars/png/vibrent_5.png"
/>
</span>
<p>
Lorem Ipsum is simply dummy text of the printing and typesetting
industry. Lorem Ipsum has been the industry's standard dummy text ever
since the 1500s, when an unknown printer took a galley of type and
scrambled it to make a type specimen book. It has survived not only five
centuries, but also the leap into electronic typesetting, remaining
essentially unchanged. It was popularised in the 1960s with the release
of Letraset sheets containing Lorem Ipsum passages, and more recently
with desktop publishing software like Aldus PageMaker including versions
of Lorem Ipsum.
</p>
{isOpen && (
<div
className="floating"
ref={refs.setFloating}
style={{
...floatingStyles,
backgroundColor: "white",
color: "black",
padding: "1rem",
fontSize: "1rem",
}}
{...getFloatingProps()}
>
{isLoading ? (
<MyLoader />
) : (
<UserProfile hasAdditionalDetails {...data} />
)}
{/* <UserProfileWithFetching /> */}
</div>
)}
</div>
);
}
هناك عدد من الأشياء التي تحدث هنا، دعوني أشرحها خطوة بخطوة:
-
لدينا
div
والأب الذي يُدعىhover-example
والذي يحتوي على صورة وبعض النصوص. -
بعد ذلك، قمنا بعرض
div
بإعتبارات حالية بإسم الصفfloating
. هذه هي العنصر الفعال للنافذة المنبثقة التي تظهر عند التحويم على الصورة.- استخدمنا مكتبة
floating-ui
وأمثلة التحويم الأساسية لها basic hover example لتحقيق تأثير التحويم للنافذة المنبثقة.
- استخدمنا مكتبة
-
داخل النافذة المنبثقة قمنا بتحميل العنصر
UserProfile
ومحمل الهيكل العظمي بشكل إعتباري. يظهر هذا المحمل عندما نحصل على بيانات الملف الشخصي للمستخدم. سنتحدث عن هذا لاحقًا. -
استخدمنا المكتبة react-content-loader في مكون
MyLoader
. ولهذه المكتبة موقع إلكتروني يساعدك في إنشاء المحركات، يمكنك أن تراه هنا.
المكون UserProfile
وعدا إعداد مثالنا للنافذة التي تظهر تحت الشرح، فهو الوقت المناسب للخوض في تفاصيل المكون UserProfile
التي تظهر داخل مكون النافذة.
هذا المكون يظهر داخل النافذة التي تظهر تحت الشرح. من شأن هذا المكون أن يحمل التفاصيل name
email
phone
website
التي يتم جمعها من API المحل للJSON.
لإظهار المثال للتوافير المستقبلي، يتوجب علينا أن نتأكد من أن المكون UserProfile
يتصرف بالفعل كمكون شفري; أي أن لا يوجد أي قواعد للتحميل البوردي في الداخل منه.
الشيء الرئيسي الذي يجب أن نلاحظه حول هذا المكون هو أن التحميل البيانات يحدث من قبل المكون الوالد وهو PopoverExample
المكون. في هذا المكون ، نبدأ بالتحميل من خلال هذا المكون (ال事件 mouseenter
). هذه هي حلولنا الرقم 1 التي تحدثنا عنها من قبل.
هذا يعطيك وقت كافي للتحميل حتى يتوجب المستخدم على توفير الصورة. هذا هو الشيء:
import { useEffect, useState } from "react";
import ContentLoader from "react-content-loader";
const MyLoader = () => (
<ContentLoader
speed={2}
viewBox="0 0 476 124"
backgroundColor="#d1d1d1"
foregroundColor="#fafafa"
>
<rect x="4" y="43" rx="0" ry="0" width="98" height="30" />
</ContentLoader>
);
export default function UserProfile(props: Record<string, string | boolean>) {
const { name, email, phone, website, hasAdditionalDetails } = props;
const [isLoading, setIsLoading] = useState(false);
const [additionalData, setAdditionalData] = useState(0);
useEffect(() => {
if (hasAdditionalDetails) {
setIsLoading(true);
fetch("https://jsonplaceholder.typicode.com/albums")
.then((resp) => resp.json())
.then((data: Array<unknown>) => {
const albumCount = data.reduce((acc, curr) => {
if (curr.userId === 1) acc += 1;
return acc;
}, 0);
setAdditionalData(albumCount);
})
.finally(() => {
setIsLoading(false);
});
}
}, [hasAdditionalDetails]);
return (
<div id="user-profile">
<div id="user-name">name: {name}</div>
<div id="user-email">email: {email}</div>
<div id="user-phone">phone: {phone}</div>
<div id="user-website">website: {website}</div>
{hasAdditionalDetails && (
<>
{isLoading ? (
<MyLoader />
) : (
<div id="user-albums">Album Count: {additionalData}</div>
)}
</>
)}
</div>
);
}
يستخدم هذا المكون الخاص بـ hasAdditionalDetails
الخيار. مقصد هذا الprop
هو تحميل البيانات الإضافية عند تثبيط المكون. يوضح هذا الحلول الرقم 2 الذي ذكرناه فوق.
مكون UserProfileWithFetching
هذا المكون مماثل لمكون UserProfile
بكثير. إنه يحتوي على المنطقة التي تحمل المعلومات عندما يتم تحميله. غرض هذا المكون هو إظهار ما تبدو عليه حلول عامة دون تقنية التوافير المستقبلية.
لذلك سيتم تحميل البيانات دوماً عند تثبيط المكون، وهذا يعطي من شكل المحرك الهيكلي.
هذا هو الشيء:
import { useEffect, useState } from "react";
import { MyLoader } from "./PopoverExample";
export default function UserProfileWithFetching() {
const [isLoading, setIsLoading] = useState(false);
const [data, setData] = useState<Record<string, string>>({});
useEffect(() => {
setIsLoading(true);
fetch("https://jsonplaceholder.typicode.com/users/1")
.then((resp) => resp.json())
.then((data) => {
setData(data);
setIsLoading(false);
});
}, []);
if (isLoading) return <MyLoader />;
return (
<div id="user-profile">
<div id="user-name">name: {data.name}</div>
<div id="user-email">email: {data.email}</div>
<div id="user-phone">phone: {data.phone}</div>
<div id="user-website">website: {data.website}</div>
</div>
);
}
يمكن الحصول على كل البرنامج هنا. هنا.
والتوافير المستقبلي قد تسبب أيضًا بالبطء.
ونصيحة واحدة، لكن التوافير المستقبلي ليس جي
-
قد يبطئ تطبيقك.
-
قد يؤذي بتجربة المستخدم إذا لم يتم تطبيق التوافر المتوقع بطريقة استراتيجية.
يتوجب تطبيق التوافر المتوقع عندما تعرف سلوك المستخدم. أي أنه يمكنك تنبؤ بحركة المستخدم من خلال المعاملات ومعرفة إن كان يزور صفحة ما بواسطة. في هذه الحالة ، توفير التوافر المتوقع هو فكرة جيدة.
لذا تذكر دائماً أن تطبيق التوافر المتوقع بطريقة استراتيجية.
الخلاصة
هذا كل شيء! أتمنى أن تعجب من مدونتي. في هذه المدونة ، تعلمت أن تنفيذ التوافر المتوقع يمكن تحسين سرعة ومسئولية تطبيقك الإلكتروني بشكل كبير ، مما يحسن رضا المستخدم.
للقراءة المتقدم ، يرجى مراجعة المقالات التالية:
لمعلومات أكثر، يمكنك متابعةي على Twitter، GitHub، و LinkedIn.
Source:
https://www.freecodecamp.org/news/boost-web-performance-with-prefetching/