مكوّنات نمطية: مكتبة CSS-in-JS للويب الحديث
نشرت: 2022-03-11تم تصميم CSS للمستندات ، وهو ما كان من المتوقع أن تحتويه "شبكة الويب القديمة". يظهر ظهور المعالجات الأولية مثل Sass أو Less أن المجتمع يحتاج إلى أكثر مما يقدمه CSS. مع تزايد تعقيد تطبيقات الويب بمرور الوقت ، أصبحت قيود CSS مرئية بشكل متزايد ويصعب التخفيف منها.
تستفيد مكوّنات الأنماط من قوة لغة برمجة كاملة - جافا سكريبت - وقدراتها في تحديد النطاق للمساعدة في هيكلة الكود في مكونات. يساعد هذا في تجنب المزالق الشائعة لكتابة CSS والحفاظ عليها للمشاريع الكبيرة. يمكن للمطور أن يصف نمط المكون دون التعرض لخطر الآثار الجانبية.
ما هي المشكلة؟
تتمثل إحدى ميزات استخدام CSS في أن النمط منفصل تمامًا عن الكود. هذا يعني أنه يمكن للمطورين والمصممين العمل بالتوازي دون التدخل مع بعضهم البعض.
من ناحية أخرى ، تجعل المكونات المصممة من السهل الوقوع في فخ أسلوب ومنطق الاقتران القوي. يشرح ماكس ستويبر كيفية تجنب ذلك. في حين أن فكرة فصل المنطق عن العرض ليست جديدة بالتأكيد ، فقد يميل المرء إلى اتباع طرق مختصرة عند تطوير مكونات React. على سبيل المثال ، من السهل إنشاء مكون لزر تحقق من الصحة يتعامل مع إجراء النقر بالإضافة إلى نمط الزر. يتطلب الأمر مزيدًا من الجهد لتقسيمه إلى مكونين.
الحاوية / العمارة التقديمية
هذا مبدأ بسيط جدا. تحدد المكونات إما كيف تبدو الأشياء ، أو أنها تدير البيانات والمنطق. أحد الجوانب المهمة جدًا لمكونات العرض هو أنه لا ينبغي أبدًا أن يكون لها أي تبعيات. يتلقون الدعائم ويقدمون DOM (أو الأطفال) وفقًا لذلك. من ناحية أخرى ، تعرف الحاويات بنية البيانات (الحالة ، والإحياء ، والتدفق ، وما إلى ذلك) ولكن لا ينبغي أبدًا أن تكون مسؤولة عن العرض. تعتبر مقالة دان أبراموف شرحًا جيدًا ومفصلاً لهذه الهندسة المعمارية.
تذكر SMACSS
على الرغم من أن Scalable and Modular Architecture لـ CSS هي دليل أسلوب لتنظيم CSS ، فإن المفهوم الأساسي هو المفهوم الذي يتم اتباعه ، في معظم الأحيان تلقائيًا ، بواسطة المكونات المصممة. الفكرة هي فصل CSS إلى خمس فئات:
- قاعدة تحتوي على جميع القواعد العامة.
- الغرض من التخطيط هو تحديد الخصائص الهيكلية بالإضافة إلى أقسام مختلفة من المحتوى (على سبيل المثال ، رأس ، تذييل ، شريط جانبي ، محتوى).
- تحتوي الوحدة النمطية على فئات فرعية للكتل المنطقية المختلفة لواجهة المستخدم.
- تحدد الحالة فئات معدل للإشارة إلى حالات العناصر ، على سبيل المثال الحقل في الخطأ ، زر تعطيل.
- يحتوي المظهر على اللون والخط والجوانب التجميلية الأخرى التي قد تكون قابلة للتعديل أو تعتمد على تفضيلات المستخدم.
من السهل الحفاظ على هذا الفصل أثناء استخدام المكونات المصممة . تتضمن المشاريع عادةً نوعًا من تطبيع CSS أو إعادة تعيينه. يقع هذا عادةً في فئة القاعدة . يمكنك أيضًا تحديد حجم الخط العام ، وتحجيم الخط ، وما إلى ذلك. يمكن القيام بذلك من خلال CSS العادي (أو Sass / Less) ، أو من خلال وظيفة injectGlobal
التي توفرها المكونات المصممة .
بالنسبة لقواعد التخطيط ، إذا كنت تستخدم إطار عمل لواجهة المستخدم ، فمن المحتمل أن تحدد فئات الحاوية ، أو نظام الشبكة. يمكنك بسهولة استخدام هذه الفئات جنبًا إلى جنب مع القواعد الخاصة بك في مكونات التخطيط التي تكتبها.
الوحدة النمطية متبوعة تلقائيًا ببنية المكونات المصممة ، حيث يتم إرفاق الأنماط بالمكونات مباشرة ، بدلاً من وصفها في الملفات الخارجية. بشكل أساسي ، سيكون كل مكون تكتبه وحدة نمطية خاصة به. يمكنك كتابة رمز التصميم الخاص بك دون القلق بشأن الآثار الجانبية.
الدولة ستكون قواعد تحددها داخل مكوناتك كقواعد متغيرة. يمكنك ببساطة تحديد وظيفة لاستيفاء قيم سمات CSS الخاصة بك. إذا كنت تستخدم إطار عمل UI ، فقد يكون لديك فئات مفيدة لإضافتها إلى مكوناتك أيضًا. من المحتمل أن يكون لديك أيضًا قواعد CSS pseudo-selector (التمرير والتركيز وما إلى ذلك)
يمكن ببساطة استيفاء السمة داخل مكوناتك. إنها لفكرة جيدة أن تحدد موضوعك كمجموعة من المتغيرات لاستخدامها في التطبيق الخاص بك. يمكنك حتى اشتقاق الألوان برمجيًا (باستخدام مكتبة ، أو يدويًا) ، على سبيل المثال للتعامل مع التباينات والإبرازات. تذكر أن لديك القوة الكاملة للغة البرمجة تحت تصرفك!
اجمعهم معًا للتوصل إلى حل
من المهم الاحتفاظ بها معًا ، لتجربة تنقل أسهل ؛ لا نريد تنظيمها حسب النوع (العرض التقديمي مقابل المنطق) ولكن بالأحرى حسب الوظيفة.
وبالتالي ، سيكون لدينا مجلد لجميع المكونات العامة (الأزرار وما شابه). يجب تنظيم الآخرين اعتمادًا على المشروع ووظائفه. على سبيل المثال ، إذا كانت لدينا ميزات إدارة المستخدم ، فيجب علينا تجميع جميع المكونات الخاصة بهذه الميزة.
لتطبيق بنية الحاوية / العرض التقديمي للمكونات المصممة على نهج SMACSS ، نحتاج إلى نوع إضافي من المكونات: هيكلي. ننتهي بثلاثة أنواع من المكونات ؛ على غرار الهيكلية والحاويات. نظرًا لأن المكونات النمطية تزين علامة (أو مكونًا) ، فنحن بحاجة إلى هذا النوع الثالث من المكونات لهيكل DOM. في بعض الحالات ، قد يكون من الممكن السماح لمكون الحاوية بمعالجة بنية المكونات الفرعية ، ولكن عندما تصبح بنية DOM معقدة وتكون مطلوبة للأغراض المرئية ، فمن الأفضل فصلها. وخير مثال على ذلك هو الجدول ، حيث يكون DOM عادةً مطولًا تمامًا.
مشروع مثال
لنقم ببناء تطبيق صغير يعرض وصفات لتوضيح هذه المبادئ. يمكننا البدء في بناء مكون وصفات. سيكون المكون الرئيسي وحدة تحكم. سوف يتعامل مع الحالة - في هذه الحالة ، قائمة الوصفات. سيستدعي أيضًا وظيفة API لجلب البيانات.

