فهم GraphQl من جانب العميل باستخدام Apollo-Client في تطبيقات React
نشرت: 2022-03-10وفقًا لـ State of JavaScript 2019 ، يرغب 38.7٪ من المطورين في استخدام GraphQL ، بينما يرغب 50.8٪ من المطورين في تعلم GraphQL.
نظرًا لكونها لغة استعلام ، فإن GraphQL تبسط سير العمل في بناء تطبيق العميل. يزيل تعقيد إدارة نقاط نهاية API في تطبيقات جانب العميل لأنه يعرض نقطة نهاية HTTP واحدة لجلب البيانات المطلوبة. ومن ثم ، فإنه يزيل الإفراط في الجلب للبيانات ونقص الجلب ، كما هو الحال في REST.
لكن GraphQL هي مجرد لغة استعلام. من أجل استخدامها بسهولة ، نحتاج إلى منصة تقوم برفع الأحمال الثقيلة نيابة عننا. إحدى هذه المنصات هي أبولو.
منصة Apollo عبارة عن تطبيق لـ GraphQL ينقل البيانات بين السحابة (الخادم) إلى واجهة المستخدم الخاصة بتطبيقك. عند استخدام عميل Apollo ، يتم تغليف كل منطق استرداد البيانات وتتبع وتحميل وتحديث واجهة المستخدم بواسطة خطاف useQuery
(كما في حالة React). ومن ثم ، فإن جلب البيانات هو إعلاني. كما أن لديها ذاكرة تخزين مؤقت صفرية التكوين. فقط من خلال إعداد Apollo Client في تطبيقك ، ستحصل على ذاكرة تخزين مؤقت ذكية خارج الصندوق ، دون الحاجة إلى تكوين إضافي.
Apollo Client قابل للتشغيل البيني أيضًا مع أطر أخرى ، مثل Angular و Vue.js و React.
ملاحظة : سيفيد هذا البرنامج التعليمي أولئك الذين عملوا مع RESTful أو أشكال أخرى من واجهات برمجة التطبيقات في الماضي من جانب العميل ويرغبون في معرفة ما إذا كانت GraphQL تستحق التجربة. هذا يعني أنه كان يجب عليك العمل مع API من قبل ؛ عندها فقط ستتمكن من فهم مدى فائدة GraphQL لك. بينما سنغطي بعض أساسيات GraphQL و Apollo Client ، فإن المعرفة الجيدة بـ JavaScript و React Hooks ستكون مفيدة.
أساسيات GraphQL
هذه المقالة ليست مقدمة كاملة لـ GraphQL ، لكننا سنحدد بعض الاصطلاحات قبل المتابعة.
ما هي GraphQL؟
GraphQL هي مواصفة تصف لغة استعلام تعريفية يمكن لعملائك استخدامها لطلب البيانات الدقيقة التي يريدونها من واجهة برمجة التطبيقات. يتم تحقيق ذلك من خلال إنشاء مخطط نوع قوي لواجهة برمجة التطبيقات (API) الخاصة بك ، مع مرونة مطلقة. كما أنه يضمن أن API يحل البيانات وأن استعلامات العميل يتم التحقق منها مقابل المخطط. يعني هذا التعريف أن GraphQL تحتوي على بعض المواصفات التي تجعلها لغة استعلام تعريفية ، مع واجهة برمجة تطبيقات مكتوبة بشكل ثابت (مبنية حول Typescript) وتجعل من الممكن للعميل الاستفادة من أنظمة النوع هذه ليطلب من واجهة برمجة التطبيقات البيانات الدقيقة التي يريدها .
لذلك ، إذا أنشأنا بعض الأنواع مع بعض الحقول بداخلها ، فعندئذٍ ، من جانب العميل ، يمكننا القول ، "أعطنا هذه البيانات مع هذه الحقول بالضبط". ثم ستستجيب واجهة برمجة التطبيقات بهذا الشكل الدقيق ، تمامًا كما لو كنا نستخدم نظام كتابة بلغة مكتوبة بقوة. يمكنك معرفة المزيد في مقالتي مطبوعة.
دعنا نلقي نظرة على بعض اصطلاحات GraphQl التي ستساعدنا ونحن نواصل.
أساسيات
- عمليات
في GraphQL ، يُطلق على كل إجراء يتم تنفيذه عملية. هناك عدد قليل من العمليات وهي:- استفسار
تختص هذه العملية بجلب البيانات من الخادم. يمكنك أيضًا تسميته إحضار للقراءة فقط. - طفره
تتضمن هذه العملية إنشاء وتحديث وحذف البيانات من الخادم. يطلق عليه شعبيا عملية CUD (إنشاء وتحديث وحذف). - الاشتراكات
تتضمن هذه العملية في GraphQL إرسال البيانات من الخادم إلى عملائها عند وقوع أحداث معينة. وعادة ما يتم تنفيذها مع WebSockets.
- استفسار
في هذه المقالة ، سنتعامل فقط مع عمليات الاستعلام والطفرات.
- أسماء العمليات
هناك أسماء فريدة لعمليات التحول والاستعلام من جانب العميل. - المتغيرات والحجج
يمكن للعمليات تحديد الوسيطات ، وهي تشبه إلى حد كبير دالة في معظم لغات البرمجة. يمكن بعد ذلك تمرير هذه المتغيرات إلى استعلامات أو استدعاءات طفرة داخل العملية كوسائط. من المتوقع أن يتم إعطاء المتغيرات في وقت التشغيل أثناء تنفيذ العملية من العميل الخاص بك. - اسم مستعار
هذا هو اصطلاح في GraphQL من جانب العميل يتضمن إعادة تسمية أسماء الحقول المطولة أو الغامضة بأسماء حقول بسيطة وقابلة للقراءة لواجهة المستخدم. يعد الاسم المستعار ضروريًا في حالات الاستخدام التي لا تريد فيها وجود أسماء حقول متعارضة.

