كيفية استخدام مكوّنات الأنماط في React

نشرت: 2022-03-10
ملخص سريع ↬ في حين أن النهج الذي يحركه المكون قد فتح آفاقًا جديدة في الطريقة التي نبني بها تطبيقات الويب ، إلا أنه لا يخلو من عيوبه - أحدها قابليته للاستخدام وقابلية التوسع باستخدام CSS. أدى هذا إلى ولادة طريقة جديدة لبناء وإدارة أنماطنا بطريقة خاصة بالمكون ، والتي تعرف باسم CSS-in-JS.

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

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

بكلمات React الخاصة ، المكونات المصممة هي " بدائل بصرية للمكونات " ، وهدفها هو تزويدنا بطريقة مرنة لمكونات النمط. والنتيجة هي اقتران محكم بين المكونات وأنماطها.

ملاحظة: تتوفر المكونات المصممة لكل من React و React Native ، وبينما يجب عليك بالتأكيد مراجعة دليل React Native ، سيكون تركيزنا هنا على المكونات المصممة لـ React.

المزيد بعد القفز! أكمل القراءة أدناه ↓

لماذا مكوّنات الأنماط؟

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

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

تثبيت

تركيب المكونات المصممة سهل. يمكنك القيام بذلك من خلال CDN أو مع مدير الحزم مثل Yarn ...

 yarn add styled-components

... أو npm:

 npm i styled-components

يستخدم عرضنا التوضيحي تطبيق create-react-app.

يبدأ

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

 import styled from "styled-components"; // Styled component named StyledButton const StyledButton = styled.button` background-color: black; font-size: 32px; color: white; `; function Component() { // Use it like any other component. return <StyledButton> Login </StyledButton>; }

هنا ، StyledButton هو المكون المصمم ، وسيتم تقديمه كزر HTML مع الأنماط المضمنة. styled هي طريقة مساعدة داخلية تحول التصميم من JavaScript إلى CSS فعلية.

في HTML و CSS الخام ، سيكون لدينا ما يلي:

 button { background-color: black; font-size: 32px; color: white; } <button> Login </button>

إذا كانت المكونات المصممة هي مكونات React ، فهل يمكننا استخدام الدعائم؟ نعم نستطيع.

التكيف على أساس الدعائم

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

 import styled from "styled-components"; const StyledButton = styled.button` min-width: 200px; border: none; font-size: 18px; padding: 7px 10px; /* The resulting background color will be based on the bg props. */ background-color: ${props => props.bg === "black" ? "black" : "blue"; `; function Profile() { return ( <div> <StyledButton bg="black">Button A</StyledButton> <StyledButton bg="blue">Button B</StyledButton> </div> ) }

نظرًا لأن StyledButton هو مكون React يقبل الخاصيات ، يمكننا تعيين لون خلفية مختلف بناءً على وجود أو قيمة خاصية bg .

ومع ذلك ، ستلاحظ أننا لم نعط زرنا type . لنفعل ذلك:

 function Profile() { return ( <> <StyledButton bg="black" type="button"> Button A </StyledButton> <StyledButton bg="blue" type="submit" onClick={() => alert("clicked")}> Button B </StyledButton> </> ); }

يمكن للمكونات المصممة التمييز بين أنواع الدعائم التي تتلقاها. إنهم يعرفون أن type هو سمة HTML ، لذا فهم يعرضون في الواقع <button type="button">Button A</button> ، أثناء استخدام bg في المعالجة الخاصة بهم. لاحظ كيف أرفقنا معالج الأحداث أيضًا؟

عند الحديث عن السمات ، تتيح لنا البنية الموسعة إدارة الدعائم باستخدام مُنشئ attrs . تحقق من هذا:

 const StyledContainer = styled.section.attrs((props) => ({ width: props.width || "100%", hasPadding: props.hasPadding || false, }))` --container-padding: 20px; width: ${(props) => props.width}; // Falls back to 100% padding: ${(props) => (props.hasPadding && "var(--container-padding)") || "none"}; `;

لاحظ كيف أننا لسنا بحاجة إلى ثلاثية عند ضبط العرض؟ هذا لأننا قمنا بالفعل بتعيين افتراضي لها width: props.width || "100%", width: props.width || "100%", . أيضًا ، استخدمنا خصائص CSS المخصصة لأننا نستطيع ذلك!

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

تمديد الأنماط

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

 const StyledContainer = styled.section` max-width: 1024px; padding: 0 20px; margin: 0 auto; `;

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

 const StyledContainer = styled.section` max-width: 1024px; padding: 0 20px; margin: 0 auto; `; const StyledSmallContainer = styled.section` max-width: 1024px; padding: 0 10px; margin: 0 auto; `;

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

 const StyledContainer = styled.section` max-width: 1024px; padding: 0 20px; margin: 0 auto; `; // Inherit StyledContainer in StyledSmallConatiner const StyledSmallContainer = styled(StyledContainer)` padding: 0 10px; `; function Home() { return ( <StyledContainer> <h1>The secret is to be happy</h1> </StyledContainer> ); } function Contact() { return ( <StyledSmallContainer> <h1>The road goes on and on</h1> </StyledSmallContainer> ); }

