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


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

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

كتب بدعم من Mail.ru Cloud Cloud Solutions .

ما هي مكونات الواجهة الأمامية ولماذا هي مطلوبة


علامات HTML - مستوى "صفر" للمكونات بشكل مشروط. كل منهم له وظائفه الخاصة والغرض منه.

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

تسمح لك القواعد المطبقة على فئات CSS ، بالإضافة إلى أي عناصر أخرى ، مثل علامات HTML ، بتعيين القواعد وتغييرها لعرض أي عدد من العناصر من نفس النوع بشكل مركزي. هناك العديد من الأدوات للعمل مع أنماط العناصر - في الواقع CSS و Sass و LESS و PostCSS ومنهجيات تطبيق الأنماط - BEM و SMACSS و Atomic CSS و CSS Modules والمكونات ذات الأنماط.

في الواقع المكونات هي:

  • عناصر من نفس النوع لها نفس الأنماط ونفس التخطيط (HTML) والسلوك (JS) ؛
  • متشابهة في عناصر الأسلوب والسلوك التي تختلف قليلاً عن بعضها البعض.

يتم تطوير تقنية Web Components ، مما يسمح لك بإنشاء علامات HTML مخصصة وتضمين أجزاء من التعليمات البرمجية في التخطيط. ومع ذلك ، أصبحت المكونات مستخدمة على نطاق واسع بفضل أطر تطوير الواجهة الأمامية الحديثة مثل Angular و Vue و React. تجعل ميزات JavaScript من السهل توصيل أحد المكونات:

import {Header, Footer} from "./components/common";
render() {
    return (
       ...
   )
}

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

من الجدير بالذكر ليس فقط كتابة المدونة ، ولكن أيضًا دعمها. يمكن للنسخ / اللصق البسيط من نفس التصميم وعزل الأنماط في فئات CSS إنشاء عرض لبعض الوقت دون أي مخاطر معينة. ولكن إذا تمت إضافة منطق سلوك مكتوب في JS إلى كل عنصر ، فإن فوائد إعادة استخدام الشفرة يتم الشعور بها حرفياً من 2-3 عناصر ، خاصة عندما يتعلق الأمر بدعم وتعديل التعليمات البرمجية المكتوبة مسبقًا.

مكونات التفاعل القابلة لإعادة الاستخدام


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

import React from "react";
import AppHeader from "./components/AppHeader";
import Article from "./components/Article";
import AppFooter from "./components/AppFooter";
export default class App extends React.Component {
    constructor(props) {
        super(props); 
        this.state = {
            title : "My App",
            contacts : "8 800 100 20 30"
           firtsArticleTitle : "Welcome",
           secondArticleTitle : "Let's speak about..."
        }
    };

    render() {
        return (
            <>
                <AppHeader 
                title={this.state.title}
            />
            <Article
                   title={this.state.firstArticleTitle}
               />
               <Article
                   title={this.state.secondArticleTitle}
               />               
               <AppFooter
                   contacts={this.state.contacts}
               />
           </>
       )
   }
}

يرجى ملاحظة : الآن ليس من الضروري استخدام علامة الالتفاف العليا في التخطيط - عادةً ما تكون div. تقدم Modern React أداة Fragment ، وهي عبارة عن سجل مختصر لها <></>. ضمن هذه العلامات ، يمكنك استخدام التسلسل الهرمي للعلامات المسطحة ، كما في المثال أعلاه.

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

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

يتم النظر في الحالة التالية أدناه: اعتمادًا على وجود رد اتصال ، قد يكون المكون الخاص بنا "مستجيبًا" أو ببساطة يظل عرضًا لعرض عنصر على الصفحة:

// App.js
...
render() {
    return (        
        <Article 
            text={this.state.articleText}
            onClick={(e) => this.bindTap(e)}
           customClass={this.state.mainCustomClass}
        />                
    )
}

// Article.js
import React from "react";

export default class Article extends React.Component {
    constructor(props) {
        super(props);         
    };

    render() {
       let cName="default";
       if (this.props.customClass) cName = cName + " " this.props.customClass;
       let bgColor="#fff";
       if (this.props.bgColor) bgColor = this.props.bgColor;
        return (
            {this.props.onClick &&
            <div
                   className={cName}
                onClick={(e) => this.props.onClick(e)}
                   style={{background : bgColor}}
            >
                <p>{this.props.text}<p/>
            </div>
            }
            {!this.props.onClick && 
                <div className={cName}>
                <p>{this.props.text}<p/>
                </div>
           }
        )
    }
} 

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

// App.js
...
render() {
    return (        
        <Article 
            title={this.state.articleTitle}
            text={
               <>
                <p>Please read the article</p>
                <p>Thirst of all, I should say programming React is a very good practice.</p>
               </>
            }
        />                
    )
}

// Article.js
import React from "react";
export default class Article extends React.Component {
    constructor(props) {
        super(props);         
    };

    render() {
        return (
            <div className="article">
            <h2>{this.props.title}</h2>
            {this.props.text}
            </div>
        )
    }
}

سيتم استنساخ التصميم الداخلي للمكون بالكامل كما تم نقله إليه props.

غالبًا ما يكون أكثر ملاءمة نقل تخطيط إضافي إلى مكون المكتبة باستخدام نمط الإدخال والاستخدام this.props.children. يعد هذا الأسلوب أفضل لتعديل المكونات المشتركة المسؤولة عن الكتل النموذجية للتطبيق أو الموقع حيث يُفترض وجود محتوى داخلي مختلف: أحرف استهلالية وأشرطة جانبية وكتل بإعلانات وغيرها.

// App.js
...
render() {
    return (        
        <Article title={this.state.articleTitle}>
           <p>Please read the article</p>
            <p>First of all, I should say programming React is a very good practice.</p>
       </Article>                          
    )
}