ما المقصود بـ GraphQL من جانب العميل؟
عندما يقوم مهندس الواجهة الأمامية ببناء مكونات واجهة المستخدم باستخدام أي إطار عمل ، مثل Vue.js أو (في حالتنا) React ، يتم نمذجة هذه المكونات وتصميمها من نمط معين على العميل لتناسب البيانات التي سيتم جلبها من الخادم.
واحدة من أكثر المشاكل شيوعًا في RESTful APIs هي الإفراط في الجلب ونقص الجلب. يحدث هذا لأن الطريقة الوحيدة التي يمكن للعميل من خلالها تنزيل البيانات هي ضرب نقاط النهاية التي تُرجع هياكل البيانات الثابتة . الجلب الزائد في هذا السياق يعني أن العميل يقوم بتنزيل معلومات أكثر مما هو مطلوب من قبل التطبيق.
من ناحية أخرى ، في GraphQL ، يمكنك ببساطة إرسال استعلام واحد إلى خادم GraphQL يتضمن البيانات المطلوبة. سيستجيب الخادم بعد ذلك بكائن JSON للبيانات الدقيقة التي طلبتها - وبالتالي ، لا يوجد زيادة في الجلب. يوضح سيباستيان إيشويلر الاختلافات بين واجهات برمجة تطبيقات RESTful و GraphQL.
تعد GraphQL من جانب العميل بنية أساسية من جانب العميل تتفاعل مع بيانات من خادم GraphQL لأداء الوظائف التالية:
- يدير البيانات عن طريق إرسال الاستعلامات وتغيير البيانات دون الحاجة إلى إنشاء طلبات HTTP بنفسك. يمكنك قضاء وقت أقل في بيانات السباكة ومزيد من الوقت في إنشاء التطبيق الفعلي.
- إنه يدير تعقيد ذاكرة التخزين المؤقت نيابة عنك. لذلك ، يمكنك تخزين واسترداد البيانات التي تم جلبها من الخادم ، دون أي تدخل من طرف ثالث ، وتجنب إعادة جلب الموارد المكررة بسهولة. وبالتالي ، فإنه يحدد متى يكون هناك مصدران متماثلان ، وهو أمر رائع بالنسبة لتطبيق معقد.
- إنها تحافظ على توافق واجهة المستخدم الخاصة بك مع Optimistic UI ، وهي اتفاقية تحاكي نتائج الطفرة (أي البيانات التي تم إنشاؤها) وتقوم بتحديث واجهة المستخدم حتى قبل تلقي استجابة من الخادم. بمجرد تلقي الاستجابة من الخادم ، يتم التخلص من النتيجة المتفائلة واستبدالها بالنتيجة الفعلية.
لمزيد من المعلومات حول GraphQL من جانب العميل ، خصص ساعة مع المنشئ المشارك لـ GraphQL وأشخاص رائعين آخرين على راديو GraphQL.
ما هو عميل أبولو؟
Apollo Client هو عميل GraphQL قابل للتشغيل البيني ، ومرن للغاية ، ويدفعه المجتمع من أجل JavaScript والأنظمة الأساسية الأصلية. تشمل ميزاته المثيرة للإعجاب أداة قوية لإدارة الحالة (Apollo Link) ، ونظام تخزين مؤقت صفري التكوين ، ونهج تعريفي لجلب البيانات ، وتقسيم الصفحات سهل التنفيذ ، وواجهة مستخدم Optimistic لتطبيقك من جانب العميل.
لا يخزن Apollo Client الحالة من البيانات التي يتم جلبها من الخادم فحسب ، بل يخزن أيضًا الحالة التي أنشأها محليًا على العميل ؛ ومن ثم ، فإنه يدير الحالة لكل من بيانات واجهة برمجة التطبيقات والبيانات المحلية.
من المهم أيضًا ملاحظة أنه يمكنك استخدام Apollo Client جنبًا إلى جنب مع أدوات إدارة الحالة الأخرى ، مثل Redux ، دون تعارض. بالإضافة إلى ذلك ، من الممكن ترحيل إدارة حالتك من ، على سبيل المثال ، Redux إلى عميل Apollo (وهو ما يتجاوز نطاق هذه المقالة). في النهاية ، الغرض الرئيسي من Apollo Client هو تمكين المهندسين من الاستعلام عن البيانات في واجهة برمجة التطبيقات بسلاسة.
ميزات عميل أبولو
لقد فاز عميل Apollo بالعديد من المهندسين والشركات بسبب ميزاته المفيدة للغاية التي تجعل إنشاء تطبيقات قوية حديثة أمرًا سهلاً. تأتي الميزات التالية مخبوزة:
- التخزين المؤقت
يدعم عميل Apollo التخزين المؤقت على الطاير. - واجهة مستخدم متفائلة
يتمتع عميل Apollo بدعم رائع لواجهة المستخدم المتفائلة. يتضمن عرض الحالة النهائية للعملية (الطفرة) مؤقتًا أثناء تقدم العملية. بمجرد اكتمال العملية ، تحل البيانات الحقيقية محل البيانات المتفائلة. - ترقيم الصفحات
يحتوي Apollo Client على وظائف مدمجة تجعل من السهل جدًا تنفيذ ترقيم الصفحات في تطبيقك. إنه يعتني بمعظم المشاكل التقنية لجلب قائمة البيانات ، إما في تصحيحات أو دفعة واحدة ، باستخدام وظيفةfetchMore
، التي تأتي مع خطافuseQuery
.
في هذه المقالة ، سنلقي نظرة على مجموعة مختارة من هذه الميزات.
كفى من النظرية. شد حزام الأمان واشرب فنجانًا من القهوة مع الفطائر ، حيث تتسخ أيدينا.
بناء تطبيق الويب الخاص بنا
هذا المشروع مستوحى من Scott Moss.
سننشئ تطبيق ويب بسيطًا لمتجر الحيوانات الأليفة ، تتضمن ميزاته ما يلي:
- جلب حيواناتنا الأليفة من جانب الخادم ؛
- إنشاء حيوان أليف (والذي يتضمن إنشاء الاسم ونوع الحيوان والصورة) ؛
- باستخدام واجهة المستخدم المتفائلة ؛
- باستخدام ترقيم الصفحات لتقسيم بياناتنا.
للبدء ، قم باستنساخ المستودع ، مع التأكد من أن فرع starter
هو ما قمت باستنساخه.
ابدء
- قم بتثبيت ملحق Apollo Client Developer Tools لمتصفح Chrome.
- باستخدام واجهة سطر الأوامر (CLI) ، انتقل إلى دليل المستودع المستنسخ ، وقم بتشغيل الأمر للحصول على جميع التبعيات:
npm install
. - قم بتشغيل الأمر
npm run app
لبدء التطبيق. - أثناء وجودك في المجلد الجذر ، قم بتشغيل الأمر
npm run server
. سيبدأ هذا الخادم الخلفي الخاص بنا ، والذي سنستخدمه أثناء المضي قدمًا.
يجب أن يفتح التطبيق في منفذ تم تكوينه. المنجم هو https://localhost:1234/
؛ لك ربما شيء آخر.
إذا كان كل شيء يعمل بشكل جيد ، فيجب أن يبدو تطبيقك كما يلي:

ستلاحظ أنه ليس لدينا حيوانات أليفة لعرضها. هذا لأننا لم ننشئ مثل هذه الوظيفة حتى الآن.
إذا قمت بتثبيت Apollo Client Developer Tools بشكل صحيح ، فافتح أدوات المطور وانقر على أيقونة العلبة. سترى "أبولو" وشيء من هذا القبيل:

مثل أدوات المطورين Redux و React ، سنستخدم Apollo Client Developer Tools لكتابة واختبار استفساراتنا وطفراتنا. يأتي الامتداد مع GraphQL Playground.
جلب الحيوانات الأليفة
دعونا نضيف الوظائف التي تجلب الحيوانات الأليفة. انتقل إلى client/src/client.js
. سنقوم بكتابة عميل Apollo ، وربطه بواجهة برمجة التطبيقات ، وتصديره كعميل افتراضي ، وكتابة استعلام جديد.
انسخ الكود التالي والصقه في client.js
:
import { ApolloClient } from 'apollo-client' import { InMemoryCache } from 'apollo-cache-inmemory' import { HttpLink } from 'apollo-link-http' const link = new HttpLink({ uri: 'https://localhost:4000/' }) const cache = new InMemoryCache() const client = new ApolloClient({ link, cache }) export default client
فيما يلي شرح لما يحدث أعلاه:
-
ApolloClient
ستكون هذه هي الوظيفة التي تغلف تطبيقنا ، وبالتالي واجهات مع HTTP وتخزين البيانات مؤقتًا وتحديث واجهة المستخدم. -
InMemoryCache
هذا هو مخزن البيانات العادي في Apollo Client الذي يساعد في معالجة ذاكرة التخزين المؤقت في تطبيقنا. -
HttpLink
هذه واجهة شبكة قياسية لتعديل تدفق التحكم لطلبات GraphQL وجلب نتائج GraphQL. تعمل كبرنامج وسيط ، حيث تجلب النتائج من خادم GraphQL في كل مرة يتم فيها تشغيل الارتباط. بالإضافة إلى أنه بديل جيد للخيارات الأخرى ، مثلAxios
وwindow.fetch
. - نعلن عن متغير ارتباط تم تعيينه لمثيل
HttpLink
. يأخذ خاصيةuri
وقيمة لخادمنا ، وهيhttps://localhost:4000/
. - التالي هو متغير ذاكرة تخزين مؤقت يحتفظ بالمثيل الجديد لـ
InMemoryCache
. - يأخذ متغير العميل أيضًا مثيلًا لـ
ApolloClient
ويلفlink
cache
. - أخيرًا ، نقوم بتصدير
client
حتى نتمكن من استخدامه عبر التطبيق.
قبل أن نتمكن من رؤية هذا أثناء العمل ، يجب أن نتأكد من أن تطبيقنا بأكمله معرض لـ Apollo وأن تطبيقنا يمكنه تلقي البيانات التي يتم جلبها من الخادم وأنه يمكنه تغيير هذه البيانات.
لتحقيق ذلك ، دعنا ننتقل إلى client/src/index.js
:
import React from 'react' import ReactDOM from 'react-dom' import { BrowserRouter } from 'react-router-dom' import { ApolloProvider } from '@apollo/react-hooks' import App from './components/App' import client from './client' import './index.css' const Root = () => ( <BrowserRouter>
<ApolloProvider client={client}> <App /> </ApolloProvider>
</BrowserRouter> ); ReactDOM.render(<Root />, document.getElementById('app')) if (module.hot) { module.hot.accept() }
كما ستلاحظ في الكود المميز ، فقد قمنا بلف مكون App
في ApolloProvider
بتمرير العميل كعنصر خاص إلى client
. ApolloProvider
مشابه لسياق Context.Provider
. إنه يلف تطبيق React الخاص بك ويضع العميل في السياق ، مما يسمح لك بالوصول إليه من أي مكان في شجرة المكونات الخاصة بك.
لجلب حيواناتنا الأليفة من الخادم ، نحتاج إلى كتابة استعلامات تطلب الحقول المحددة التي نريدها. توجه إلى client/src/pages/Pets.js
، وانسخ الكود التالي والصقه فيه:
import React, {useState} from 'react' import gql from 'graphql-tag' import { useQuery, useMutation } from '@apollo/react-hooks' import PetsList from '../components/PetsList' import NewPetModal from '../components/NewPetModal' import Loader from '../components/Loader'
const GET_PETS = gql` query getPets { pets { id name type img } } `;
export default function Pets () { const [modal, setModal] = useState(false)
const { loading, error, data } = useQuery(GET_PETS); if (loading) return <Loader />; if (error) return <p>An error occured!</p>;
const onSubmit = input => { setModal(false) } if (modal) { return <NewPetModal onSubmit={onSubmit} onCancel={() => setModal(false)} /> } return ( <div className="page pets-page"> <section> <div className="row betwee-xs middle-xs"> <div className="col-xs-10"> <h1>Pets</h1> </div> <div className="col-xs-2"> <button onClick={() => setModal(true)}>new pet</button> </div> </div> </section> <section>
<PetsList pets={data.pets}/>
</section> </div> ) }
من خلال بضع أجزاء من التعليمات البرمجية ، يمكننا جلب الحيوانات الأليفة من الخادم.
ما هو gql؟
من المهم ملاحظة أن العمليات في GraphQL تكون بشكل عام كائنات JSON مكتوبة graphql-tag
و backticks.

