آشنایی با CSS Module در react

Introduction to CSS Module in React

23 بهمن 1399
آشنایی با CSS Module در react

CSS Module چیست؟

در قسمت قبلی با پکیجی به نام radium آشنا شدیم که به ما اجازه می داد در استایل های inline در React از قابلیت هایی مانند hover و media query ها استفاده کنیم. در این قسمت می خواهیم راه دیگری به نام CSS Module ها را به شما معرفی کنیم. CSS Module ها راهی هستند که با استفاده از آن ها می توانیم از فایل های CSS در جاوا اسکریپت استفاده کنیم و حتی اگر نام کلاس های چندین عنصر را یکی قرار بدهیم باز هم فایل CSS مربوط فقط روی عنصر مربوط اعمال شده و روی دیگر عناصر اعمال نمی شود.

برای شروع دستور import برای radium و ثابت style را از فایل Person.js حذف می کنیم. همچنین دیگر نیازی به property استایل روی div خود نداریم. بنابراین کل کدهای این فایل به این شکل خواهند بود:

import React from 'react';
import './Person.css';

const person = (props) => {
    return (
        <div className="Person">
            <p onClick={props.click}>I'm {props.name} and I am {props.age} years old!</p>
            <p>{props.children}</p>
            <input type="text" onChange={props.changed} value={props.name} />
        </div>
    )
};

export default person;

حالا به فایل App.js بروید و دستور import برای radium و عنصر <StyleRoot> را حذف کنید. سپس Radium را از دستور export و کدهای hover را نیز از قسمت استایل ها حذف کنید.

برای استفاده از CSS Module ها دو راه وجود دارد. در راه اول باید پیکربندی پکیج هایمان را تغییر دهیم که راه قدیمی تر است و راه دوم راه جدیدی است که اخیرا اضافه شده است و نیازی به تغییر در پیکربندی ندارد. ما از راه اول شروع می کنیم...

در روش اول باید به نوعی پیکربندی پکیج react-scripts را تغییر دهیم. برای این کار می توانیم به پوشه node_modules برویم و به صورت دستی فایل های مورد نظر را تغییر دهیم اما با هر بار اجرای دستور npm install تمام تغییرات ما override شده و به نسخه قبلی برمی گردند. راه بهتر استفاده از دستوری به نام eject است. این دستور به npm می گوید همه چیز را به صورت خودکار برای من آماده کن اما پیکربندی را خودم تعیین می کنم. این دستور راه برگشتی ندارد اما نباید به مشکلی برخورد کنیم، تمامی پکیج ها به خوبی کار می کنند.

نکته: اگر از git برای نظارت بر تغییرات پروژه استفاده می کنید باید حتما قبل از اجرای این دستور تمام تغییرات را commit کنید و گرنه تغییرات را از دست می دهید.

یک بار دیگر ترمینال را باز کنید (منوی view و سپس terminal) و دستور زیر را در آن اجرا کنید:

npm run eject

با اجرای این دستور از شما سوال می شود:

Are you sure you want to eject? This action is permanent.

سوال برای اطمینان یافتن از اجرای این دستور است. همانطور که گفتم اگر این دستور را اجرا کنید راه بازگشتی به حالت قبلی ندارید. حرف y را (مخفف yes) وارد کرده و اینتر بزنید.

پس از اجرا شدن این دستور دو پوشه جدید برایمان ساخته می شود:

  • scripts
  • config

در پوشه config دو فایل برای ما اهمیت دارند:

  • config.dev.js
  • confgi.prod.js

webpack یک ابزار برای bundle کردن فایل های ما است؛ به زبان ساده تر webpack فایل های ما را مرتب کرده و با هم ادغام می کند و به صورت یک مجموعه به کاربر پاس می دهد. به طور مثال ما در فایل جاوا اسکریپت خود دستور import را برای فایل های css استفاده کرده ایم که در حالت عادی غلط است اما webpack متوجه منظور ما شده و فایل ها را خودش مرتب می کند.