// Article.js
import React from "react";
export default class Article extends React.Component {
    constructor(props) {
        super(props);         
    };

    render() {
        return (
            <div className="article">
            <h2>{this.props.title}</h2>
            {this.props.children}
            </div>
        )
    }
} 

مكونات رد الفعل الكاملة


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

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

// Phone.js
import React from "react";
import Validator from "../helpers/Validator";
export default class Phone extends React.Component {
    constructor(props) {
        super(props);   
        this.state = {
            value : this.props.value || "",
            name : this.props.name,
            onceValidated : false,
            isValid : false,
            isWrong : true
        }
        this.ref = React.createRef();    
    };

    componentDidMount = () => {
        this.setValidation();
    };

    setValidation = () => {
        const validationSuccess = (formattedValue) => {
            this.setState({
            value : formattedValue,
            isValid : true,
            isWrong : false,
            onceValidated : true
           });
            this.props.setPhoneValue({
            value : formattedValue, 
            item : this.state.name, 
            isValid : true
            })
        }
        const validationFail = (formattedValue) => {
            this.setState({
            value : formattedValue,
            isValid : false,
            isWrong : true,
            });
            this.props.setPhoneValue({
            value : formattedValue, 
            item : this.state.name, 
            isValid : false
            })
        }
        new Validator({
            element : this.ref.current,
            callbacks : {
            success : validationSuccess,
            fail : validationFail
            }
        });
    }

    render() {
        return (
            <div className="form-group">
            <labeL htmlFor={this.props.name}>
                    <input 
                name={this.props.name}
                id={this.props.name}
                type="tel"
                placeholder={this.props.placeholder}
                defaultValue={this.state.value}
                ref={this.ref}
                />
            </label>
            </div>
        )
    }

} 

يحتوي هذا المكون بالفعل على حالة حالة داخلية ، يمكن مشاركة جزء منها مع الكود الخارجي الذي يطلق عليه. يبقى الجزء الآخر داخل المكون ، في المثال أعلاه onceValidated. وبالتالي ، يتم تضمين جزء من منطق المكون فيه بالكامل.

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

مكونات MV


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

  • "ذكي" للعمل مع البيانات ( Model) ؛
  • "Dumb" لعرض ( View).

سيستمر الاتصال عن طريق استدعاء مكون واحد. الآن سيكون Model. - الجزء الثاني Viewسوف يطلق في - render()مع propsبعضها جاء من التطبيق، والجزء الآخر هو بالفعل حالة المكون نفسه:

// App.js
...
render() {
    return (        
        <Phone 
            name={this.state.mobilePhoneName}
            placeholder={"You mobile phone"}
        />                
    )
}

// Phone.js
import React from "react";
import Validator from "../helpers/Validator";
import PhoneView from "./PhoneView";
export default class Phone extends React.Component {
    constructor(props) {
        super(props);   
        this.state = {
            value : this.props.value || "",
            name : this.props.name,
            onceValidated : false,
            isValid : false,
            isWrong : true
        }
        this.ref = React.createRef();    
    };

    componentDidMount = () => {
        this.setValidation();
    };

    setValidation = () => {
        const validationSuccess = (formattedValue) => {
            ...
        }
        const validationFail = (formattedValue) => {
            ...
        }
        new Validator({
           element : this.ref.current,
            ...
        });
    }    

   render() {
        return (
            <PhoneView
                name={this.props.name}
            placeholder={this.props.placeholder}
               value={this.state.value}
               ref={this.ref}
            />
        )
    }
}

// PhoneView.js
import React from "react";
const PhoneView = React.forwardRef((props, ref) => (   
    <div className="form-group">
        <labeL htmlFor={props.name}>
            <input 
                name={props.name}
            id={props.name}
            type="tel"
            ref={ref}
            placeholder={props.placeholder}
            value={props.value}                
            />
        </label>
    </div>    
));
export default PhoneView;

يجدر الانتباه إلى الأداة React.forwardRef(). يسمح لك بإنشاء refعنصر Phone، ولكن ربطه مباشرة بعناصر التخطيط في PhoneView. كل التلاعبات كالعادة refستكون متاحة في Phone. على سبيل المثال ، إذا كنا بحاجة إلى توصيل أداة التحقق من رقم الهاتف.

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

الآن ينقسم مكون قابلة لإعادة الاستخدام لدينا في Modelو View، يمكننا وضع على حدة منطق الأعمال ورمز التخطيط. يمكننا أيضًا تجميع التخطيط من عناصر المكونات الأصغر.

حالة التطبيق بأكمله الذي يعمل على المكونات


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

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

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

مع تداخل كبير للمكونات ، من الضروري مراقبة تنظيم العمل باستخدام البيانات من أجل معرفة مكان "مصدر الحقيقة" دائمًا. لقد كتبت بالفعل

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



createContextReact getChildContext

ثم لا يتعين عليك تمرير الدعائم عبر "سمك" المكونات أسفل شجرة تداخل المكونات. أو استخدم مكتبات متخصصة لإدارة البيانات وتغيير التسليم ، مثل Redux و Mobx (انظر المقالة في حزمة Mobx + React ).

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

بعض المكتبات الجاهزة لمكونات التفاعل


هناك مكتبات شعبية للمكونات الجاهزة ، والتي ، كقاعدة عامة ، كانت في الأصل مشاريع داخلية للشركات:

  1. Material-UI — , Material Design Google.
  2. React-Bootstrap — , . : API , , .
  3. VKUI — «». VK mini apps, (. VK mini apps). VKUI «». «» . vkconnect — iOS Android.
  4. ARUI Feather — React- -. , . open source, .

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

تمت كتابة هذه المقالة بدعم من Mail.ru Cloud Solutions .


All Articles