علامات gql
هي علامات حرفية لقالب جافا سكريبت تحلل سلاسل استعلام GraphQL في GraphQL AST (شجرة بناء الجملة المجردة).
- عمليات الاستعلام
من أجل جلب حيواناتنا الأليفة من الخادم ، نحتاج إلى إجراء عملية استعلام.- نظرًا لأننا نجري عملية
query
، فقد احتجنا إلى تحديدtype
العملية قبل تسميتها. - اسم استعلامنا هو
GET_PETS
. إنها اصطلاح تسمية لـ GraphQL لاستخدام camelCase لأسماء الحقول. - اسم حقولنا
pets
. ومن ثم ، فإننا نحدد الحقول التي نحتاجها بالضبط من الخادم(id, name, type, img)
. -
useQuery
هو خطاف React وهو الأساس لتنفيذ الاستعلامات في تطبيق Apollo. لإجراء عملية استعلام في مكون React الخاص بنا ، نسمي الخطافuseQuery
، والذي تم استيراده في البداية من@apollo/react-hooks
. بعد ذلك ، نمرر لها سلسلة استعلام GraphQL ، وهيGET_PETS
في حالتنا.
- نظرًا لأننا نجري عملية
- عندما يتم عرض المكون الخاص بنا ، تُرجع
useQuery
استجابة كائن من عميل Apollo تحتوي على خصائص التحميل والخطأ والبيانات. وبالتالي ، يتم تدميرها ، حتى نتمكن من استخدامها لعرض واجهة المستخدم. -
useQuery
رائع. لا يتعين علينا تضمينasync-await
. لقد تم الاهتمام به بالفعل في الخلفية. رائع ، أليس كذلك؟-
loading
تساعدنا هذه الخاصية في التعامل مع حالة تحميل التطبيق. في حالتنا ، نعيد مكونLoader
أثناء تحميل تطبيقنا. افتراضيا ، التحميلfalse
. -
error
فقط في حالة استخدام هذه الخاصية للتعامل مع أي خطأ قد يحدث. -
data
هذا يحتوي على بياناتنا الفعلية من الخادم. - أخيرًا ، في مكون
PetsList
بنا ، نقوم بتمرير دعائمpets
، معdata.pets
كقيمة كائن.
-
في هذه المرحلة ، نجحنا في الاستعلام عن خادمنا.
لبدء تطبيقنا ، لنقم بتشغيل الأمر التالي:
- ابدأ تشغيل تطبيق العميل. قم بتشغيل الأمر
npm run app
في CLI الخاص بك. - ابدأ الخادم. قم بتشغيل الأمر
npm run server
في CLI آخر.

