توحيد المكونات البصرية. الجزء 1. الأنماط



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

كلمات


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

المصطلحات المستخدمة:

  • المكون المرئي هو مكون يقوم بإرجاع عنصر مضمن في DOM. على سبيل المثال ، قم return (<div />) . لا يجب تفسير المكون الذي يقوم بإرجاع مكون آخر فقط بصريًا.

مقدمة


عندما تقوم بتطوير مكون ، لا يمكنك جعله عالميًا بالكامل. في رأسك ، تبدأ دائمًا من خيارات محددة لاستخدامه. غالبًا ما يتبين أنه بعد التطوير ، يبدأ زملاؤك في "دفع هذا المكون إلى أي مكان". أنت غاضب منهم: "حسنًا ، لم أطوره من أجل هذا! ليس المقصود لهذه المهمة! " بالطبع ، التحسينات لا مفر منها ، بل ضرورية. ولكن هذا لا ينبغي أن يكون تحسينات مثل رمي الدعائم الجديدة لزيادة المسافة البادئة من 4px إلى 8px ، والتي سيتم استخدامها في واحدة أو حالتين من خمسين. يجب أن تحتوي المكونات على هندسة خارجية مخصصة.

TypeScript ، مساعدة


النظر في الواجهة ، والتي ينبغي أن يكون موجودا في المعنى ، على سبيل المثال ، في src/Library/Controls.ts . يتم تقديم تعليقات مختصرة للحقول ، أدناه سنقوم بتحليلها بمزيد من التفاصيل.

 export interface VisualComponentProps { //  .   - children?: React.ReactNode; //    css className?: string; //   ,     . doNotRender?: boolean; //     doNotRender true fallback?: JSX.Element; //    css style?: React.CSSProperties; } 

هذه هي واجهة الدعائم المكون. أي منها؟ جميع المكونات البصرية. ينبغي تطبيقها على عنصر الجذر.

يجب توسيع الدعائم لكل مكون مرئي مطور باستخدام هذه الواجهة.

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

  • children في مكونات فئة React.Component ، مكونات مكون React.FC ، لكنها ليست في وظائف عادية دون تحديد كتابة React.FC. لذلك ، نسأله.
  • className/style نستخدم أسماء مشابهة ، كما في JSX'nom المعتاد <div />. نحن لا ننتج دلالات. يستخدم مبدأ هوية الاسم ، على سبيل المثال ، في الدعائم لتحديد رابط المرجع .
  • يستخدم doNotRender المؤلم في عرض JSX حسب الحالة . عند تطبيق هذا الحل ، لا نحتاج إلى وضع الأقواس في طرق التجسيد ، مما يضعف إمكانية قراءة التعليمات البرمجية. مقارنة مقتطفين من الكود:

    تقديم العذراء الشرطي:

    App.tsx

     renderComponent() { const {props, state} = this; const needRender = state.something; return ( <PageLayout> <UIButton children={'This is a button'} /> {needRender && <UIButton children={'This is another button'} /> } </PageLayout> ); } 

    الدعائم تشاد doNotRender:

    App.tsx

     renderComponent() { const {props, state} = this; const needRender = state.something; return ( <PageLayout> <UIButton children={'This is a button'} /> <UIButton children={'This is another button'} doNotRender={!needRender} /> </PageLayout> ); } 

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

    في الخيار الثاني ، لدينا تداخل متشابه ، ناقصًا أن doNotRender قد لا يلفت الأنظار ولن يفهم المطور ما يحدث. ولكن إذا كان كل مكون مرئي في مشروعك مصنوعًا وفقًا لهذا المبدأ ، فستتوقف هذه المشكلة فورًا.
  • هناك حاجة إلى fallback إذا كنا لا نريد أن نجعل null مع doNotRender true ، ولكن نوعا من عنصر مخصص. يتم استخدامه عن طريق القياس مع React Suspense ، لأنه له معنى مماثل (لا ننتج دلالات)

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

ملاحظة: في الكود أدناه ، أستخدم أيضًا css-modules ، sass و classnames.

UIButton.tsx

 import * as React from 'react'; import { VisualComponentProps } from 'Library/Controls'; import * as css from './Button.sass'; import cn from 'classnames'; //  ()  export interface ButtonBasicProps { disabled?: boolean; } export interface ButtonProps extends ButtonBasicProps, VisualComponentProps {} export function UIButton(props: ButtonProps) { //   undefined,     // "Nothing was returned from render." if (props.doNotRender) return props.fallback || null; //      const rootClassNames = cn( // ,   sass css.Button, // ,     props props.className, //      props.disabled && css._disabled ); return ( <div children={props.children} className={rootClassNames} style={props.style} /> ) } 

App.tsx

 renderComponent() { const {props, state} = this; const needRenderSecond = true; return ( <PageLayout> <UIButton children={'This is a button'} style={{marginRight: needRenderSecond ? 5 : null}} /> <UIButton disabled children={'This is another button'} doNotRender={!needRenderSecond} /> </PageLayout> ); } 

النتيجة:



التفكير والاستنتاج


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

يمكن القول أنه نظرًا لعدم وجود أزرار صفراء مشروطة في نظام التصميم ، ويحتاج المطور إلى صنعها ، فإن المشكلة ليست في المكونات ، ولكن في حقيقة أن هذه الحاجة تخلقها. ومع ذلك ، فإن الواقع هو أن مثل هذه الحالات تنشأ ، وكثيرا ما. "... ولكن يجب أن نعيش! يجب أن نعيش". بالإضافة إلى ذلك ، لا يمكن تطبيق مبدأ css cascade دائمًا في الممارسة ، وقد تواجه حالات تتداخل فيها أنماطك ببساطة مع خصوصية أعلى لمحدِّد آخر (أو الموصوف أعلاه). هنا أسلوب يساعد فقط خارج.

أخيرًا ، سأضيف لحظات (حرفيًا).

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

    باستخدام مثال UIButton: أعد تسمية UIButton إلى UIButtonInner وأضف الكود التالي تحته:

    UIButton.tsx

     export function UIButton(props: ButtonProps) { if (props.doNotRender) return props.fallback || null; return <UIButtonInner {...props} />; } 

    ملاحظة: لا تجعل UIButton خطأ مكالمة متكررة في هذه الوظيفة!
  2. في حالات نادرة عندما يمكن أن تتغير الأنماط الموجودة على برنامج التضمين والمكون الملتف بشكل مستقل ، قد تكون الواجهة التالية مفيدة لك

    Library/Controls.ts

     export interface VisualComponentWrapperProps extends VisualComponentProps { wrappedVisual?: VisualComponentProps; } 

    واستخدامه
    UIButton.tsx

     interface ButtonSomeWrapperProps extends ButtonBasicProps, VisualComponentWrapperProps { myCustomProp?: number; } export function UIButtonSomeWrapper(props: ButtonSomeWrapperProps) { if (props.doNotRender) return props.fallback || null; const { //  VisualComponentProps  style, className, children, fallback, doNotRender, // VisualComponentProps   wrappedVisual, //    myCustomProp, //     ...uiButtonProps } = props; return ( <div style={style} className={className} > {myCustomProp} <UIButton {...wrappedVisual} {...uiButtonProps} /> {children} </div> ); } 

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

Source: https://habr.com/ru/post/ar479714/


All Articles