شعار زيفيرنت

المسار إلى بنية واجهة React قابلة للتطوير والصيانة

التاريخ:

العمارة الأمامية الحديثة - 101

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

التحفيز

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

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

وجهة نظر من أعلى

بصفتي مطور مكدس كامل ، فقد أتيت في الأصل من الجانب الخلفي.
هناك يمكن للمرء أن يجد العديد من الأساليب المعمارية التي يمكننا الاستفادة منها. هناك مفهومان شائعان الاستخدام هما "التنمية الموجهة بالمجال" و "فصل الاهتمامات".
إنها مفيدة جدًا في بنية الواجهة الأمامية أيضًا.
"DDD" هو نهج يحاول فيه المرء إنشاء مجموعات مرتبطة تقنيًا. باستخدام "SoC" ، نفصل بين مسؤوليات وتبعيات المنطق وواجهة المستخدم والبيانات عن بعضها البعض.

0 * hTN6qVaxMhD-Sz2r.webp

قاعدة العمارة الأمامية

عندما يتفاعل المستخدم مع تطبيق الويب الخاص بنا ، يقوم التطبيق بتوجيههم إلى الوحدة الصحيحة. تحتوي كل وحدة على وظائف معينة ومنطق الأعمال. بالإضافة إلى ذلك ، كل وحدة لديها خيار الاتصال بالخلفية باستخدام طبقة التطبيق (http / s) من خلال واجهة برمجة التطبيقات (REST ، GraphQL).

رد فعل إعداد المشروع

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

app/ > config/ > components/ > services/ > modules/ > shared/
content/ > css/ > img/ > fonts/ > js/
cypress/
i18n/
webpack/
.gitignore
package.json
tsconfig.json
Readme.md

يحتوي دليل المحتوى على أصولنا (مثل الصور أو الخطوط أو كود JS الخاص بطرف ثالث). يجب على المرء دائمًا استخدام إطار عمل اختبار (هنا Cypress) ، الموجود في دليله الخاص.
يستخدم Webpack كمجمع أصول. ملفات .json المتبقية هي تكوينات. بالطبع لا نريد أن ننسى ملفنا التمهيدي.

دعنا نتحقق من التفاصيل

مع البنية الأساسية وهيكل المشروع ، لدينا أساس جيد. نحتاج الآن إلى مزيد من التفاصيل لتنفيذ بنية الواجهة الأمامية.
من المفيد دائمًا إنشاء بنية مكونة.
أحب استخدام draw.io لهذا الغرض.
هناك نسمي المكونات والوحدات الفردية.
هذا الجزء مهم للغاية ، لأننا هنا نرى التسلسل الهرمي للمكونات واستخدامها. أحاول بصريًا فصل المكونات والمكونات الخاصة بالوحدة النمطية التي تم استخدامها عدة مرات (متقطع مقابل الخط العادي). من الجيد أيضًا إضافة برامج وسيطة أو وظائف إضافية هنا.

0 * sXZlYGnjFJlXyPqQ.webp

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

app/ > modules/ > shop/ > shop.tsx > shop.logic.ts > shop.reducer.ts

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

//app/modules/shop/shop.tsx
const Shop = (props) => { const { state, actions, reducer } = useShopLogic(props); return ( <div id="shop"> <Header /> {!state?.loading && <Button onClick={actions?.buyNow}> <Translate contentKey="shop.buyNow"/> </Button>} <FeaturedProducts products={reducer?.products} /> <Footer /> </div> );
};
export default Shop;

الآن دعونا نلقي نظرة على مثال على الخطاف. يحتوي هذا على الكود الكامل ذي الصلة بالتفاعل مع واجهة المستخدم ، بالإضافة إلى التواصل مع واجهة برمجة التطبيقات عبر Reducer / Redux.

//app/modules/shop/shop.logic.ts
export const useShopLogic = (props) => { const dispatch = useDispatch(); const history = useHistory(); const [loading, setLoading] = useState(false); const { products } = useSelector(({shop}) => { products: shop?.products; }); const buyNow = () => { history.push("/product/1"); }; const loadProducts = () => { setLoading(true); dispatch(ShopReducer.loadProducts()); }; useEffect(() => { if (ProductService.areValid(products)) setLoading(false); }, [products]); useEffect(() => { loadProducts(); }, []); return { state: { loading }, actions: { buyNow }, reducer: { products }, };
};

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

يتعامل Shop Reducer مع منطق الحالة العالمية من خلال Redux.
توجد وظائف API الخاصة بنا هنا أيضًا ، والتي يمكن استدعاؤها من أي مكان.

//app/modules/shop/shop.reducer.tsexport const ACTION_TYPES = { FETCH_PRODUCTS: "shop/FETCH_PRODUCTS", RESET: "shop/RESET",
};
const initialState = { products: null, loading: false, success: false,
};
export type ShopState = Readonly<typeof initialState>;
export default (state: ShopState = initialState, action): ShopState => { switch (action.type) { case PENDING(ACTION_TYPES.FETCH_PRODUCTS): return { ...state, success: false, loading: true, }; case FAILURE(ACTION_TYPES.FETCH_PRODUCTS): return { ...state, success: false, loading: false, }; case SUCCESS(ACTION_TYPES.FETCH_PRODUCTS): return { ...state, products: action.payload.data, success: true, loading: false, };case ACTION_TYPES.RESET: return { ...initialState, }; default: return state; }
};
export const getProducts = () => async (dispatch) => { const requestUrl = `${SERVER_API_URL}${API_MAP.getProducts}`;const result = await dispatch({ type: ACTION_TYPES.FETCH_PRODUCTS, payload: axios.get < any > requestUrl, });return result;
};
export const reset = () => ({ type: ACTION_TYPES.RESET,
});

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

نبذة عامة

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

بقعة_صورة

أحدث المعلومات الاستخباراتية

بقعة_صورة