إذا سارت الأمور على ما يرام ، يجب أن ترى هذا:

تحويل البيانات
تغيير البيانات أو إنشاء البيانات في Apollo Client هو نفسه تقريبًا الاستعلام عن البيانات ، مع تغييرات طفيفة جدًا.
لا يزال في client/src/pages/Pets.js
، فلننسخ ولصق الكود المميز:
.... const GET_PETS = gql` query getPets { pets { id name type img } } `;
const NEW_PETS = gql` mutation CreateAPet($newPet: NewPetInput!) { addPet(input: $newPet) { id name type img } } `;
const Pets = () => { const [modal, setModal] = useState(false) const { loading, error, data } = useQuery(GET_PETS);
const [createPet, newPet] = useMutation(NEW_PETS);
const onSubmit = input => { setModal(false)
createPet({ variables: { newPet: input } }); } if (loading || newPet.loading) return <Loader />; if (error || newPet.error) return <p>An error occured</p>;
if (modal) { return <NewPetModal onSubmit={onSubmit} onCancel={() => setModal(false)} /> } return ( <div className="page pets-page"> <section> <div className="row betwee-xs middle-xs"> <div className="col-xs-10"> <h1>Pets</h1> </div> <div className="col-xs-2"> <button onClick={() => setModal(true)}>new pet</button> </div> </div> </section> <section> <PetsList pets={data.pets}/> </section> </div> ) } export default Pets
لإنشاء طفرة ، سنتخذ الخطوات التالية.
1. mutation
للإنشاء أو التحديث أو الحذف ، نحتاج إلى إجراء عملية mutation
. عملية mutation
لها اسم CreateAPet
، مع وسيطة واحدة. تحتوي هذه الوسيطة على متغير $newPet
، مع نوع NewPetInput
. ال !
يعني أن العملية مطلوبة ؛ وبالتالي ، لن تنفذ GraphQL العملية إلا إذا newPet
متغيرًا جديدًا من نوع بيت نوع NewPetInput
.
2. addPet
تأخذ وظيفة addPet
، الموجودة داخل عملية mutation
، وسيطة input
ويتم تعيينها على متغير $newPet
. يجب أن تكون مجموعات الحقول المحددة في وظيفة addPet
بنا مساوية لمجموعات الحقول في استعلامنا. المجموعات الميدانية في عمليتنا هي:
-
id
-
name
-
type
-
img
3. useMutation
خطاف رد الفعل useMutation
هو واجهة برمجة التطبيقات الأساسية لتنفيذ الطفرات في تطبيق Apollo. عندما نحتاج إلى تغيير البيانات ، فإننا نسمي useMutation
في مكون React ونمررها سلسلة GraphQL (في حالتنا ، NEW_PETS
).
عندما يعرض المكون الخاص بنا useMutation
، فإنه يعيد مجموعة (أي ، مجموعة مرتبة من البيانات تشكل سجلا) في مصفوفة تتضمن:
- دالة
mutate
يمكننا استدعاؤها في أي وقت لتنفيذ الطفرة ؛ - كائن مع الحقول التي تمثل الوضع الحالي لتنفيذ الطفرة.
يتم تمرير خطاف useMutation
سلسلة طفرة GraphQL (وهي NEW_PETS
في حالتنا). لقد دمرنا المجموعة ، وهي الوظيفة ( createPet
) التي ستغير البيانات وحقل الكائن ( newPets
).
4. createPet
في دالة onSubmit
الخاصة بنا ، بعد فترة وجيزة من حالة setModal
، حددنا createPet
. تأخذ هذه الوظيفة variable
بخاصية كائن لقيمة معينة إلى { newPet: input }
. يمثل input
حقول الإدخال المختلفة في نموذجنا (مثل الاسم والنوع وما إلى ذلك).
بعد ذلك ، يجب أن تبدو النتيجة كما يلي:

إذا لاحظت صورة GIF عن كثب ، فستلاحظ أن حيواننا الأليف الذي تم إنشاؤه لا يظهر على الفور ، فقط عندما يتم تحديث الصفحة. ومع ذلك ، فقد تم تحديثه على الخادم.
السؤال الكبير هو ، لماذا لا يتم تحديث حيواننا الأليف على الفور؟ دعنا نكتشف في القسم التالي.
التخزين المؤقت في عميل أبولو
السبب وراء عدم تحديث تطبيقنا تلقائيًا هو أن بياناتنا التي تم إنشاؤها حديثًا لا تتطابق مع بيانات ذاكرة التخزين المؤقت في عميل Apollo. لذلك ، هناك تعارض حول ما يحتاج بالضبط إلى تحديث من ذاكرة التخزين المؤقت.
ببساطة ، إذا قمنا بإجراء طفرة تقوم بتحديث أو حذف إدخالات متعددة (عقدة) ، فنحن مسؤولون عن تحديث أي استعلامات تشير إلى تلك العقدة ، بحيث تعدل بياناتنا المخزنة مؤقتًا لتتناسب مع التعديلات التي تجريها الطفرة على ظهرنا- بيانات النهاية .
حفظ ذاكرة التخزين المؤقت متزامنة
هناك عدة طرق للحفاظ على مزامنة ذاكرة التخزين المؤقت في كل مرة نقوم فيها بإجراء عملية طفرة.
الأول هو إعادة جلب استعلامات المطابقة بعد حدوث طفرة ، باستخدام خاصية الكائن refetchQueries
(أبسط طريقة).
ملاحظة: إذا أردنا استخدام هذه الطريقة ، فستأخذ خاصية الكائن في دالة createPet
تسمى refetchQueries
، وستحتوي على مصفوفة من الكائنات ذات قيمة الاستعلام: refetchQueries: [{ query: GET_PETS }]
.
نظرًا لأن تركيزنا في هذا القسم ليس فقط تحديث الحيوانات الأليفة التي أنشأناها في واجهة المستخدم ، ولكن لمعالجة ذاكرة التخزين المؤقت ، فلن نستخدم هذه الطريقة.
الطريقة الثانية هي استخدام وظيفة update
. في Apollo Client ، توجد وظيفة مساعد update
التي تساعد على تعديل بيانات ذاكرة التخزين المؤقت ، بحيث تتزامن مع التعديلات التي تجريها الطفرة على بياناتنا الخلفية. باستخدام هذه الوظيفة ، يمكننا القراءة والكتابة في ذاكرة التخزين المؤقت.
تحديث ذاكرة التخزين المؤقت
انسخ الكود المميز التالي والصقه في client/src/pages/Pets.js
:
...... const Pets = () => { const [modal, setModal] = useState(false) const { loading, error, data } = useQuery(GET_PETS);
const [createPet, newPet] = useMutation(NEW_PETS, { update(cache, { data: { addPet } }) { const data = cache.readQuery({ query: GET_PETS }); cache.writeQuery({ query: GET_PETS, data: { pets: [addPet, ...data.pets] }, }); }, } );
.....
تتلقى وظيفة update
وسيطين:
- الوسيطة الأولى هي ذاكرة التخزين المؤقت من عميل Apollo.
- والثاني هو استجابة الطفرة الدقيقة من الخادم. ندمر خاصية
data
ونضبطها على الطفرة (addPet
).
بعد ذلك ، لتحديث الوظيفة ، نحتاج إلى التحقق من الاستعلام الذي يجب تحديثه (في حالتنا ، استعلام GET_PETS
) وقراءة ذاكرة التخزين المؤقت.
ثانيًا ، نحتاج إلى الكتابة إلى query
الذي تمت قراءته ، حتى يعرف أننا على وشك تحديثه. نقوم بذلك عن طريق تمرير كائن يحتوي على خاصية كائن query
، مع تعيين القيمة لعملية query
( GET_PETS
) ، وخاصية data
تكون قيمتها كائنًا pet
أليفًا وتحتوي على مصفوفة من طفرة addPet
ونسخة من بيانات الحيوانات الأليفة.
إذا اتبعت هذه الخطوات بعناية ، فمن المفترض أن ترى حيواناتك الأليفة يتم تحديثها تلقائيًا أثناء إنشائها. دعنا نلقي نظرة على التغييرات:

واجهة مستخدم متفائلة
كثير من الناس هم من كبار المعجبين باللوادر والغزل. لا حرج في استخدام اللودر ؛ هناك حالات استخدام مثالية يكون فيها اللودر هو الخيار الأفضل. لقد كتبت عن اللوادر مقابل الغزالات وأفضل حالات استخدامها.
تلعب اللوادر والعجلات دورًا مهمًا في تصميم واجهة المستخدم وتجربة المستخدم ، لكن وصول واجهة المستخدم المتفائلة قد سرق الأضواء.
ما هي واجهة المستخدم المتفائلة؟
Optimistic UI هي اتفاقية تحاكي نتائج الطفرة (البيانات التي تم إنشاؤها) وتقوم بتحديث واجهة المستخدم قبل تلقي استجابة من الخادم. بمجرد تلقي الاستجابة من الخادم ، يتم التخلص من النتيجة المتفائلة واستبدالها بالنتيجة الفعلية.
في النهاية ، لا تعد واجهة المستخدم المتفائلة أكثر من طريقة لإدارة الأداء المتصور وتجنب حالات التحميل.
لدى Apollo Client طريقة شيقة جدًا لدمج واجهة المستخدم المتفائلة. يعطينا خطافًا بسيطًا يسمح لنا بالكتابة إلى ذاكرة التخزين المؤقت المحلية بعد حدوث طفرة. دعونا نرى كيف يعمل!
الخطوة 1
توجه إلى client/src/client.js
، وأضف الكود المميز فقط.
import { ApolloClient } from 'apollo-client' import { InMemoryCache } from 'apollo-cache-inmemory' import { HttpLink } from 'apollo-link-http'
import { setContext } from 'apollo-link-context' import { ApolloLink } from 'apollo-link' const http = new HttpLink({ uri: "https://localhost:4000/" }); const delay = setContext( request => new Promise((success, fail) => { setTimeout(() => { success() }, 800) }) ) const link = ApolloLink.from([ delay, http ])
const cache = new InMemoryCache() const client = new ApolloClient({ link, cache }) export default client
تتضمن الخطوة الأولى ما يلي:
- نقوم باستيراد
setContext
منapollo-link-context
. تأخذ وظيفةsetContext
وظيفة رد الاتصال وتعيد الوعد الذي تم تعيينهsetTimeout
على800ms
، من أجل إنشاء تأخير عند تنفيذ عملية الطفرة. - تضمن طريقة
ApolloLink.from
نشاط الشبكة الذي يمثل الرابط (API الخاص بنا) منHTTP
.
الخطوة 2
الخطوة التالية هي استخدام خطاف Optimistic UI. مرر مرة أخرى إلى client/src/pages/Pets.js
، وأضف الرمز المميز أدناه فقط.
..... const Pets = () => { const [modal, setModal] = useState(false) const { loading, error, data } = useQuery(GET_PETS); const [createPet, newPet] = useMutation(NEW_PETS, { update(cache, { data: { addPet } }) { const data = cache.readQuery({ query: GET_PETS }); cache.writeQuery({ query: GET_PETS, data: { pets: [addPet, ...data.pets] }, }); }, } ); const onSubmit = input => { setModal(false) createPet({ variables: { newPet: input },
optimisticResponse: { __typename: 'Mutation', addPet: { __typename: 'Pet', id: Math.floor(Math.random() * 10000 + ''), name: input.name, type: input.type, img: 'https://via.placeholder.com/200' } }
}); } .....
يتم استخدام كائن optimisticResponse
إذا أردنا تحديث واجهة المستخدم على الفور عند إنشاء حيوان أليف ، بدلاً من انتظار استجابة الخادم.
تتضمن مقتطفات التعليمات البرمجية أعلاه ما يلي:
-
__typename
يتم إدخاله بواسطة Apollo في الاستعلام لجلبtype
الكيانات التي تم الاستعلام عنها. يتم استخدام هذه الأنواع بواسطة Apollo Client لبناء خاصيةid
(وهي رمز) لأغراض التخزين المؤقت فيapollo-cache
. لذا ، فإن__typename
خاصية صالحة لاستجابة الاستعلام. - يتم تعيين الطفرة على أنها
__typename
اسم نوع الاستجابةoptimisticResponse
. - تمامًا كما تم تحديده سابقًا ، اسم الطفرة هو
addPet
، و__typename
هو حيوانPet
. - فيما يلي مجالات الطفرة التي نريد تحديث الاستجابة المتفائلة:
-
id
نظرًا لأننا لا نعرف ما هو المعرف من الخادم ، فقد أنشأنا واحدًا باستخدامMath.floor
. -
name
تم تعيين هذه القيمة علىinput.name
. -
type
قيمة النوع هيinput.type
. -
img
الآن ، نظرًا لأن خادمنا يقوم بإنشاء صور لنا ، فقد استخدمنا عنصرًا نائبًا لتقليد صورتنا من الخادم.
-
كانت هذه بالفعل رحلة طويلة. إذا وصلت إلى النهاية ، فلا تتردد في أخذ قسط من الراحة من مقعدك مع فنجان من القهوة.
دعونا نلقي نظرة على نتائجنا. المستودع الداعم لهذا المشروع موجود على GitHub. استنساخ وجربها.

خاتمة
الميزات المذهلة لـ Apollo Client ، مثل Optimistic UI و pagination ، تجعل بناء التطبيقات من جانب العميل حقيقة واقعة.
بينما يعمل Apollo Client بشكل جيد للغاية مع أطر أخرى ، مثل Vue.js و Angular ، فإن مطوري React لديهم Apollo Client Hooks ، وبالتالي لا يمكنهم إلا الاستمتاع ببناء تطبيق رائع.
في هذه المقالة ، خدشنا السطح فقط. يتطلب إتقان عميل Apollo ممارسة مستمرة. لذا ، امض قدمًا وقم باستنساخ المستودع وإضافة ترقيم الصفحات واللعب بالميزات الأخرى التي يوفرها.
يرجى مشاركة ملاحظاتك وتجربتك في قسم التعليقات أدناه. يمكننا أيضًا مناقشة تقدمك على Twitter. هتافات!
مراجع
- "برنامج GraphQL في رد الفعل من جانب العميل" ، سكوت موس ، رئيس الواجهة الأمامية
- "التوثيق" ، عميل أبولو
- "واجهة المستخدم المتفائلة مع التفاعل" ، باتريك أندريهجفسكي
- "الأكاذيب الحقيقية لواجهات المستخدم المتفائلة" ، مجلة Smashing