ابتدا به فایل webpack.config.dev.js بروید، در حدود خط 103 به قسمت module می رسیم، سپس در حدود خط 162 به قسمت test برای فایل های CSS می رسیم. حالا در همان قسمت باید کدهای زیر را ببینید:

test: /\.css$/,
            use: [
              require.resolve('style-loader'),
              {
                loader: require.resolve('css-loader'),
                options: {
                  importLoaders: 1,
                },
              },

قسمت oprions را به شکل زیر تغییر دهید:

options: {
                  importLoaders: 1,
                  modules: true,
                  localIndentName: '[name]__[local]__[hash:base64:5]'
},

Modules:true به ما اجازه می دهد که از CSS Modules استفاده کنیم. سپس localIndentName کاری می کند که name (یعنی نام کلاس های CSS) به صورت محلی (local) ذخیره شوند. Hash اضافه شده در آخر نیز مسئول تعیین hash است تا استایل ها یکتا باشند و روی کلاس های دیگر اعمال نشوند (override رخ ندهد).

حالا می توانیم همین کدهای قسمت options را کپی کرده و به فایل webpack.config.prod.js برویم. در حدود خط 181 به قسمت use در قسمت CSS می رسید. مقدار options را به این شکل تغییر دهید:

use: [
        {
          loader: require.resolve('css-loader'),
          options: {
            importLoaders: 1,
            modules: true,
            localIndentName: '[name]__[local]__[hash:base64:5]',
            minimize: true,
            sourceMap: shouldUseSourceMap,
         },
},

با این کار تغییر مورد نظر را هم برای مرحله توسعه برنامه (فایل webpack.config.dev) و هم در مرحله تولید (یعنی نهایی شدن برنامه و قرار گیری روی هاست - فایل webpack.config.prod) ایجاد کرده ایم چرا که نمی خواهیم هنگام ارسال برنامه به سرور به مشکل برخورد کنیم.

نکته مهم: در این آموزش من در فایل webpack.config.js به دنبال این قسمت می گردم:

{
  test: /\.css$/,
  ...
}

اما در نسخه های جدیدتر، این قسمت به شکل زیر درآمده است:

{
  test: cssRegex,
  exclude: cssModuleRegex,
  ...
}

بنابراین اگر حالت اول را ندیدید نگران نشوید. اگر نسخه شما جدید است و با حالت دوم روبرو هستید باید این قسمت در فایل webpack.config.js را به شکل زیر دربیاورید:

{
  test: cssRegex,
  exclude: cssModuleRegex,
  use: getStyleLoaders({
      importLoaders: 1,
      modules: true,
      localIdentName: '[name]__[local]__[hash:base64:5]'
  }),
}

همچنین در آخرین نسخه create-react-app دیگر فایل webpack.config.prod.js را نداریم و فقط همان فایل webpack.config.dev.js به شما داده می شود. بنابراین فقط همان را ویرایش کنید.

از این به بعد فایل های CSS دارای scope خواهند بود. به طور مثال فایل App.css که در فایل App.js وارد (import) شده است، فقط و فقط مخصوص همان کامپوننت (کامپوننت App) است و در فایل های دیگر اعمال نخواهد شد. البته باید دستور import را کمی تغییر دهیم:

import classes from './App.css';

شما می توانید هر نامی به جای Classes قرار دهید (مثلا styles). مهم این است که classes در اینجا یک شیء جاوا اسکریپتی است که کلاس های فایل App.css را در خود نگه می دارد. حالا برای تست می توانیم به قسمت return در کامپوننت App برویم (کدهای JSX) و به جای وارد کردن مقدار رشته ای App برای className به شیء classes دسترسی پیدا کنیم:

return (
        <div className={classes.App}>
          <h1>Hi, I'm a React App</h1>
          <p className={classes.join(' ')}>This is really working!</p>
          <button
            style={style}
            onClick={this.togglePersonsHandler}>Toggle Persons
        </button>
          {persons}
        </div>
);

توجه کنید که قسمت تغییر داده شده div اول است:

<div className={classes.App}>

حالا باید قسمت های دیگر کد را هم تغییر دهیم. به طور مثال ما دیگر چنین کدی را قبول نمی کنیم:

    const classes = [];

    if (this.state.persons.length <= 2) {
      classes.push(classes.red); // classes = ['red']
    }

    if (this.state.persons.length <= 1) {
      classes.push(classes.bold); // classes = ['red', 'bold']
    }

چرا؟ به این دلیل که کلاس های ما در یک شیء جاوا اسکریپتی قرار دارند بنابراین دیگر رشته ها را push نمی کنیم. بلکه می گوییم:

    if (this.state.persons.length <= 2) {
      classes.push(classes.red); // classes = ['red']
    }

    if (this.state.persons.length <= 1) {
      classes.push(classes.bold); // classes = ['red', 'bold']
    }

بهتر است برای جلوگیری از ایجاد مشکل، آرایه classes را که در یک ثابت قرار داده بودیم ویرایش کنیم. منظورم این قسمت است:

const classes = [];

بنابراین نامش را به یک نام دیگر ویرایش می کنیم:

const assignedClasses = [];

و طبیعتا خط های بعدی را نیز باید درست کنیم:

    const assignedClasses = [];

    if (this.state.persons.length <= 2) {
      assignedClasses.push(classes.red); // classes = ['red']
    }

    if (this.state.persons.length <= 1) {
      assignedClasses.push(classes.bold); // classes = ['red', 'bold']
    }

همچنین در قسمت return:

<p className={assignedClasses.join(' ')}>This is really working!</p>

اگر الان به مرورگر برویم می بینیم که تغییرات ما اعمال نشده است. دلیلش هم این است که اگر پیکربندی را تغییر دهید باید سرور مجازی خود را ری استارت کنیم. بنابراین command prompt را ببندید و دوباره دستور npm start را اجرا کنید تا سرور دوباره راه بیفتد.

حالا باید تغییرات استایل ها را برای کامپوننت Person.js نیز انجام دهیم. بنابراین ابتدا دستور import را تغییر می دهیم:

import classes from './Person.css';

و نام کلاس را هم اضافه می کنیم:

return (
        <div className={classes.Person}>
            <p onClick={props.click}>I'm {props.name} and I am {props.age} years old!</p>
            <p>{props.children}</p>
            <input type="text" onChange={props.changed} value={props.name} />
        </div>
)

حالا کار ما تکمیل شده است (فعال کردن hover و اینگونه موارد را برای جلسه بعد می گذاریم).

روش دوم برای استفاده از CSS Module ها بسیار راحت تر است. اگر از react-scripts@2.0.0 (یعنی نسخه 2 به بالا) استفاده می کنید نیازی به eject کردن نیست! در واقع react به طور خودکار از CSS Module ها پشتیبانی می کند و نیازی نیست هیچ فایلی را تغییر دهید. استفاده از آن هم دقیقا مانند مواردی است که بالاتر گفتیم (منهای eject کردن). می توانید انواع مثال ها و توضیحات در این رابطه را در این صفحه react در github بخوانید.

امیدوارم از این قسمت نیز به درک بهتر شما کمک کرده باشد.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری دوره جامع آموزش ری اکت توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما (1 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

rad rad
05 دی 1398
سلام من از نسخه سه create-react-appاستفاده میکنم طبق توضیحات شما تنظیمات فایلها رو تغییر ندادم و فقط قسمت import cssتغییر دادم ولی استایل ها اعمال نمیشه؟

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

امیر زوارمی
06 دی 1398
سلام دوست عزیز، مطمئن باشید که پسوند module.css. در انتهای فایلتون باشه. مثل: style.module.css اگر فقط دستور import رو بدون تغییر نام فایل Css تغییر بدید استایل های اعمال نمیشن. یک مثال کوتاه دیگه در لینک زیر از سایت رسمی هست که می تونین مطالعه کنید تا متوجه بشید کدوم قسمت رو صحیح انجام ندادین: https://create-react-app.dev/docs/adding-a-css-modules-stylesheet/

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.