توسيع تطبيق Redux مع البط

تحسبًا لبدء الدورة أعد "React.js developer" ترجمة للمواد المفيدة.





كيف الواجهة الأمامية لمقياس التطبيق الخاص بك؟ كيف تجعل التعليمات البرمجية الخاصة بك تدعم بعد ستة أشهر؟

في عام 2015 ، اقتحمت Redux عالم تطوير الواجهة الأمامية وأسست نفسها كمعيار يتجاوز React.

انتهت الشركة التي أعمل بها مؤخرًا من إعادة هيكلة قاعدة كبيرة من الرموز على React ، حيث نفذنا عملية redux بدلاً من الارتجاع .

كان علينا أن نتخذ هذه الخطوة لأن المضي قدمًا لم يكن ممكنًا بدون تطبيق منظم جيدًا ومجموعة واضحة من القواعد.

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

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

دعونا نلقي نظرة على كيفية اعتيادنا على تنظيم الكود.

الوظيفة مقابل الميزة


هناك نهجان مقبولان بشكل عام لتنظيم التطبيقات: الوظيفة أولاً والميزة الأولى.
في لقطة الشاشة على اليسار ، يتم تنظيم بنية المجلد وفقًا لمبدأ الوظيفة أولاً ، وعلى اليمين - الميزة الأولى.



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

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

مشكلة أخرى هي دمج المجلدات. من المحتمل أن يتطلب أحد سلاسل عمليات التطبيق الوصول إلى الملفات من كافة المجلدات.

واحدة من مزايا هذا النهج هو أنه يمكن عزل ، في حالتنا ، رد فعل من Redux. لذلك ، إذا كنت ترغب في تغيير مكتبة إدارة الدولة ، فستعرف المجلدات التي ستحتاج إليها. إذا كنت بحاجة إلى تغيير مكتبة العرض ، يمكنك ترك المجلدات مع إعادة الاختزال سليمة.

الميزة الأولى تعني أنه سيتم تسمية أدلة المستوى الأعلى وفقًا للوظيفة الرئيسية للتطبيق: المنتج ، عربة التسوق ، الجلسة.

هذا النهج يتطور بشكل أفضل ، حيث أن كل ميزة جديدة تكمن في مجلد جديد. ومع ذلك ، ليس لديك فصل بين مكونات Redux و React. تغيير أحدها على المدى الطويل ليس بالمهمة السهلة.

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

الجمع بين أفضل عالمين


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

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

في قلب منهجنا هو مبدأ العزلة رمز التفاعل في مجلد يسمى طرق العرض ، وإعادة رمز التعليمات البرمجية في مجلد آخر يسمى redux.

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

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

لكي لا تصاب بالجنون بشأن عدد الملفات في مجلد ، يمكنك استخدام نهج الميزة الأولى داخل هذه المجلدات.

وفي الوقت نفسه ، في مجلد redux ...

إدخال إعادة البط


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

يعمل البط النموذجي المعياري الأصلي على تسهيل العمل مع redux ويقدم طريقة منظمة لإضافة وظائف جديدة إلى تطبيقك.

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

لذا ظهرت البط . كان الحل تقسيم الوظيفة إلى مجلدات بطة.

duck/
├── actions.js
├── index.js
├── operations.js
├── reducers.js
├── selectors.js
├── tests.js
├── types.js
├── utils.js

يجب أن يكون مجلد البطة:

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

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

إذا كنت تريد التأكد من أن التجريد سيئ ، فقم بمشاهدة فيديو Cheng Lou هذا .

دعونا نلقي نظرة على محتويات كل ملف.

أنواع


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

const QUACK = "app/duck/QUACK";
const SWIM = "app/duck/SWIM";

export default {
    QUACK,
    SWIM
};

أجراءات


يحتوي هذا الملف على جميع وظائف منشئ الإجراء .

import types from "./types";

const quack = ( ) => ( {
    type: types.QUACK
} );

const swim = ( distance ) => ( {
    type: types.SWIM,
    payload: {
        distance
    }
} );

export default {
    swim,
    quack
};

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

عمليات


لتمثيل سلسلة العمليات ( العمليات ) ، ستحتاج إلى الوسيطة الوسيطة redux لتحسين وظيفة الإرسال . الأمثلة الشائعة هي redux-thunk أو redux-saga أو redux-ملحوظ .

في حالتنا ، يتم استخدام redux-thunk . نحن بحاجة إلى فصل thunks من منشئي الحركة ، حتى على حساب كتابة كود إضافي. لذلك ، سنحدد العملية على أنها التفاف على الإجراءات .