في StyledSmallContainer ، ستحصل على جميع الأنماط من StyledContainer ، ولكن سيتم تجاوز المساحة المتروكة. ضع في اعتبارك أنه ، عادةً ، ستحصل على عنصر قسم يتم تقديمه لـ StyledSmallContainer ، لأن هذا هو ما StyledContainer . لكن هذا لا يعني أنها منحوتة في الحجر أو غير قابلة للتغيير.

الدعامة متعددة الأشكال "كـ"

باستخدام الخاصية as الأشكال ، يمكنك تبديل عنصر النهاية الذي يتم عرضه. حالة استخدام واحدة عندما ترث الأنماط (كما في المثال الأخير). على سبيل المثال ، إذا كنت تفضل div as section StyledSmallContainer ، فيمكنك تمرير الخاصية المميزة إلى المكون المصمم بقيمة العنصر المفضل لديك ، مثل:

 function Home() { return ( <StyledContainer> <h1>It's business, not personal</h1> </StyledContainer> ); } function Contact() { return ( <StyledSmallContainer as="div"> <h1>Never dribble when you can pass</h1> </StyledSmallContainer> ); }

الآن ، سيتم تقديم StyledSmallContainer كملف div . يمكنك حتى أن يكون لديك مكون مخصص كقيمة لك:

 function Home() { return ( <StyledContainer> <h1>It's business, not personal</h1> </StyledContainer> ); } function Contact() { return ( <StyledSmallContainer as={StyledContainer}> <h1>Never dribble when you can pass</h1> </StyledSmallContainer> ); }

لا تأخذ الأمر كأمر مسلم به.

بناء الجملة مثل SCSS

يُمكّن Stylis المعالج CSS المسبق المكونات المصممة لدعم بناء جملة يشبه SCSS ، مثل التداخل:

 const StyledProfileCard = styled.div` border: 1px solid black; > .username { font-size: 20px; color: black; transition: 0.2s; &:hover { color: red; } + .dob { color: grey; } } `; function ProfileCard() { return ( <StyledProfileCard> <h1 className="username">John Doe</h1> <p className="dob"> Date: <span>12th October, 2013</span> </p> <p className="gender">Male</p> </StyledProfileCard> ); }

حيوية

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

 import styled, {keyframes} from "styled-components"; const slideIn = keyframes` from { opacity: 0; } to { opacity: 1; } `; const Toast = styled.div` animation: ${slideIn} 0.5s cubic-bezier(0.4, 0, 0.2, 1) both; border-radius: 5px; padding: 20px; position: fixed; `;

التصميم العالمي

في حين أن الهدف الأصلي لـ CSS-in-JS ، وبالتالي ، المكونات المصممة هو تحديد نطاق الأنماط ، يمكننا أيضًا الاستفادة من التصميم العالمي للمكونات المصممة. نظرًا لأننا نعمل في الغالب باستخدام أنماط محددة النطاق ، فقد تعتقد أن هذا إعداد مصنع ثابت ، لكنك ستكون مخطئًا. فكر في الأمر: ما هو النطاق حقًا؟ من الممكن تقنيًا بالنسبة لنا - باسم التصميم العالمي - أن نفعل شيئًا مشابهًا لهذا:

 ReactDOM.render( <StyledApp> <App /> </StyledApp>, document.getElementById("root") );

لكن لدينا بالفعل وظيفة مساعدة - createGlobalStyle - سبب وجودها الوحيد هو التصميم العالمي. فلماذا تنكر مسؤوليتها؟

شيء واحد يمكننا استخدام createGlobalStyle من أجله هو تطبيع CSS:

 import {createGlobalStyle} from "styled-components"; const GlobalStyle = createGlobalStyle` /* Your css reset here */ `; // Use your GlobalStyle function App() { return ( <div> <GlobalStyle /> <Routes /> </div> ); }

ملاحظة: الأنماط التي تم إنشاؤها باستخدام createGlobalStyle لا تقبل أي توابع. تعلم المزيد في الوثائق.

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

  • لا يمكننا استهداف أي شيء خارج تصيير الجذر بدونه (على سبيل المثال ، html ، body ، إلخ).
  • createGlobalStyle الأنماط ولكنها لا تقدم أي عناصر فعلية. إذا نظرت إلى المثال الأخير عن كثب ، ستلاحظ أننا لم نحدد أي عنصر HTML لعرضه. هذا رائع لأننا في الواقع قد لا نحتاج إلى العنصر. بعد كل شيء ، نحن مهتمون بالأنماط العالمية. نحن نستهدف المحددات بشكل عام ، وليس عناصر محددة.
  • لا يتم تحديد createGlobalStyle في أي مكان في تطبيقنا وسيكون قابلاً للتطبيق طالما أنه موجود في DOM. فكر في المفهوم وليس الهيكل .
 import {createGlobalStyle} from "styled-components"; const GlobalStyle = createGlobalStyle` /* Your css reset here */ .app-title { font-size: 40px; } `; const StyledNav = styled.nav` /* Your styles here */ `; function Nav({children}) { return ( <StyledNav> <GlobalStyle /> {children} </StyledNav> ); } function App() { return ( <div> <Nav> <h1 className="app-title">STYLED COMPONENTS</h1> </Nav> <Main /> <Footer /> </div> ); }

إذا كنت تفكر في البنية ، فلا ينبغي أن يكون app-title غرار ما هو محدد في GlobalStyle . لكنها لا تعمل بهذه الطريقة. أينما اخترت تقديم GlobalStyle الخاص بك ، فسيتم إدخاله عند تقديم المكون الخاص بك.

كن حذرًا : لن يتم عرض createGlobalStyles إلا إذا كانت موجودة في DOM وعندما تكون موجودة.

مساعد CSS

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

 const StyledTextField = styled.input` color: ${(props) => (props.isEmpty ? "none" : "black")}; `;

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

 const StyledTextField = styled.input` color: ${(props) => props.isEmpty ? "none" : props.active ? "purple" : "blue"}; `;

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

 const StyledTextField = styled.input` width: 100%; height: 40px; ${(props) => (props.empty && css` color: none; backgroundcolor: white; `) || (props.active && css` color: black; backgroundcolor: whitesmoke; `)} `;

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

 const StyledTextField = styled.input` width: 100%; height: 40px; // 1. Empty state ${(props) => props.empty && css` color: none; backgroundcolor: white; `} // 2. Active state ${(props) => props.active && css` color: black; backgroundcolor: whitesmoke; `} // 3. Filled state ${(props) => props.filled && css` color: black; backgroundcolor: white; border: 1px solid green; `} `;

تقسم صقلنا التصميم إلى ثلاث قطع مختلفة يمكن التحكم فيها وسهلة الفهم. إنه فوز.

StyleSheetManager

مثل مساعد CSS ، يعد StyleSheetManager طريقة مساعدة لتعديل كيفية معالجة الأنماط. يتطلب الأمر بعض الدعائم - مثل disableVendorPrefixes (يمكنك الاطلاع على القائمة الكاملة) - التي تساعدك على إلغاء الاشتراك في بادئات البائع من شجرته الفرعية.

 import styled, {StyleSheetManager} from "styled-components"; const StyledCard = styled.div` width: 200px; backgroundcolor: white; `; const StyledNav = styled.div` width: calc(100% - var(--side-nav-width)); `; function Profile() { return ( <div> <StyledNav /> <StyleSheetManager disableVendorPrefixes> <StyledCard> This is a card </StyledCard> </StyleSheetManager> </div> ); }

يتم تمرير disableVendorPrefixes كعنصر خاص إلى <StyleSheetManager> . لذلك ، سيتم تعطيل المكونات المصممة والملفوفة بواسطة <StyleSheetManager> ، ولكن لن يتم تعطيل المكونات الموجودة في <StyledNav> .

تصحيح أخطاء أسهل

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

 import React from "react"; import styled from "styled-components"; import "./App.css"; const LoginButton = styled.button` background-color: white; color: black; border: 1px solid red; `; function App() { return ( <div className="App"> <LoginButton>Login</LoginButton> </div> ); }

بشكل افتراضي ، تعرض المكونات LoginButton كـ <button class="LoginButton-xxxx xxxx">Login</button> في DOM ، LoginButton في React Developer Tools ، مما يجعل تصحيح الأخطاء أسهل. يمكننا تبديل قيمة displayName المنطقية إذا كنا لا نريد هذا السلوك. هذا يتطلب تكوين بابل.

ملاحظة : في الوثائق ، تم تحديد مكونات الحزمة babel-plugin-styled-components styleled ، بالإضافة إلى ملف التكوين .babelrc . تكمن المشكلة في ذلك ، نظرًا لأننا نستخدم create-react-app ، فلا يمكننا تكوين الكثير من الأشياء ما لم يتم إخراجها. هذا هو المكان الذي تأتي فيه وحدات ماكرو Babel.

سنحتاج إلى تثبيت babel-plugin-macros مع npm أو Yarn ، ثم إنشاء babel-plugin-macros.config.js في جذر تطبيقنا ، مع المحتوى:

 module.exports = { styledComponents: { displayName: true, fileName: false, }, };

مع fileName قيمة اسم الملف ، displayName اسم العرض باسم الملف لمزيد من الدقة الفريدة.

نحتاج الآن أيضًا إلى الاستيراد من macro :

 // Before import styled from "styled-components"; // After import styled from "styled-components/macro";

خاتمة

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

مزيد من الموارد

  1. التوثيق والمكونات المصممة
  2. "بناء نظام مكونات قابل لإعادة الاستخدام باستخدام React.js والمكونات المصممة" ، Lukas Gisder-Dube
  3. الاستخدام مع Next.js
  4. الاستخدام مع جاتسبي