حامد نیرومند
حامد نیرومند
خواندن ۱۱ دقیقه·۳ سال پیش

چگونه استایل‌های اضافی و بلااستفاده در css را با استفاده از webpack حذف کنیم؟

در هنگام توسعه سمت کلاینت یک وبسایت، از ابزارهای css خارجی مثل بوت استرپ استفاده می‌کنیم یا برای استفاده از اسلایدر، کروسل و پلاگین‌های مختلف جاوااسکریپتی، قطعا ملزم به اضافه کردن حداقل یک فایل css دیگه به پروژه می‌شیم که گاهی اوقات در بهترین حالت، ما 40 درصد از کلاس‌های بوت استرپ و فایل‌های خارجی رو در html‌ خودمون استفاده می‌کنیم و فقط باعث بالا رفتن حجم فایل‌هایی asset و در نتیجه عملکر ضعیف‌تر در لود سریع سایت و بحث سئو میشه. من در این آموزش قصد دارم به شما یاد بدم چطور با استفاده از webpack کلاس‌هاس بلااستفاده در فایل‌های css‌تون رو حذف کنید بطوری که فایل‌های css مختلف و html تون رو تحویل بدید و یک فایل css کم حجم و مفید دریافت کنید.

ابتدا از نصب بودن node در سیستم خودتون مطمئن شید و یک پروژه جدید ایجاد کنید:

npm init --y

سپس پکیج‌های زیر که عملکرد هر کدوم رو در ادامه توضیح میدم نصب می‌کنیم:

npm i -D webpack webpack-cli style-loader css-loader sass sass-loader html-webpack-plugin mini-css-extract-plugin purgecss-webpack-plugin

فایل webpack.config.js رو ایجاد می‌کنیم و اطلاعات زیر رو در اون قرار میدیم

ابتدا مواردی که در ادامه لازم داریم رو require می‌کنیم:

const path = require('path'); const glob = require('glob'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const PurgecssPlugin = require('purgecss-webpack-plugin');

سپس آبجکتی به نام config ایجاد میکنیم و entry، output ، module و plugins رو در اون معرفی می‌کنیم:

const config = { entry: { app: './src/js/index.js', }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].bundle.js' }, module: { rules: [], }, plugins: [ new HtmlWebpackPlugin({ template: path.join(__dirname, 'src', 'pages', 'index.html'), filename: 'index.html', chunks: ['app'], favicon: './src/public/favicon.png', title: 'Home' }), ] }
  • بخش entry: در این بخش مشخص میکنیم فایل‌(های) ورودی ما در کجا قرار دارن و در ادامه‌ی کانفیگ، به چه نامی استفاده شن که من اسم اون رو گذاشتم app.
  • بخش output: در این بخش مشخص می‌کنیم فایل خروجی با چه نامی و در کجا ایجاد بشه. که من مشخص کردم در فولدر dist و به نام خودش + bundle ایجاد بشه.
  • بخش plugins: در این بخش، پلاگین‌هایی که استفاده میکنیم رو معرفی و کانفیگ می‌کنیم. ما در حالت معمولی از پلاگین HtmlWebpackPlugin استفاده می‌کنیم. در کانفیگ این پلاگین باید مشخص کنیم سند html ما در کجا قرار داره، در هنگام یبلد شدن به چه نامی ایجاد بشه، از چه فایل‌های entry که در ابتدا مشخص کردیم استفاده کنه، فاوآیوکنش چی باشه و عنوان سند html در خروجی چی باشه. از این پلاگین استفاده می‌کنیم تا خود webpack و این پلاگین بصورت اتومات فایل‌های entry ما رو داخلش اینجکت کنه و درگیر مسیردهی فایل‌های asset به داخل سند html نشیم.

خب باید فولدر و فایل های مختلفی که تا اینجا مشخص کردیم رو بسازیم.

ساختار فایل‌ و فولدرهای استفاده شده
ساختار فایل‌ و فولدرهای استفاده شده

در ادامه‌ی کانفیگ webpack که مهمترین فایل ما هست خواهیم داشت:

module.exports = (env, {mode}) => { const isDevelopment = mode === 'development'; }

در این بخش آبجکت اصلی کانفیگ webpack رو داریم که براساس ورودی‌هاش می‌تونیم متوجه شیم پروژه روی حالت development داره اجرا میشه یا production تا بر اساس اون، کانفیگی که در بالا داشتیم رو تغییر بدیم و یک سری کانفیگ‌های بیشتر بهش اضافه کنیم.

در داخل این آبجکت باید rule مربوط به لودر فایل‌های scss رو به فایل کانفیگ بالا اضافه کنیم:

config.module.rules.push(...[ { test: /\.s[ac]ss$/, use: [ isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader', ], } ]);

همچنین اگر در حالت پروداکشن بود پلاگین های MiniCssExtractPlugin و PurgecssPlugin رو به پلاگین‌های کانفیگ اضافه می‌کنیم:

if (!isDevelopment) { config.plugins.push(...[ new MiniCssExtractPlugin({ filename: '[name].bundle.css', }), new PurgecssPlugin({ paths: glob.sync(`${path.join(__dirname, 'src')}/**/*.html`, { nodir: true, }) }), ]); }

پلاگین MiniCssExtractPlugin برای استخراج فایل‌های scss‌ یا css از داخل javascript استفاده میشه. چون در ادامه خواهید دید که ما فایل style.scss رو در index.js لود می‌کنیم. این پلاگین، اون فایل scss رو بصورت یک فایل مجزا در میاره.

پلاگین PurgecssPlugin پلاگین اصلی‌ای هست که این مقاله درموردش ایجاد شده و وظیفه بررسی و مقایسه فایل‌های html در بخش paths کانفیگش رو با فایل‌های entry در webpack داره و چنانچه در فایل‌های css سلکتوری رو پیدا کنه که در html ها وجود نداره اون رو حذف می‌کنه. من در بخش کانفیگ این پلاگین مشخص کردم که تمام فایل های html در فولدر src رو رصد کنه.

کار ما با اضافه کردن پلاگین و رول های مختلف به کانفیگ webpack تموم شد. در انتها باید اون فایل کانفیگ رو در این آبجکت return کنیم. فایل کامل webpack.config.js بصورت زیر خواهد بود:

const path = require('path'); const glob = require('glob'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const PurgecssPlugin = require('purgecss-webpack-plugin'); const config = { entry: { app: './src/js/index.js', }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].bundle.js' }, devServer: { static: { directory: path.join(__dirname, 'src/public'), }, compress: true, port: 9000, }, module: { rules: [], }, plugins: [ new HtmlWebpackPlugin({ template: path.join(__dirname, 'src', 'pages', 'index.html'), filename: 'index.html', chunks: ['app'], favicon: './src/public/favicon.png', title: 'Home' }), ] } module.exports = (env, {mode}) => { const isDevelopment = mode === 'development'; config.module.rules.push(...[ { test: /\.s[ac]ss$/, use: [ isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader', ], } ]); if (!isDevelopment) { config.plugins.push(...[ new MiniCssExtractPlugin({ filename: '[name].bundle.css', }), new PurgecssPlugin({ paths: glob.sync(`${path.join(__dirname, 'src')}/**/*.html`, { nodir: true, }) }), ]); } return config; }

وارد فایل entry اصلی‌مون یعنی فایل index.js می‌شیم و فایل style.scss‌ رو داخلش لود میکنیم:

require('../scss/style.scss');

در فولدر src/pages فایل index.html رو بصورت زیر خواهیم داشت:

<!DOCTYPE html> <html lang=&quoten&quot> <head> <meta charset=&quotUTF-8&quot> <meta http-equiv=&quotX-UA-Compatible&quot content=&quotIE=edge&quot> <meta name=&quotviewport&quot content=&quotwidth=device-width, initial-scale=1.0&quot> <title><%= htmlWebpackPlugin.options.title %></title> </head> <body> </body> </html>

در فایل style.scss من دو استایل ساده‌ی زیر رو قرار دادم:

body { background-color: blue; } .test { background-color: aqua; }

و در انتها، اسکریپت‌های لازم برای اجرای برنامه رو در فایل package.json قرار می‌دیم:

&quotscripts&quot: { &quotdev&quot: &quotwebpack --mode=development&quot, &quotbuild&quot: &quotwebpack --mode=production &quot, },

ابتدا می‌خوام پروژه رو در حالت development اجرا کنم.

npm run dev

در این حالت چون پلاگین MiniCssExtractPlugin تعریف نشده و در قسمت اضافه کردن rules، مشخص کردیم اگر در حالت development بود از style-loader استفاده کنه و اگر در حالت پروداکشن بود از MiniCssExtractPlugin.loader، پس در این حالت فایل scss ما بصورت یک فایل تکی از داخل js استخراج نمیشه و این جاوااسکریپت هست که استایل‌ها رو در هنگام اجرا در تگ styles در html اضافه می‌کنه:

همچنین چون در حالت develop از پلاگین PurgecssPlugin خبری نیست انتظار نداریم کلاس test در فایل style.scss حذف بشه.

حالا اگر فولدر dist رو پاک کنیم و پروژه رو در حالت پروداکشن اجرا کنیم جالب میشه:

npm run production

چهار فایل در فولدر dist ایجاد میشه که اگر فایل app.bundle.css‌ رو اجرا کنید با صحنه عجیب زیر مواجه میشید:

body{background-color:blue}

خبری از استایل مربوط به کلاس test نیست! چرا که المانی در اون سند html با چنین سلکتوری وجود نداره.

بله این یکی از کارهای جالبی هست که میشه با webpack‌ و پلاگین‌های اون انجام داد.

اما اگر از این روش در یک پروژه واقعی استفاده کنید قطعا دچار مشکل میشید. چرا؟

معمولا ما در html‌ خود المانی داریم که بر اساس رفتار کاربر روی المانی دیگه، با استفاده از جاوااسکریپت کلاس active (یا هر چیز دیگه) به یک المان دیگه اضافه می‌کنیم و این کلاس باعث میشه تغییر در استایل اون ایجاد شه. پس در حالت عادی، این کلاس در html ما وجود نداره پس webpack در هنگام بیلد، اون استایل رو از فایل css خروجی حذف میکنه.

برای رفع این مشکل، در بخش کانفیگ پلاگین PurgecssPlugin بخشی رو داریم بنام safelist‌ که کلاس‌هایی که میخوایم در هرصورت حذف نشن رو معرفی میکنیم؛ مثل همین کلاس active.

در رابطه با پکیج‌های جاوااسکریپتی هم به همین صورته. اگر از پکیج splide که پکیج مورد علاقه من در بحث ایجاد اسلایدر و کروسل هست استفاده کنید، پس از بیلد، هیچ اسلایدری در سایت نمی‌بینید چرا که این پکیج‌ها، اکثر کلاس‌های خودشون رو در هنگام لود صفحه، به سند html اضافه می‌کنن و در مرحله بیلد توسط webpack حذف شدن.

در اینجا هم باید بصورت regex در بخش safelist مشخص کنیم تمام کلاس‌هایی که با splide شروع میشن رو حذف نکنه تا اسلایدر بدون مشکل در سایت کار کنه.

new PurgecssPlugin({ paths: glob.sync(`${path.join(__dirname, 'src')}/**/*.html`, { nodir: true, }), safelist: () => ({ standard: ['active', 'fixed', /^splide*/], deep: [], greedy: [], }), }),

وب‌پک ابزار جالبیه که امکانات عجیب و غریبی به توسعه دهنده میده. تمام کتابخونه‌ها و فریمورک‌های جاوااسکریپتی در بستر همین webpack ایجاد میشن.

بنظر من webpack مرز بین یک توسعه دهنده فرانت اند ساده و حرفه‌ایه.

ما در webpack پلاگینی داریم که کدهای جاوااسکریپت شما رو encode میکنه تا سورس شما توسط دیگران قابل استفاده نشه.

یا این قابلیت رو به شما میده از جاوااسکریپت مدرن (ECMA) در سطح وب استفاده کنید تا از تمام امکانات جاوااسکریپت با خیال راحت بهره‌مند شید و نگران عدم اجرای برخی کدها در برخی مرورگرهای قدیمی نباشید چرا که خودش کد شما رو برای تمام دیوایس‌ها ترجمه و قابل اجرا می‌کنه.

یا این قابلیت رو به شما میده در یک پروژه‌ی قالب سایت، از تایپ اسکریپت بجای جاوااسکریپت استفاده کنید تا به حرفه‌ای‌ترین و استیبل‌ترین حالت ممکن بحث پویایی سایت رو توسعه بدید و از امکاناتی مثل شی‌گرایی و اینترفیس و تایپ‌ها و ... که در تایپ اسکریپت موجود هست در برنامتون بهره‌مند شید.

یا امکان استفاده از autoprefixer رو خواهید داشت تا از نمایش و عملکرد درست استایل‌ها در تمام مرورگرها مطمئن شید.

یا این امکان رو به شما میده در توسعه ساختار html تون بصورت ماژولار پیش برید. یعنی قطعه کد مربوط به هدر رو یک بار بنویسید و در فایل‌های html دیگه فقط اون فایل رو صدا بزنید و کاملا کامپوننت محور پیش برید و توسعه‌ی قالب ساده یک سایت رو به حرفه‌ای‌ترین شکل ممکن پیش ببرید.

سعی ‌می‌کنم بزودی آموزش‌های بیشتری از webpack رو بصورت ویدئویی در کانال آپاراتم قرار بدم.

مشتاقانه منتظر نظر، انتقاد و پیشنهادات شما دوستان عزیز خواهم بود.

webpackcssoptimize
توسعه دهنده فرانت اند و مسلط به VueJs، Angular و Webpack
شاید از این پست‌ها خوشتان بیاید