class Recipes extends Component{ constructor (props) { super(props); this.state = { recipes: [] }; } componentDidMount () { this.loadData() } loadData () { getRecipes().then(recipes => { this.setState({recipes}) }) } render() { let {recipes} = this.state return ( <RecipesContainer recipes={recipes} /> ) } }
سيعرض قائمة الوصفات ، لكنه لا يحتاج (ولا ينبغي) أن يعرف كيف. لذلك نعرض مكونًا آخر يحصل على قائمة الوصفات والمخرجات DOM:
class RecipesContainer extends Component{ render() { let {recipes} = this.props return ( <TilesContainer> {recipes.map(recipe => (<Recipe key={recipe.id} {...recipe}/>))} </TilesContainer> ) } }
هنا ، في الواقع ، نريد عمل شبكة مربعة. قد يكون من الجيد جعل تخطيط التجانب الفعلي مكونًا عامًا. لذلك إذا استخرجنا ذلك ، فسنحصل على مكون جديد يشبه هذا:
class TilesContainer extends Component { render () { let {children} = this.props return ( <Tiles> { React.Children.map(children, (child, i) => ( <Tile key={i}> {child} </Tile> )) } </Tiles> ) } }
TilesStyles.js:
export const Tiles = styled.div` padding: 20px 10px; display: flex; flex-direction: row; flex-wrap: wrap; ` export const Tile = styled.div` flex: 1 1 auto; ... display: flex; & > div { flex: 1 0 auto; } `
لاحظ أن هذا المكون عرضي بحت. إنه يحدد أسلوبه ويلف أي أطفال يتلقاهم داخل عنصر DOM مصمّم آخر يحدد شكل البلاط. إنه مثال جيد على الشكل الذي ستبدو عليه مكونات العرض التقديمي العامة من الناحية المعمارية.
بعد ذلك ، نحتاج إلى تحديد شكل الوصفة. نحتاج إلى مكون حاوية لوصف DOM المعقد نسبيًا وكذلك تحديد النمط عند الضرورة. ننتهي بهذا:
class RecipeContainer extends Component { onChangeServings (e) { let {changeServings} = this.props changeServings(e.target.value) } render () { let {title, ingredients, instructions, time, servings} = this.props return ( <Recipe> <Title>{title}</Title> <div>{time}</div> <div>Serving <input type="number" min="1" max="1000" value={servings} onChange={this.onChangeServings.bind(this)}/> </div> <Ingredients> {ingredients.map((ingredient, i) => ( <Ingredient key={i} servings={servings}> <span className="name">{ingredient.name}</span> <span className="quantity">{ingredient.quantity * servings} {ingredient.unit}</span> </Ingredient> ))} </Ingredients> <div> {instructions.map((instruction, i) => (<p key={i}>{instruction}</p>))} </div> </Recipe> ) } }
لاحظ هنا أن الحاوية تقوم ببعض عمليات إنشاء DOM ، لكنها المنطق الوحيد الذي تحتويه. تذكر أنه يمكنك تحديد الأنماط المتداخلة ، لذلك لا تحتاج إلى إنشاء عنصر ذي نمط لكل علامة تتطلب التصميم. هذا ما نفعله هنا لاسم وكمية عنصر المكون. بالطبع ، يمكننا تقسيمه أكثر وإنشاء مكون جديد لمكون. الأمر متروك لك - اعتمادًا على مدى تعقيد المشروع - لتحديد مستوى الدقة. في هذه الحالة ، هو مجرد مكون مصمم معرّف مع الباقي في ملف RecipeStyles:
export const Recipe = styled.div` background-color: ${theme('colors.background-highlight')}; `; export const Title = styled.div` font-weight: bold; ` export const Ingredients = styled.ul` margin: 5px 0; ` export const Ingredient = styled.li` & .name { ... } & .quantity { ... } `
لغرض هذا التمرين ، استخدمت ThemeProvider. إنه يضخ السمة في دعائم المكونات المصممة. يمكنك ببساطة استخدامه color: ${props => props.theme.core_color}
، أنا فقط أستخدم غلافًا صغيرًا للحماية من السمات المفقودة في السمة:
const theme = (key) => (prop) => _.get(prop.theme, key) || console.warn('missing key', key)
يمكنك أيضًا تحديد الثوابت الخاصة بك في وحدة نمطية واستخدامها بدلاً من ذلك. على سبيل المثال: color: ${styleConstants.core_color}
الايجابيات
ميزة استخدام المكونات المصممة هي أنه يمكنك استخدامها بقدر ما تريد. يمكنك استخدام إطار عمل واجهة المستخدم المفضل لديك وإضافة مكونات نمطية فوقه. هذا يعني أيضًا أنه يمكنك بسهولة ترحيل مكون مشروع موجود حسب المكون. يمكنك اختيار تصميم معظم التخطيط باستخدام CSS القياسي واستخدام المكونات المصممة فقط للمكونات القابلة لإعادة الاستخدام.
سلبيات
سيحتاج المصممون / مكاملو الأسلوب إلى تعلم JavaScript أساسي جدًا للتعامل مع المتغيرات واستخدامها بدلاً من Sass / Less.
سيتعين عليهم أيضًا تعلم كيفية التنقل في هيكل المشروع ، على الرغم من أنني أزعم أن العثور على أنماط لمكون في مجلد هذا المكون أسهل من الاضطرار إلى العثور على ملف CSS / Sass / Less الصحيح الذي يحتوي على القاعدة التي تحتاج إلى تعديلها.
سيحتاجون أيضًا إلى تغيير أدواتهم قليلاً إذا كانوا يريدون تمييز بناء الجملة ، وفحصها ، وما إلى ذلك. أفضل مكان للبدء هو مع هذا المكون الإضافي Atom وهذا البرنامج المساعد babel.