إذا أرسلت العملية إجراء واحدًا فقط ، أي أنها لا تستخدم فعلاً redux-thunk ، فإننا نرسل وظيفة منشئ الإجراء. إذا كانت العملية تستخدم thunk ، فيمكنها إرسال الكثير من الإجراءات وربطها باستخدام الوعود .

import actions from "./actions";

// This is a link to an action defined in actions.js.
const simpleQuack = actions.quack;

// This is a thunk which dispatches multiple actions from actions.js
const complexQuack = ( distance ) => ( dispatch ) => {
    dispatch( actions.quack( ) ).then( ( ) => {
        dispatch( actions.swim( distance ) );
        dispatch( /* any action */ );
    } );
}

export default {
    simpleQuack,
    complexQuack
};

ندعو لهم العمليات، thunks ، الملاحم، الملاحم، كما تريد. ما عليك سوى تحديد مبادئ التسمية والالتزام بها.

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

المخفضات


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

import { combineReducers } from "redux";
import types from "./types";

/* State Shape
{
    quacking: bool,
    distance: number
}
*/

const quackReducer = ( state = false, action ) => {
    switch( action.type ) {
        case types.QUACK: return true;
        /* ... */
        default: return state;
    }
}

const distanceReducer = ( state = 0, action ) => {
    switch( action.type ) {
        case types.SWIM: return state + action.payload.distance;
        /* ... */
        default: return state;
    }
}

const reducer = combineReducers( {
    quacking: quackReducer,
    distance: distanceReducer
} );

export default reducer;

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

راجع كاملة سبيل المثال مشروع ومعرفة كيفية استخدام combineReducers بشكل صحيح ، خصوصا في الملفات reducers.jsو store.jsأين نحن بناء شجرة الدولة.

المحددات


جنبا إلى جنب مع محدد العمليات ( محدد ) هي جزء من بطة الواجهة العامة. يشبه الفرق بين العمليات والمحددات نمط CQRS .

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

function checkIfDuckIsInRange( duck ) {
    return duck.distance > 1000;
}

export default {
    checkIfDuckIsInRange
};

فهرس


يشير هذا الملف إلى ما سيتم تصديره من مجلد البطة.
هل هو:

  • يصدر وظيفة المخفض من البط افتراضيًا.
  • تصدير محددات وعمليات كصادرات مسجلة.
  • أنواع الصادرات إذا لزم الأمر في البط الأخرى.

import reducer from "./reducers";

export { default as duckSelectors } from "./selectors";
export { default as duckOperations } from "./operations";
export { default as duckTypes } from "./types";

export default reducer;

الاختبارات


ميزة استخدام Redux مع إطار البط هو أنه يمكنك كتابة الاختبارات مباشرة بعد الرمز الذي تريد اختباره.

اختبار الكود الخاص بك على Redux سهل جدًا:

import expect from "expect.js";
import reducer from "./reducers";
import actions from "./actions";

describe( "duck reducer", function( ) {
    describe( "quack", function( ) {
        const quack = actions.quack( );
        const initialState = false;

        const result = reducer( initialState, quack );

        it( "should quack", function( ) {
            expect( result ).to.be( true ) ;
        } );
    } );
} );

داخل هذا الملف ، يمكنك كتابة اختبارات للمخفضات والعمليات والمحددات وما إلى ذلك.
يمكنني كتابة مقالة منفصلة كاملة حول فوائد اختبار الكود ، لكنها كافية بالفعل ، لذا فقط اختبر الكود الخاص بك!

هذا كل شئ


الخبر السار حول إعادة البط هو أنه يمكنك استخدام نفس القالب لكل كود redux الخاص بك.

و feature- نهج التقسيم على أساس لرمز مسترجع الخاص بك يساعد على بقاء طلبك مرنة وقابلة للتطوير لأنها تنمو. سوف يعمل نهج الفصل القائم على الوظيفة بشكل جيد عند بناء مكونات صغيرة مشتركة لأجزاء مختلفة من التطبيق.

يمكنك إلقاء نظرة على قاعدة رمز مثال رد الفعل الكامل للتفاعل هنا . ضع في اعتبارك أن المستودع يعمل بنشاط.

كيف تنظم تطبيقات redux الخاصة بك؟ أتطلع إلى التعليقات على النهج الموصوف.

نراكم على بالطبع .

All Articles