حسین احمدی
بنیانگذار توسینسو و برنامه نویس و توسعه دهنده ارشد وب

کاملترین آموزش وب پک (Webpack) برای طراحان وب | رایگان

در طول یکسری مطب قصد داریم آموزش وب پک (Webpack) رو شروع کنیم. اما Webpack چیست؟ سوال اصلی که خیلی از دوستان در ابتدا از خودشون می پرسن و اینکه اصلاً چرا باید کار با Webpack رو یاد بگیریم؟ دنیای کامپیوتر و مخصوصاً دنیای برنامه نویسی دائم در حال به روز شدن هست و هر روز ابزارهای زیادی معرفی میشن.

دوره های شبکه، برنامه نویسی، مجازی سازی، امنیت، نفوذ و ... با برترین های ایران

آموزش وب پک | آموزش WebPack


Webpack چیست؟

اگر کار شما طراحی و توسعه وب سایت هست و تا این لحظه با Webpack کار نکردید بدونید که یک نعمت بزرگ در زمینه طراحی و توسعه وب رو از دست دادید. بحث مدیریت فایل های مورد استفاده در یک پروژه وب مثل فایل های جاوا اسکریپت، فایل های CSS، تصاویر، فونت ها و ... همیشه یک مشکل بوده.

برای مثال، فرض کنید شما چند فایل Javascript در پروژتون دارید و هر کدوم از این فایل ها یکسری وابستگی ها به هم دارن ، حالا ممکنه یکسری از این کدها در یک بخش از سایت شما مورد استفاده قرار بگیره و در بخش دیگه ای مورد استفاده نباشه.

کاری که Webpack برای شما انجام میده مدیریت فایل ها و وابستگی اون ها، ایجاد فایل های Bundle شده و Minify شده و کلی کارهای جالب دیگه هست که قصد داریم با اون ها آشنا بشیم.قابلیت ها و امکاناتی که در Webpack وجود داره خیلی زیاد هست

اما با یک مثال خیلی ساده شروع می کنیم تا نحوه نصب Webpack و استفاده از اون رو یاد بگیریم و بعد بریم سراغ مثال های کاربری دیگه. برای استفاده از Webpack شما باید Nodejs رو نصب کنید. برای نصب Nodejs به این آدرس برید و نسخه مناسب با سیستم عاملتون رو دریافت کنید و عملیات نصب رو انجام بدید. بعد از از نصب Nodejs تو محیط Command دستور زیر رو وارد کنید:

D:\>node -v
v14.4.0

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

برای ایجاد اولین پروژه یک پوشه با نام WebpackSample ایجاد کنید:

D:\>md WebpackSample

D:\>cd WebpackSample

D:\WebpackSample>

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

D:\WebpackSample>npm init -y
npm WARN npm npm does not support Node.js v14.4.0
npm WARN npm You should probably upgrade to a newer version of node as we
npm WARN npm can't make any promises that npm will work with this version.
npm WARN npm Supported releases of Node.js are the latest release of 6, 8, 9, 10, 11, 12, 13.
npm WARN npm You can find the latest version at https://nodejs.org/
Wrote to D:\WebpackSample\package.json:

{
  "name": "WebpackSample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

فایل ایجاد شده، تنظیماتی هست که به صورت خودکار توسط Nodejs ایجاد میشه و شامل یکسری اطلاعات مانند نام پروژه، نسخه و صفحه اصلی سایت ما هست. همچنین این فایل شامل Dependecy های پروژه ما (برای مثال، Webpack به عنوان یک Dependecy شناخته میشه

هر چیزی که پروژه ما برای اجرا بهش نیاز داره) و اسکریپت هایی که میشه بوسیله npm اونا رو اجرا کرد که جلوتر باهاشون آشنا میشیم.برای این پروژه ما یک فایل ساده Javascript و یک فایل html ایجاد می کنیم که یک پیام ساده به کاربر نمایش میده. قبل از هر کاری Webpack رو بوسیله دستور زیر نصب می کنیم:

D:\WebpackSample>npm install webpack webpack-cli --save-dev

با اجرای دستور بالا Webpack به پروژه اضافه میشه و می تونیم ازش استفاده کنیم (اجرای دستور بالا در محیط ویندوز یا لینوکس تفاوتی نداره و فقط باید npm روی سیستم شما نصب باشه). گزینه های webpack و webpack-cli میگه که ما میخواییم webpack و امکانات webpack برای استفاده از محیط command رو نصب کنیم.

سوئیچ --save-dev هم برای این هست که ما webpack رو فقط برای محیط Development میخواییم در محیط Production بهش نیازی نداریم. بعد از اجرای دستور بالا، اگر فایل package.json رو باز کنیم میبینیم که بخش devDependencies به این فایل اضافه شده و ساختار فایل به صورت زیر تغییر کرده:

{
  "name": "WebpackSample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.12"
  }
}

داخل پوشه WebpackSample پوشه جدیدی ایجاد شده با نام node_modules که شامل dependency های پروژه ماست، یک پوشه با نام .bin هم وجود داره که اگر داخل این پشه رو نگاهی بندازیم مییبیم که فایلی با نام webpack.cmd ایجاد شده که ما برای استفاده از webpack این فایل رو اجرا می کنیم.

اگر شما از نسخه 3 و پایین تر webpack استفاده کرده باشید، می دونید که برای استفاده از webpack باید یک فایل config ایجاد می کردیم. اما در نسخه 4 نیازی به ایجاد فایل config نیست (البته برای ساده ترین حالت استفاده) که اصطلاحاً بهش Zero Config هم گفته میشه.

بدون استفاده از فایل config یکسری پیش فرض ها وجود داره، مثل اینکه فایل اصلی js ما داخل پوشه ای با نام src و با نام index.js باید تعریف میشه و خروجی webpack در پوشه ای با نام dist و فایلی با نام main.js ایجاد میشه.داخل پوشه WebpackSample یک پوشه با نام src ایجاد کنید و داخل پوشه src یک فایل با نام index.js:

D:\WebpackSample>md src

D:\WebpackSample>cd src

D:\WebpackSample\src>type nul > index.js

دستور آخر یک فایل خالی با نام index.js برای ما ایجاد می کند، محتویات فایل index.js رو به صورت زیر تغییر بدید:

alert('Hello Webpack! TOSINSO.COM');

داخل محیط command دستور زیر رو اجرا کنید:

D:\WebpackSample>node_modules\.bin\webpack
Hash: 0cd4cd3a207463b144c6
Version: webpack 4.43.0
Time: 288ms
Built at: 07/01/2020 11:49:55 AM
  Asset       Size  Chunks             Chunk Names
main.js  965 bytes       0  [emitted]  main
Entrypoint main = main.js
[0] ./src/index.js 36 bytes {0} [built]

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/

با اجرای این دستور پوشه ای با نام dist ایجاد شده و فایلی با نام main.js داخل آن ایجاد می شود. اگر دقت کنید من یک پیام هشدار دریافت کردم مبنی بر اینکه تنظیمات mode مشخص نشده و به خاطر همین فایل ما در حالت production ایجاد میشه. اگر بخواییم این پیام نمایش داده نشه دستور رو به صورت زیر تغییر میدیم:

D:\WebpackSample>node_modules\.bin\webpack --mode=production

با اجرای این دستور پیام warning حذف خواهد شد. اگر نگاهی به فایل main.js داخل پوشه dist بندازیم محتویاتش به صورت زیر هست:

آموزش Webpack

اما شاید بپرسید چرا محتویات فایل به این شکل شده؟ اصلاً دستور alert ای که نوشتیم کجاست؟ اگر آخر فایل رو نگاه کنید دستور alert رو میبینید، اما بقیه کدها از کجا اومدن؟ زیاد نگران این موضوع نباشید، چون این کدها توسط خود webpack برای مدیریت module های مورد نیاز فایل جهت اجرای درست برنامه اضافه شدن. در انتها باید تست کنیم که فایل ایجاد شده ما به درستی کار میکنه، برای اینکار داخل پوشه WebpackSample یک فایل با نام index.html ایجاد کنید و محتویات فایل رو به صورت زیر تغییر بدید:

<!DOCTYPE html>
<html lang="en" dir="ltr">
 <head>
  <meta charset="utf-8">
  <title>Hello Webpack</title>
 </head>
 <body>
  <script type="text/javascript" src="dist/main.js"></script>
 </body>
</html>

با باز کردن این فایل در مرورگر پیام alert برای ما نمایش داده میشه:

آموزش Webpack

تبریک میگم، شما اولین برنامتون رو با کمک Webpack نوشتید، اما این تازه شروع ماجرا بود. کلی امکانات و قابلیت های جذاب دیگه هم هست که باید باهاش آشنا بشید. در قسمت های بعدی با این امکانات و قابلیت ها بیشتر آشنا میشیم.

  • کدهای این مطلب رو می تونید از بخش ضمیمه ها در ابتدای مطلب دانلود کنید

دستور Build

در قسمت قبلی آموزش Webpack با نحوه نصب و استفاده از Webpack آشنا شدیم و دیدیم که چجوری میشه بوسیله دستور Webpack فایل های Javascript رو پردازش و Minify کرد. در این قسمت با دستورات Build آشنا میشیم. دستوری که در قسمت قبلی می نوشتیم به صورت زیر بود:

node_modules\.bin\webpack --mode=production

اما میشه کاری کرد که نوشتن این دستور راحت تر بشه. اگر نگاهی به فایل package.json بندازیم بخشی داریم به نام scripts که یک دستور با نام test تعریف شده:

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },

در این بخش می تونیم دستورات مورد نظرمون رو بنویسیم و بوسیله دستور npm اون ها رو فراخوانی کنیم. بخش test رو حذف می کنیم و دستور زیر رو جایگزینش می کنیم:

  "scripts": {
    "build": "webpack --mode=production"
  },

حالا کافیه به جای دستور قبلی برای اجرای webpack، دستور زیر رو در محیط command اجرا کنیم:

D:\WebpackSample>npm run build

بعد از اجرای دستور بالا عملیات اجرای دستور webpack برای ما انجام میشه و فایل main.js ایجاد میشه. همونطور که میبینید ما در بخش scripts آدرس webpack رو مشخص نکردیم و فقط دستور webpack رو نوشتیم، دلیل اینکار این هست که npm به صورت خودکار داخل پوشه bin به دنبال این دستور میگرده و اجراش می کنه. می تونیم اسکریپت دیگه ای با نام dev ایجاد کنیم برای حالت Development:

  "scripts": {
    "build": "webpack --mode=production",
    "dev": "webpack --mode=development"
  },

نحوه اجرا هم به صورت زیر هست:

npm run dev

تفاوت حالت production و development در minify کردن فایل javascript و محتویات اون هست، در حالت production فایل ما minify میشه و در بهینه ترین حالت ممکن خروجی برای ما تولید میشه، اما در حالت production فایل به صورت عادی ایجاد میشه و بهینه سازی خاصی بر روی فایل انجام نمیشه و تنها هدف ایجاد فایلی هست که برای ما خروجی رو تولید کنه.به امید خدا در بخش های بعدی در مورد نوشتن کدهای Modular و نحوه نشوتن کدها در چندین فایل مختلف توضیح میدیم.

نوشتن کدهای Modular

تا این قسمت از آموزش Webpack، ما با مفاهیم اولیه Webpack و نحوه استفاده از اون آشنا شدیم. در ادامه با نحوه نوشتن کدهای Modular آشنا خواهیم شد. در قسمت های قبلی ما یک فایل با نام index.js ایجاد کردیم و یک خط کد ساده داخل آن نوشتیم.

اما اگر کدهایی که می نویسیم زیاد باشه، باید این کدها رو توی چندین فایل تقسیم کنیم و بعد به بوسیله Webpack این کدها رو داخل یک فایل ترکیب کنیم. از پروژه ای که در قسمت های قبلی ایجاد کردیم در این بخش استفاده می کنیم. در ابتدا داخل پوشه src یک فایل با نام greeting.js ایجاد کرده و یک function با نام sayHello داخلش تعریف می کنیم:

function sayHello(){
    alert("Hello Webpack! This is from greeting.");
}

حالا برای اینکه بتونیم از تابع sayHello داخل فایل index.js استفاده کنیم، با ابتدا تابع sayHello رو export کنیم و بعد داخل فایل index عملیات import رو انجام بدیم. قابلیت export و import کردن در نسخه ES6 جاواسکریپت اضافه شده که خوشبختانه Webpack قابلیت شناسایی این کدها رو هم داره. در ابتدا از CommonJS استفاده می کنیم و در ادامه روش ES6 رو هم توضیح میدیم. برای export کردن خط زیر رو به فایل greeting.js اضافه می کنیم:

module.exports = sayHello;

در فایل index.js کدهای قبلی رو حذف کرده و کدهای زیر رو جایگزین می کنیم:

var sayHello = require('./greeting.js');

sayHello();

خط اول کد بالا بخش export شده در فایل greeting.js رو برای ما import میکنه و بعد می تونیم عملیات فراخوانی تابع sayHello رو انجام بدیم. دقت کنید که ما برای مشخص کردن مسیر فایل از ./ استفاده کردیم که اشاره به مسیری داره که فایل index.js در اون قرار داره، به این دلیل که فایل index.js و greeting.js در کنار هم هستند.حالا بوسیله دستور npm عملیات build رو انجام میدیم:

D:\WebpackSample>npm run build

حالا فایل index.html رو در مرورگر باز می کنیم و خواهیم دید که خروجی دستور alert تغییر کرده:

آموزش Webpack

حالا برای روش ES6 کد greeting.js رو به صورت زیر تغییر میدیم:

function sayHello(){
 alert("Hello Webpack! This is from greeting.");
}

export { sayHello };

و کد فایل index.js رو به صورت زیر تغییر میدیم:

import { sayHello } from './greeting.js';

sayHello();

در ادامه دستور build رو اجرا می کنیم:

D:\WebpackSample>npm run build

عملیات build با موفقیت انجام شده و فایل main.js برای ما ایجاد می شود. نکته ای که وجود داره میشه نام تابع sayHello رو به نام دیگه در زمان import کردن تغییر داد:

import { sayHello as greeting } from './greeting.js';

دو نکته اینجا وجود داره که باید بهش دقت کرد:

  1. اگر در قسمت نام فایل ./ رو ننویسیم پیام خطا دریافت می کنیم، چون به صورت خودکار پوشه node_modules برای جستجو در نظر گرفته میشه
  2. میشه پسوند فایل یعنی .js رو ننوشت، به این خاطر که webpack به صورت پیش فرض به دنبال فایل هایی با پسوند .js میگرده.

در این قسمت با نحوه نوشتن کدها به صورت modular آشنا شدیم، در قسمت بعدی با نحوه ایجاد فایل configuration برای webpack و نحوه استفاده از اون آشنا خواهیم شد.

تنظیمات webpack و فایل config

تا اینجا متوجه شدیم که webpack بدون نیاز به فایل configuration کار میکنه که اصطلاحاً بهش Zero Config میگن. مثل اینکه فایل پیش فرض برای پردازش index.js هست یا پوشه ای که فایل های خروجی تولید میشن dist نام داره و فایل خروجی هم main.js هست.

بوسیله فایل config میشه تمامی این موارد رو تغییر داد و البته کارهای دیگه ای که در قسمت های بعدی باهاش آشنا میشیم. در این بخش با مفاهیم اولیه فایل configuration آشنا میشیم. برای اعمال تنظیمات دلخواه در webpack باید فایلی با نام webpack.config.js ایجاد کنیم.

در زمان build کردن به صورت پیش فرض webpack به دنبال فایلی با این نام میگردد. البته امکان تغییر نام این فایل وجود دارد. برای تغییر نام فایل در فایل package.json بخش script دستور build رو به صورت زیر تغییر میدیم (البته در صورتی که بخواهیم نام فایل config چیز دیگه ای غیر از نام پیش فرض باشه):

scripts: {
"build": "webpack --config my-config.js"
}

ما در اینجا از فایل پیش فرض استفاده می کنیم و به همین خاطر در پوشه WebpackSample فایلی با نام webpack.config.js ایجاد می کنیم و محتویات فایل رو به صورت زیر تغییر میدیم:

module.exports = {
 entry: "./src/index.js",
 output: {
  filename: "main.js"
 }
}

در فایل تنظیمات، بوسیله entry نام فایل اصلی و در بخش output نام فایل خروجی رو مشخص کردیم. حالا دستور زیر رو اجرا می کنیم:

npm run build

عملیات build مانند سابق برای ما اجرا شده و البته تغییری در خروجی مشاهده نمی کنیم، چون تنظیمات ما، همون تنظیمات پیش فرض webpack هست، در ادامه بخش filename رو به صورت زیر تغییر میدیم:

module.exports = {
 entry: "./src/index.js",
 output: {
  filename: "../build/application.js"
 }
}

در تنظیمات بالا، بخش filename گفتیم که یک پوشه از پوشه پیش فرض که dist هست به عقب برگرد و داخل پوشه ای با نام build فایل خروجی رو با نام application.js ایجاد کن، دستور زیر رو اجرا می کنیم:

npm run build

حالا اگه به پوشه WebpackSample مراجعه کنیم میبینیم که پوشه ای با نام build برای ایجاد شده و داخلش فایلی با نام application.js هست:

آموزش webpack

امکان جداکردن مسیر از فایل هم در تنظیمات بالا وجود داره، کافیه بخشی به نام path به قسمت output اضافه کنیم و مسیر رو از قسمت filename حذف کنیم:

module.exports = {
 entry: "./src/index.js",
 output: {
  filename: "application.js",
  path: "/path/folder/build"
 }
}

مسیری که مشخص می کنیم باید آدرس کامل باشه، یعنی این مسیر به صورت relative در نظر گرفته نمیشه. البته مشخص کردن مسیر به صورت کامل در فایل تنظیمات زیاد جالب نیست، برای اینکه داخل قسمت path بتونیم خروجی رو بر اساس پوشه پروژه مشخص کنیم از یک module به نام path استفاده می کنیم. در ابتدا دستور زیر رو برای نصب path اجرا می کنیم:

npm install path --save-dev

بعد از نصب این module فایل webpack.config.js رو باید تغییر بدیم، در ابتدای فایل خط زیر رو اضافه می کنیم:

const path = require('path');

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

path: path.resolve(__dirname, 'build')

دستور path.resolve بوسیله متغیر __dirname و نام پوشه build مسیر نهایی رو برای ما ایجاد می کنه. فایل webpack.config.js به صورت باید بشه:

const path = require('path');

module.exports = {
 entry: "./src/index.js",
 output: {
  filename: "application.js",
  path: path.resolve(__dirname, 'build')
 }
}

پوشه های dist و build رو پاک می کنیم و مجدد دستور npm run build رو اجرا می کنیم تا فایل های خروجی برای ما ایجاد بشن. در نهایت هم فایل index.html رو تغییر میدیم تا از خروجی جدید استفاده کنه:

<!DOCTYPE html>
<html lang="en" dir="ltr">
 <head>
  <meta charset="utf-8">
  <title>Hello Webpack</title>
 </head>
 <body>
  <script type="text/javascript" src="build/application.js"></script>
 </body>
</html>

در ادامه مورد نحوه مشخص کردن حالت خروجی که production یا development هست رو توضیح میدیم. در فایل config بخشی به نام mode اضافه می کنیم:

const path = require('path');

module.exports = {
 mode: "development",
 entry: "./src/index.js",
 output: {
  filename: "application.js",
  path: path.resolve(__dirname, 'build')
 }
}

و در فایل package.json بخش scripts نیازی به سوئیچ --mode نداریم:

  "scripts": {
    "build": "webpack"
  },

در انتها اجرای دستور npm run build تا خروجی برای ما در حالت development ایجاد بشه. آخرین موردی که در این بخش در موردش توضیح میدیم قابلیت watch هست، در صورت استفاده از قابلیت watch، با یک بار اجرای دستور build، بعد از هر تغییر در فایل های js خروجی به صورت خودکار برای ما ایجاد میشه و نیازی به اجرای مجدد دستور build نیست، کافیه فایل webpack.config.js رو به صورت زیر تغییر بدیم:

const path = require('path');

module.exports = {
 watch: true,
 mode: "development",
 entry: "./src/index.js",
 output: {
  filename: "application.js",
  path: path.resolve(__dirname, 'build')
 }
}

دستور npm run build رو اجرا می کنیم و فایل index.html رو داخل مرورگر باز میکنیم. پیام Hello Webpack! This is from greeting برای ما نمایش داده میشه. حالا در فایل greeting.js کد alert رو به صورت زیر تغییر میدیم:

function sayHello(){
 alert("Hello Webpack! This is from greeting. I'm watching you!");
}

export { sayHello };

بدون اجرای دستور npm run build فقط صفحه html رو رفرش می کنیم و میبینیم که متن alert تغییر کرده.در این بخش با مفاهیم اولیه مربوط به تنظیمات webpack و فایل webpack.config.js آشنا شدیم. در بخش بعدی با Loader ها و Plugin ها آشنا میشیم.

آموزش وب پک | Webpack چیست؟

Loader ها ، Plugin ها و babel-loader

یکی از مهمترین یا بهتره بگیم دو مورد از مهمترین ویژگی های Webpack که خیلی مورد استفاده قرار میگیرن Loader ها و Plugin ها هستن. به طور خلاصه میشه گفت Webpack بدون Loader ها و Plugin ها هیچ کاربردی نداره و این دو مورد نقش قلب Webpack رو بازی می کنن.

در حقیقت Loader ها و Plugin ها این قدرت رو به Webpack میدن که کارهایی ماند پردازش فایل ها مانند فایل های css، ایجاد تغییر در ساختار فایل ها و ایجاد خروجی در شکل های مختلف رو انجام بده. در این بخش و چند قسمت پیش رو با Loader ها و Plugin ها آشنا شده و چند مثال هم از موارد پرکاربرد میزنیم.

تفاوت Loader با Plugin

در اینجا شاید به نظر بیاد که Loader و Plugin شبیه هم هستند و هر دو یکسری قابلیت ها به Webpack اضافه می کنن. اما یکسری تفاوت ها وجود داره. کاری که Loader ها انجام میدن پردازش فایل هایی مانند فایل های TypeScript و SASS و ترجمه اون ها هست، کار Loader ها مثل کامپایلر یا مفسر هایی هست که در زبان های برنامه نویسی استفاده میشن.


اما Plugin ها، هر کاری که بوسیله Loader ها نمیشه انجام داد بوسیله Plugin ها قابل انجام هست، برای مثال شاید ما از یک Loader برای پردازش فایل های SASS یا استفاده از استایل های css استفاده کنیم، ولی بخواییم این استایل ها در یک فایل جداگانه ایجاد بشن، Loader وظیفه پردازش فایل ما و ایجاد خروجی مورد نظر ما رو داره و پلاگین هم وظیفه ایجاد فایل.

تفاوت دیگه ای که وجود داره اینه که Plugin ها مانند یک شئ عمل میکنن (منظور شئ در برنامه نویسی شئ گرا هست) که در قسمت های بعدی کامل با این موضوع آشنا میشید. ما در این بخش و قسمت های دیگه با بعضی از پرکاربرد ترین Loader ها و Plugin های موجود در Webpack آشنا میشیم. برای استفاده از Loader ها و Plugin ها معمولاً سه مرحله رو باید طی کنید:

  1. نصب Loader یا Plugin مورد نظر بوسیله npm
  2. استفاده از require برای استفاده از Plugin نصب شده (این کار معمولاً برای Loader ها انجام نمیشه)
  3. استفاده از Loader یا Plugin (نکته ای که باید اینجا گفته بشه، حتماً باید مستندات مربوط به Loader یا Plugin رو بخونید، معمولاًتمامی پلاگین ها و لودرها مستندات کاملی دارن و هیچ برنامه نویسی کدهای مربوطه رو حفظ نمیکنه، چون بوسیله یک کپی و پیست میشه از کد استفاده کرد. اما اینکه بدونید کدی که استفاده می کنید چجوری کار میکنه خیلی مهمه)

در ادامه با اولین Loader یعنی Babel-Loader آشنا میشیم.

استفاده از Babel-Loader

فرض کنیم که برای نوشتن کدهای JavaScript از استاندارد ECMAScript 6 استفاده می کنیم که توسط بعضی مرورگرها پشتیبانی نمیشه، مخصوصاً مرورگرهای قدیمی. به صورت پیش فرض Webpack کدهای مربوط به import و export رو به دستورات دیگه تبدیل میکنه

اما با بقیه کدهایی که می نویسیم کاری نداره و اگر کد ما مبتنی بر ES6 نوشته شده باشه روی برخی از مرورگرها به مشکل برمیخوریم. برای رفع این مشکل از Babel-Loader استفاده می کنیم که کدما رو از استاندارد ES6 به ES6 تبدیل می کنه. برای استفاده از Babel ابتدا باید loader مربوط رو نصب کنیم. برای نصب این loader از دستور npm زیر استفاده می کنیم:

npm install --save-dev babel-loader @babel/core @babel/preset-env

باز هم میگم، نیازی به حفظ کردن تمامی این دستورات نیست و شما بوسیله مستندات هر Loader یا plugin میتونید به این موارد دسترسی داشته باشید. در قدم بعدی برای استفاده از babel-loader و تبدیل کدهای ES6 به ES5 فایل webpack.config.js رو باید تغییر بدیم. زیر بخش output، بخش جدیدی با نام module اضافه می کنیم و تنظیمات مربوط به babel-loader رو به صورت زیر انجام میدیم:

output: {
 filename: "application.js",
 path: path.resolve(__dirname, 'build')
},
module: {
 rules: [
 {
  test: /\.m?js$/,
  exclude: /(node_modules|bower_components)/,
  use: {
   loader: 'babel-loader',
   options:{
    presets: ['@babel/preset-env']
   }
  }
 }
 ]
}

شاید کمی کد بالا به نظر پیچیده و ترسناک بیاد، اما زیاد هم پیچیده نیست، در قسمت rules که زیر مجموعه module هست، ما تنظیمات مربوط به loader ها رو قرار میدیم که در اینجا تنظیمات مربوط به babel-loader هست. در قسمت test مشخص میکنیم که این loader بر روی چه فایل هایی اعمال میشه

بوسیله RegEx مشخص کردیم که فایل های mjs یا js باید توسط این loader پردازش بشن. در قسمت exclude مشخص کردیم که فایل های داخل پوشه های node_modules و bower_components نباید توسط babel پردازش بشن. در قسمت use هم مشخص کردیم که babel-loader باید توسط این rule استفاده بشه.

در بخش presets هم تنظیمات پیش فرضی که babel استفاده می کنه رو روی babel/preset-env قرار دادیم که برای ایجاد خروجی استفاده میشه.در ادامه فایل greeting.js رو به صورتی تغییر میدیم که از دستورات ES6 داخلش استفاده شده باشه. برای اینکار متن داخل alert رو تغییر میدیم و از قابلیت string interpolation استفاده می کنیم:

function sayHello(){
 let website = 'TOSINSO';
 alert(`Hello Webpack! This is from ${website}. I'm watching you!`);
}

export { sayHello };

دستور npm run build رو اجرا می کنیم و میبینیم که کد ایجاد شده مبتنی بر استاندارد ES5 هست که این تبدیل توسط babel انجام شده:

آموزش Webpack

حالا دیگه می تونیم بدون نگرانی کدهامون رو مبتنی بر ES6 بنویسیم و خیالمون راحت باشه که babel کار تبدیل رو برای ما انجام میده.در قسمت بعد در مورد نحوه فعال سازی قابلیت debugging در پروژه های مبتنی بر webpack صحبت می کنیم و اینکه چطور می تونیم فایل های Source Map رو به صورت خودکار ایجاد کنیم.

Debug کردن فایل های JavaScript

در ادامه مباحث مربوط به آموزش Webpack با نحوه Debug کردن فایل های JavaScript آشنا میشیم. همونطور که قبلاً دیدیم، زمان build کردن پروژه ها توسط Webpack، یکسری کد به صورت خودکار توسط Webpack به کدهای نوشته شده ما اضافه میشه.

وقتی که عملیات build در حالت production انجام میشه، فایل js به صورت bundle شده و minify شده تبدیل میشه، یعنی اگر ما 10 فایل مختلف داشته باشیم تمامی این فایل ها به یک فایل با نام مثلاً application.js تبدیل میشه و این باعث میشه که عملیات خطا یابی و Debug کردن کدها کار سختی بشه یا عملاً غیر ممکن بشه.

برای رفع این مشکل از قابلیتی به نام Source Map استفاده می کنیم. کاری که Source Map انجام میده کدهای Bundle شده رو به فایل سورس اصلی Map میکنه و میشه متوجه شد که یک خطا در کدوم فایل اتفاق افتاده. خود Webpack ابزاری برای اینکار در اختیار ما قرار میده.

اگر به این لینک مراجعه کنید جدولی رو مشاهده می کنید که مربوط به devtool در webpack هست، بخشی از این جدول قابلیت های مربوط به Source Map رو شامل میشه که راهکار های مختلف برای ایجاد Source Map رو توضیح میده. شما می تونید هر کدوم از این قابلیت ها که مناسب شماست رو انتخاب کنید یا حتی می تونید از Plugin هایی که برای اینکار ایجاد شدن استفاده کنید، اما در این مطلب ما با یکی از این ابزارها که نامش cheap-module-eval-source-map هست آشنا میشیم.

اگر توضیحات مربوطه رو مطالعه کنید، گفته شده که این ابزار تنها شماره خط و نام فایلی که خطا در اون اتفاق افتاده رو برای ما نمایش میده که در حال حاضر برای ما کافی هست.

برای فعال سازی این قابلیت، در فایل webpack.config.js بخشی به نام devtool رو به صورت زیر اضافه کنید:

module.exports = {
 watch: true,
 mode: "development",
 devtool: "cheap-module-eval-source-map",
 entry: "./src/index.js",

حالا باید ببینیم که قابلیت اضافه شده چه کاری برای ما انجام میده. فایل greeting.js رو باز میکنیم و دستور console.log زیر رو بهش اضافه میکنیم:

function sayHello(){
 let website = 'TOSINSO';
 alert(`Hello Webpack! This is from ${website}. I'm watching you!`);
 console.log("This message added for debugging!");
}

export { sayHello };

تغییرات فایل رو ذخیره می کنیم و دستور npm run build رو اجرا می کنیم. بعد از عملیات build فایل index.html رو داخل مرورگر باز میکنیم، کلید F12 رو برای نمایش Developer Tools میزنیم و وارد تب Console میشیم، پیام ما نمایش داده شده و جلوی پیام نمایش داده شده اطلاعات مربوط به فایل و خطی که دستور console.log داخلش نوشته شده نمایش داده میشه:

آموزش Webpack

اگر روی شماره خط و نام فایل کلیک کنیم، کد داخل فایل greeting.js برای ما نمایش داده میشه:

آموزش Webpack

اگر از قابلیت Source Map استفاده نمیکردیم، اطلاعات نمایش داده شده به فایل application.js اشاره میکرد که عملاً کمکی به ما برای پیدا کردن بخش مورد نظر نمیکرد.

  • سورس این بخش به صورت ضمیمه به این مطلب اضافه شده است که از بالای صفحه قابل دریافت است. برای کم شدن فایل پوشه node_modules حذف شده که برای resolve کردن dependency ها می تونید دستور npm install رو برای دانلود dependency ها اجرا کنید.

کار با CSS و SASS

یکی از مزیت های جالب و البته کاربردی Webpack، امکان استفاده و کار با CSS هست. در این بخش با نحوه نوشتن استایل ها CSS، پردازش اون ها توسط Webpack، قرار دادن استایل ها در صفحات html، پردازش فایل های SASS و در نهایت ایجاد فایل CSS نهایی جداگانه توسط Webpack توضیح میدیم.

اما شاید این سوال براتون پیش بیاد که چرا باید CSS رو بوسیله فایل JavaScript در پروژه استفاده کنیم؟ بهتر نیست که استایل ها جداگانه نوشته بشه و بعد به صفحه مورد نظر Link بشه؟ اجازه بدید اینطور توضیح بدم، فرض کنید که شما از یک کتابخانه برای Date Picker استفاده می کنید

و این Date Picker علاوه بر کدهای JavaScript، شامل یکسری Style ها در قالب CSS هم هست، شما بوسیله Import کردن استایل ها در فایل JavaScript امکان جدا کردن بخش های یک پروژه رو خواهید داشت و این کار باعث میشه که دسترسی به فایل ها راحت تر باشه.

در ادامه و در تکمیل پروژه ای که در بخش های قبلی استفاده کردیم، با یک مثال ساده نحوه استفاده از CSS رو توضیح میدیم. داخل پوشه src، یک فایل جدید با نام styles.css ایجاد کرده و کدهای زیر رو داخلش می نویسیم:

body{
 background-color: darkred;
}

در ادامه، فایل index.js رو باز کرده و خط زیر رو به ابتدای فایل اضافه می کنیم:

import styles from './styles.css';

و در نهایت اجرای دستور npm run build. بعد از اجرای دستور build با پیام خطای زیر مواجه میشیم:

ERROR in ./src/styles.css 1:4
Module parse failed: Unexpected token (1:4)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders

این خطا به ما میگه کع شمان دارید از فایلی استفاده می کنید که نیاز به Loader مخصوص اون فایل رو داره. در بخش Loader ها گفتیم که از loader برای پردازش فایل های خاص مثل فایل js یا css استفاده میشه. برای اینکه بتونیم از css در پروژمون استفاده کنیم loader مخصوص اینکار رو بوسیله دستور زیر به پروژه اضافه می کنیم:

npm install css-loader --save-dev

بعد از نصب css-loader باید تنظیمات مربوطه رو به فایل webpack.config.js اضافه کنیم:

rules: [
{
 test: /\.m?js$/,
 ...
},
{
 test: /\.css$/i,
 use:['css-loader']
}

اگر مجدد دستور npm run build رو اجرا کنیم میبینیم که دیگه پیام خطایی دریافت نمیکنیم و webpack تونست فایل css مارو پردازش کنه. اما اگر صفحه index.html رو در مرورگر باز کنیم، میبینیم که هیچ تغییری ایجاد نشده! دلیل این مشکل این هست که ما فقط فایل css رو پردازش کردیم، ولی محتویات اون رو به صفحه مورد نظر inject نکردیم. برای رفع این مشکل از loader ای با نام style-loader استفاده می کنیم. ابتدا loader رو بوسیله دستور زیر نصب می کنیم:

npm install style-loader --save-dev

بعد از نصب، rule ای که در فایل webpack.config.js تعریف کردیم رو به صورت زیر تغییر میدیم:

{
 test: /\.css$/i,
 use:['style-loader','css-loader']
}

دستور npm run build رو اجرا کرده، صفحه index.html رو باز می کنیم و اینبار خواهیم دید که استایل مورد نظر به صفحه ما اعمال شده. دقت کنید که ما داخل صفحه index.html به هیچ فایل css ای link ندادیم، کاری که انجام شده، استایل ها توسط کد javascript به بخش head صفحه ما اضافه شده که از بخش inspect element میشه این موضوع رو مشاهده کرد:

آموزش Webpack

حالا که با نحوه استفاده از css در webpack آشنا شدیم، یه نگاه کوتاه به نحوه کامپایل کردن SASS به CSS هم بندازیم. برای اینکار از Loader ای با نام sass-loader استفاده میکنیم (این loader از یک loader دیگه به نام node-sass استفاده میکنه که باید حتماً نصب بشه). برای نصب loader های مورد نیاز دستور زیر رو اجرا میکنیم:

npm install sass-loader node-sass --save-dev

بعد از نصب loader ها، به فایل webpack.config.js یک rule جدید برای فایل sass اضافه می کنیم:

{
 test: /\.css$/i,
 use:['style-loader','css-loader']
},
{
 test: /\.scss$/i,
 use:['style-loader','css-loader','sass-loader']
}  

داخل پوشه src یک فایل با نام application.scss ایجاد می کنیم با محتویات زیر:

$gradient: linear-gradient(to right, #00467f, #a5cc82);
body{
 background-image: $gradient;
}

و داخل فایل index.js، فایل application.sass رو import می کنیم:

import styles from './styles.css';
import application from './application.scss';

و در انتها اجرای دستور npm run build. اگر صفحه index.html رو باز کنیم gradient تعریف شده به صفحه اعمال شده و در بخش head هم میشه استایل های اضافه شده رو دید.یکی از قابلیت های خیلی جالب در webpack، امکان اضافه کردن Vendor Prefix ها به استایل های تعریف شده هست.

اگر با CSS کار کرده باشید می دونید که بعضی از خصوصیت های css زمان تعریف، نیاز به مشخص کردن یکسری خصوصیت های اضافی برای پشتیبانی مرورگرهای مختلف دارن. برای مثال، خاصیت linear-gradient که در همین قسمت در فایل sass تعریف کردیم، در صورت استفاده از Vendor Prefix ها به صورت زیر تعریف می شوند:

background: -webkit-gradient(linear, left top, right top, from(#00467f),
to(#a5cc82));
background: -o-linear-gradient(left, #00467f, #a5cc82);
background: linear-gradient(to right, #00467f, #a5cc82);

در webpack ما نیازی به تعریف Vendor Prefix ها نداریم و بوسیله loader ای با نام postcss-loader میشه این prefix ها رو به صورت خودکار اضافه کرد. برای اینکار ابتدا loader مورد نظر رو نصب می کنیم:

npm install postcss-loader --save-dev

و فایل webpack.config.js رو به صورت زیر تغییر میدیم:

{
 test: /\.css$/i,
 use:['style-loader','css-loader','postcss-loader']
},
{
 test: /\.scss$/i,
 use:['style-loader','css-loader','postcss-loader','sass-loader']
}

اما اگر مستندات مربوط به این loader رو مطالعه کنید، ذکر شده که در حالت عادی امکان استفاده از postcss-loader با سایر loader هایی که عملیات پردازش فایل های css رو انجام میدن وجود نداره، برای اینکه postcss-loader به درستی کار کنه، باید بوسیله گزینه importLoaders مشخص کنیم که css-loader از postcss-loader استفاده میکنه. فایل webpack.config.js رو به صورت زیر تغییر میدیم:

{
 test: /\.css$/i,
 use:['style-loader',{loader:'css-loader',options:{importLoaders: 1}},'postcss-loader']
},
{
 test: /\.scss$/i,
 use:['style-loader',{loader:'css-loader',options:{importLoaders: 1}},'postcss-loader','sass-loader']
}

کار دیگه ای که باید انجام بدیم این هست که مشخص کنیم postcss-loader از یک plugin با نام autoprefixer استفاده کنه. برای اینکار بخش تعریف postcss-loader رو مطابق کد زیر به یک Object تغییر میدیم تا علاوه بر اضافه کردن Prefix ها، مشخص کنیم که چه نسخه هایی از مرورگر نیاز به اضافه کردن prefix ها دارند:

{
 test: /\.css$/i,
 use: [
  'style-loader',
  {loader:'css-loader',options:{importLoaders: 1}},
  {
   loader: 'postcss-loader',
   options: {
    plugins: [
     require('autoprefixer')({
      overrideBrowserslist: ['last 3 versions', 'ie>9']
     })
    ]
   }
  }
 ]
},
{
 test: /\.scss$/i,
 use:[
  'style-loader',
  {loader:'css-loader',options:{importLoaders: 1}},
  {
   loader: 'postcss-loader',
   options: {
    plugins: [
     require('autoprefixer')({
      overrideBrowserslist: ['last 3 versions', 'ie>9']
     })
    ]
   }
  },
  'sass-loader'
 ]
}

فقط نصب autoprefixer رو نباید فراموش کنیم:

npm install autoprefixer --save-dev

برای نهایی کردن کار، دستور npm run build رو اجرا میکنیم تا خروجی برای ما ایجاد شود. فایل index.html رو مجدد باز می کنیم و اگر بخش styles در قسمت head رو باز کنیم، خواهیم دید که prefix ها به صورت خودکار برای ما ایجاد شدن:

آموزش Webpack

تا اینجا ما با نحوه پردازش فایل های css، فایل sass و همچنین اضافه کردن prefix ها به خصوصیات css آشنا شدیم. استایل های ما تا اینجا داخل خود صفحه اضافه میشدند. این حالت زمانی که تعداد استایل ها زیاد نیست مشکلی ایجاد نمی کند. اما اگر تعداد استایل ها زیاد باشد باید این استایل ها در یک فایل جداگانه ایجاد بشن. خوشبختانه webpack بوسیله plugin ای با نام mini-css-extract-plugin اینکار را نیز برای ما انجام می دهد. ابتدا این plugin را نصب می کنیم:

npm install mini-css-extract-plugin --save-dev

برای استفاده از این plugin کد زیر رو به ابتدای فایل webpack.config.js اضافه می کنیم:

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

و تنظیمات مربوط به این پلاگین رو هم به صورت زیر انجام میدیم:

plugins: [
 new MiniCssExtractPlugin({
  filename: 'application.css'
 })
],

حالا باید بخش rule های مربوط به loader ها رو تغییر بدیم تا به جای Inject کردن css در صفحه، فایل خروجی برای ما ایجاد بشه:

{
 test: /\.css$/i,
 use: [
  MiniCssExtractPlugin.loader,
  ...
 ]
},
{
 test: /\.scss$/i,
 use:[
  MiniCssExtractPlugin.loader,
  ...
 ]
}

در نهایت دستور npm run build رو اجرا می کنیم. اگر به پوشه build مراجعه کنیم میبینیم که فایلی با نام application.css برای ایجاد شده و شامل کدهای css ای هست که قبلاً در بخش style صفحه ما قرار می گرفتند. کد صفحه index.html رو تغییر داده و به فایل application.js لینک میدهیم:

<!DOCTYPE html>
<html lang="en" dir="ltr">
 <head>
  <meta charset="utf-8">
  <title>Hello Webpack</title>
  <link rel="stylesheet" href="build/application.css" />
 </head>

در قسمت های قبلی حالت build رو روی development قرار داده بودیم و فایل های ما bundle و minify نمی شدند. بخش mode از فایل webpack.config.js رو به production تغییر میدیم:

module.exports = {
 watch: true,
 mode: "production",

دستور npm run build رو اجرا می کنیم و اگر به پوشه build مراجعه کنیم، میبینیم که فایل js ما minify شده، اما فایل css خیر. دلیل این موضوع این هست که webpack به صورت پیش فرض تنها فایل های js رو minify میکنه، اما فایل های css خیر. برای minify کردن فایل های از plugin ای با نام optimze-css-assets-webpack-plugin استفاده می کنیم. ابتدا نصب این پلاگین:

npm install optimize-css-assets-webpack-plugin --save-dev

و بعد require کردن این پلاگین در ابتدای فایل webpack.config.js:

const OptimzeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');

و بعد هم تنظیمات مربوط به این پلاگین تا برای استفاده آماده بشه. در مستندات خود webpack بخشی هست که توضیحات مربوط به optimization هست، برای optimize کردن فایل های css به فایل webpack.config.js بخشی به صورت زیر استفاده می کنیم:

optimization: {
 minimizer: [
  new TerserJSPlugin({}),
  new OptimzeCssAssetsPlugin({})
 ]
},

در این بخش ما دو Optimizer مشخص کردیم، یکی OptimzeCssAssetsPlugin که بوسیله پلاگین optimize-css-assets-webpack-plugin انجام میشه و یکی دیگه TerserJSPlugin که به صورت پیش فرض توسط Webpack ارائه میشه و نیازی به نصب اون نیست. اگر ما TerserJSPlugin رو اضافه نمی کردیم، optimize کردن فایل های js برای ما انجام نمی شد. البته برای استفاده از TerserJSPlugin با حتماً در ابتدای فایل webpack.config.js این پلاگین رو require کنیم:

const OptimzeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');

تمامی کارهای مورد نیاز برای minify کردن فایل های css انجام شده، حالا باید دستور npm run build رو اجرا کنیم. بعد از build کردن اگر فایل css رو باز کنیم میبینیم که محتویات به صورت minify شده هست.

کار با تصاویر

یکی دیگر از قابلیت های بسیار جالب و کاربری Webpack کار با تصاویر هست. اینکه شما می توانید تصاویر مورد نظر رو داخل فایل های css استفاده کنید و این فایل ها بوسیله Webpack مدیریت شوند. اما همونطور که قابل حدس زدن هست، Webpack به صورت پیش فرض امکان شناسایی فایل های تصویر و مدیریت اون ها رو نداره

و باید از loader ها و plugin های مربوط استفاده کنیم.ابتدا یک تصویر دلخواه را داخل پوشه src کپی کنید (تصویر استفاده شده در این مثال flowers.jpg هست که در فایل ضمیمه موجوده). فایل application.scss رو به صورت زیر تغییر میدیم:

body{
 background-image: url('flowers.jpg');
 background-size: cover;
}

و دستور npm run build رو اجرا می کنیم. اما همونطور که انتظار داشتیم پیام خطا دریافت می کنیم. چون loader ای برای کار با تصاویر نداریم:

You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders

برای رفع این مشکل loader ای با نام url-loader رو نصب می کنیم:

npm install url-loader --save-dev

و در فایل webpack.config.js یک rule جدید برای url-loader به صورت زیر تعریف می کنیم:

{
 test: /\.(png|jpg|gif|svg)$/i,
 use:[
 {
  loader: 'url-loader',
  options: {
   limit: 8192
  }
 }
 ]
}

برای این rule مشخص کردیم که url-loader بر روی فایل هایی با پسوند png و jpg و gif و svg اعمال شود. اما یک گزینه اینجا وجود داره با نام limit که مشخص میکنه در صورتی که حجم فیل 8192 بایت کمتر بود در قالب base64 داخل فایل قرار بگیرد (انکود کردن فایل ها در قالب base64 باعث میشه تا به جای آدرس فایل، محتویات فایل استفاده بشه که باعث کاهش تعداد درخواست ها به سرور میشه). دستور npm run buld رو مجدد اجرا می کنیم. این بار خطای دیگه به صورت زیر دریافت می کنیم:

Error: Cannot find module 'file-loader'

برای رفع این مشکل file-loader که url-loader به اون وابسته هست رو نصب می کنیم:

npm install file-loader --save-dev

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

{
 test: /\.(png|jpg|gif|svg)$/i,
 use:[
 {
  loader: 'url-loader',
  options: {
   limit: 8192,
   name: '[name].[hash:7].[ext]'
  }
 }
 ]
}

همونطور که گفتیم اگر حجم فایل کمتر یا مساوی 8192 بایت باشه، محتویات فایل به صورت base64 داخل فایل قرار میگیرن، در غیر اینصورت فایل با قالبی که بوسیله بخش name مشخص شده، داخل پوشه build کپی شده و البته نام فایل در فایل css هم تغییر میکنه.

بخش hash:7 بر اساس محتویات فایل یک عبارت 7 کاراکتری ایجاد میکنه که و داخل نام فایل قرار میده که کمک میکنه در صورت تغییر فایل، در صورت کش شدن فایل استفاده شده در مرورگر، عملیات به روزرسانی برای دریافت فایل انجام بشه.دستور npm run build رو اجرا می کنیم. اگر داخل پوشه build رو نگاه کنیم محتویات پوشه به صورت زیر هست:

آموزش Webpack

و محتویات فایل application.css هم به صورت زیر تغییر میکنه:

body{background-color:#8b0000;background-image:url(flowers.b0208ba.jpg);background-size:cover}

صفحه index.html رو داخل مرورگر باز می کنیم و میبینیم که تصویر ما در صفحه نمایش داده می شود.حجم فایل اصلی flowers.jpg حدود 197 کیلوبایت هست که حجم زیادی حساب میشه. بوسیله webpack میشه فایل های تصویر رو compress کرد تا حجم تصاویر کاهش پیدا کنه. برای اینکار از image-webpack-loader استفاده می کنیم. برای نصب این loader دستور زیر رو اجرا می کنیم:

npm install image-webpack-loader --save-dev

اگر از macOs استفاده می کنید، برای استفاده از این loader باید libpng رو بوسیله دستور زیر نصب کنید:

brew install libpng

برای compress شدن تصاویر بخش زیر رو به فایل webpack.config.js اضافه می کنیم:

{
 test: /\.(png|jpg|gif|svg)$/i,
 use:[
 {
  loader: 'url-loader',
  options: {
   limit: 8192,
   name: '[name].[hash:7].[ext]'
  }
 },
 {loader: 'image-webpack-loader'}
 ]
}

دستور npm run build رو اجرا کرده و وارد پوشه build میشیم. دو تصویر متفاوت وجود داره که یکی تصویر اصلی و دیگری تصویر compress شده هست که حجم تصویر حدود 90 کیلوبایت کاهش پیدا کرده:

آموزش Webpack  قسمت 8: کار با تصاویر

اگر محتویات فایل application.css رو ببینیم، آدرس تصویر ما از تصویر compress شده استفاده شده:

body{background-color:#8b0000;background-image:url(flowers.7b441a0.jpg);background-size:cover}

به این نکته توجه داشته باشید که ما در قسمت rule تنها پسوند فایل های تصویر رو تعریف کردیم، اما اگر شما از سایر فایل ها مثل font ها استفاده کرده باشید، باید حتماً rule برای پسوندهای مورد نظرتون تعریف کنید.

مدیریت فایل ها و Cache

در این بخش مورد نحوه مدیریت Cache در Webpack صحبت می کنیم. زمانی که درخواستی از سمت مرورگر به سمت سرور ارسال می شود، فایل های استاتیک مانند تصاویر، فایل های JavaScript و فایل های CSS یکبار دانلود شده و در Cache مرورگر ذخیره می شود تا برای دفعات بعد، درخواستی به سمت سرور ارسال نشود و سرعت بارگذاری صفحات افزایش پیدا کند.

اما مشکلی که اینجا باهاش روبرو هستیم، Cache کردن بر اساس نام و مسیر فایل انجام می شود و اگر تغییری در فایل ها ایجاد شود، مرور به صورت خودکار فایل ها را به روز رسانی نمی کند. برای رفع این مشکل باید نام فایل ها بر اساس محتویات داخل آنها ایجاد شود تا مرورگر فایل های به روز شده را مجدد دانلود کند.

بوسیله Webpack، اینکار با افزودن یک رشته اضافی به نام فایل مانند Hash امکان پذیر است که در این بخش با این موارد آشنا خواهیم شد.موضوع دیگری که وجود دارد این است که شاید داخل پروژه ما، نیاز به چند فایل script داشته باشیم. برای مثال، یک فایل اسکریپت برای پنل مدیریت و یک فایل اسکریپت برای بخش اصلی وب سایت.

در این بخش ما در مورد مدیریت فایل ها و همچنین مبحث Cache صحبت خواهیم کرد.برای بخش اول، با نحوه ایجاد چند فایل خروجی در webpack صحبت می کنیم. برای مثال، فرض کنید که ما علاوه بر اسکریپت های مربوط به بخش اصلی سایت، یک سری اسکریپت ها برای پنل مدیریت داریم. داخل پوشه src یک فایل با نام admin.js ایجاد می کنیم با محتویات زیر:

function welcome(){
 alert("Welcome to admin panel!");
}

داخل فایل webpack.config.js بخشی به صورت زیر داریم:

entry: "./src/index.js",

برای اینکه فایل admin.js هم به عنوان یک اسکریپت مجزا در نظر گرفته شود، این بخش را به صورت زیر تغییر می دهیم:

entry: {
 application: "./src/index.js",
 admin: "./src/admin.js"
},

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

output: {
 filename: "[name].js",
 path: path.resolve(__dirname, 'build')
},

اگر دستور npm run build رو اجرا کنیم و نگاهی به پوشه build بندازیم دو فایل با نام های application.js و admin.js خواهیم دید. اما برای فایل های CSS چطور؟ اگر به خاطر داشته باشید داخل فایل index.js ما دو خط زیر رو ابتدای فایل اضافه کرده بودیم:

import styles from './styles.css';
import application from './application.scss';

خط اول فایل بالا رو به فایل admin.js منتقل کرده و دستور npm run build رو اجرا می کنیم. اما اینبار خطای زیر رو دریافت می کنیم:

ERROR in chunk application [entry]
application.css
Conflict: Multiple chunks emit assets to the same filename application.css (chunks 0 and 1)

این خطا به ما میگه تداخل در ایچاد فایل application.css وجود داره. برای رفع این مشکل، داخل فایل webpack.config.js بخش plugins، تنظیمات MiniCssExtractPlugin رو به صورت زیر تغییر میدیم:

plugins: [
 new MiniCssExtractPlugin({
  filename: '[name].css'
 })
],

کاری که انجام دادیم، از [name] برای مشخص کردن نام فایل استفاده کردیم. مجدد دستور npm run build رو اجرا می کنیم. اگر پوشه build رو نگاه کنیم، دو فایل با نام های application.css و admin.css ایجاد شده. شاید این سوال براتون پیش بیاد که webpack نام فایل ها رو از کجا انتخاب میکنه، بخش زیر رو در فایل wepack.config.js تغییر داده بودیم:

entry: {
 application: "./src/index.js",
 admin: "./src/admin.js"
},

نام فایل بر اساس نام خصوصیت شئ ای که برای entry مشخص شده انتخاب میشه. یعنی اگر ما application رو به myapp تغییر بدیم، نام فایل ها به myapp تغییر میکنه:

entry: {
 myapp: "./src/index.js",
 admin: "./src/admin.js"
},

قدم بعدی، اضافه کردن Hash به نام فایل ها برای مدیریت Cache مرورگر می باشد. در مورد Cache ابتدای مطلب توضیح دادیم. برای اضافه کردن Hash به نام فایل ها، بخش Output در فایل webpack.config.js رو به صورت زیر تغییر میدیم:

output: {
 filename: "[name]-[contenthash].js",
 path: path.resolve(__dirname, 'build')
},

همچنین برای فایل های css هم بخش plugins به صورت زیر تغییر میکنه:

plugins: [
 new MiniCssExtractPlugin({
  filename: '[name]-[contenthash].css'
 })
],

دستور npm run build رو مجدد اجرا کرده و محتویات پوشه build باید به صورت زیر باشه:

آموزش Webpack

علاوه contenthash موارد دیگه ای هم هست که میشه ازشون استفاده کرد که داخل مستندات Webpack در موردشون توضیح داده شده.فایل admin.js رو باز کرده و کد زیر رو به این فایل اضافه می کنیم:

function calculate(a,b){
 console.log(a+b);
}

دستور npm run build رو اجرا کرده و داخل پوشه build باید به این صورت باشه:

آموزش Webpack

مشکلی که اینجا وجود داره، با هر بار تغییر فایل ها، یک فایل جدید با Hash جدید برای ما ایجاد میشه که باعث بالا رفتن تعداد فایل های داخل پوشه به مرور زمان میشه. راهکار این مشکل حذف فایل های داخل پوشه build در زمان اجرای دستور build هست. برای اینکار از پلاگینی با نام clean-webpack-plugin استفاده می کنیم. ابتدا بوسیله دستور زیر این پلاگین رو نصب می کنیم:

npm install clean-webpack-plugin --save-dev

خط زیر رو به ابتدای فایل webpack.config.js اضافه می کنیم:

const CleanWebpackPlugin = require('clean-webpack-plugin').CleanWebpackPlugin;

و در ادامه بخش plugins رو به صورت زیر تغییر میدیم:

plugins: [
 new CleanWebpackPlugin(),
 new MiniCssExtractPlugin({
  filename: '[name]-[contenthash].css'
 })
],

و در نهایت دستور npm run build رو اجرا میکنیم. اگر اینبار پوشه build رو نگاه کنیم، خواهیم دید که فایل های اضافی حذف شده و تنها فایل های جدید ایجاد شده اند:

آموزش Webpack

تا این لحظه همه چیز به خوبی پیش رفته غیر از یک موضوع! به روز رسانی فایل index.html که باعث مشکل شده. اگر با هر بار تغییر فایل های js و css قرار باشه فایل index.html به صورت دستی تغییر کنه، کار ما خیلی سخت میشه. برای رفع این مشکل دو کار میشه انجام داد:

  1. استفاده از فایل manifest
  2. استفاده از template

فایل manifest، یک فایل شامل اطلاعات فایل های نهایی ما هست. برای ایجاد این فایل از پلاگین webpack-manifest-plugin استفاده می کنیم. ابتدا این پلاگین رو نصب می کنیم:

npm install webpack-manifest-plugin --save-dev

در ابتدای فایل webpack.config.js خط زیر رو اضافه کرده:

const WebpackManifestPlugin = require('webpack-manifest-plugin');

و بخش plugins رو هم به صورت زیر تغییر میدیم:

plugins: [
 new WebpackManifestPlugin(),
 new CleanWebpackPlugin(),
 new MiniCssExtractPlugin({
  filename: '[name]-[contenthash].css'
 })
],

دستور npm run build رو اجرا کرده و خواهیم دید داخل پوشه build یک فایل با نام manifest.json با محتویات زیر برای ما ایجاده شده:

{
 "admin.css": "admin-06cb27c45a021926b1c4.css",
 "admin.js": "admin-fc4cad311bcd9ee7e5d8.js",
 "application.css": "application-a6f39414f54ee74e901e.css",
 "application.js": "application-46a73b324507f99360e1.js",
"application.scss": "flowers.7b441a0.jpg"
}

کاری که ما باید با این فایل انجام بدیم، بوسیله یک تکنولوژی Server-Side مثل PHP یا ASP.NET، محتویات این فایل رو بخونیم و استایل ها رو در مکان مناسب قرار بدیم. روش دوم استفاده از قابلیت template هست، بوسیله این قابلیت ما یک template برای صفحه html مشخص میکنیم و خود webpack بخش های مربوط به js و css رو برای ما ایجاد میکنه. برای استفاده از این قابلیت پلاگین زیر رو نصب می کنیم:

npm install html-webpack-plugin --save-dev

و در قدم بعدی خط زیر رو به فایل webpack.config.js اضافه می کنیم:

const HtmlWebpackPlugin = require('html-webpack-plugin');

بخش plugins رو به صورت زیر تغییر میدیم:

plugins: [
 new HtmlWebpackPlugin(),
 new WebpackManifestPlugin(),
 new CleanWebpackPlugin(),
 new MiniCssExtractPlugin({
  filename: '[name]-[contenthash].css'
 })
],

دستور npm run build رو اجرا می کنیم و اگر داخل پوشه build رو نگاه کنیم فایل جدیدی با نام index.html با محتویات زیر ایجاد شده:

<!doctype html>
<html>
 <head>
  <meta charset="utf-8">
  <title>Webpack App</title>
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <link href="application-a6f39414f54ee74e901e.css" rel="stylesheet">
  <link href="admin-06cb27c45a021926b1c4.css" rel="stylesheet">
 </head>
 <body>
  <script src="application-46a73b324507f99360e1.js"></script>
  <script src="admin-fc4cad311bcd9ee7e5d8.js"></script>
 </body>
</html>

مورد آخری که باید بهش اشاره کنیم این هست که این فایل توسط خود webpack ایجاد شده و هیچ کدی داخلش وجود نداره. برای رفع این مشکل یک فایل با نام template.html داخل پوشه src و با محتویات زیر ایجاد می کنیم:

<!DOCTYPE html>
<html lang="en" dir="ltr">
 <head>
  <meta charset="utf-8">
  <title>Hello Webpack</title>
 </head>
 <body>
  <h1>Welcome to TOSINSO Webpack Tutorial!</h1>
 </body>
</html>

حالا باید به پلاگین html-webpack-plugin بگیم که از این template برای ایجاد خروجی استفاده کنه. بخش plugins داخل فایل webpack.config.js رو به صورت زیر تغییر میدیم:

plugins: [
 new HtmlWebpackPlugin({
  template: './src/template.html'
 }),
 new WebpackManifestPlugin(),

دستور npm run build رو اجرا کرده و میبینیم که محتویات فایل index.html ایجاد شده به صورت زیر هست:

<!doctype html>
<html lang="en" dir="ltr">
 <head>
  <meta charset="utf-8">
  <title>Hello Webpack</title>
  <link href="application-a6f39414f54ee74e901e.css" rel="stylesheet">
  <link href="admin-06cb27c45a021926b1c4.css" rel="stylesheet">
 </head>
 <body>
  <h1>Welcome to TOSINSO Webpack Tutorial!</h1>
  <script src="application-46a73b324507f99360e1.js"></script>
  <script src="admin-fc4cad311bcd9ee7e5d8.js"></script>
 </body>
</html>

کار با پوشه ها

در ادامه مباحث آموزش Webpack در مورر کار با پوشه ها صحبت می کنیم. یکی از مشکلات پروژه ای که تا الان باهاش کار کردیم، نحوه قرار گرفتن فایل ها داخل پوشه src هست که تمامی فایل های تصویر، جاوا اسکریپت، استایل ها و ... همگی داخل یک پوشه قرار گرفتن. برای رفع این مشکل ساختار پوشه src رو به صورت زیر تغییر می کنیم:

src
 images
  flowers.jpg
 scripts
  admin.js
  greeting.js
  index.js
 styles
  application.scss
  styles.css
 template.html

قدم بعدی، تغییر بخش entry در فایل webpack.config.js هست تا مسیر فایل های javascript بر اساس ساختار پوشه بندی جدید مشخص بشن:

entry: {
 application: "./src/scripts/index.js",
 admin: "./src/scripts/admin.js"
},

دستور npm run build رو اجرا می کنیم و با پیام زیر روبرو میشیم:

ERROR in ./src/scripts/index.js
Module not found: Error: Can't resolve './application.scss' in 'D:\WebpackSample\src\scripts'
 @ ./src/scripts/index.js 1:0-45

ERROR in ./src/scripts/admin.js
Module not found: Error: Can't resolve './styles.css' in 'D:\WebpackSample\src\scripts'
 @ ./src/scripts/admin.js 1:0-34

دلیل این خطا بخش import فایل های css و scss در فایل های اسکریپت هست. ما باید مسیر فایل های css و scss داخل فایل های جاوا اسکریپت و مسیر فایل تصویر در فایل scss رو تغییر بدیم:

فایل index.js:

import application from '../styles/application.scss';

فایل admin.js:

import styles from '../styles/styles.css';

و فایل application.scss:

body{
 background-image: url('../images/flowers.jpg');
 background-size: cover;
}

دستور npm run build رو مجدد اجرا می کنیم و این بار بدون مشکل عملیات build انجام می شود. موضوع بعدی استفاده از Alias ها است. فرض کنید تعداد پوشه هایی که ایجاد میشه زیاد هست و ما چندین Level پوشه داریم و ممکن هست که نوشتن آدرس ها به صورت زیر هم بشه:

import application from "../../../styles/application.scss"

نوشتن آدرس به این صورت زاد جالب نیست و برای رفع این مشکل میتونیم از Alias ها استفاده کنیم. برای اینکار داخل فایل webpack.config.js بخشی به نام resolve به صورت زیر تعریف می کنیم:

devtool: "cheap-module-eval-source-map",
resolve: {
 alias: {
  CssFolder: path.resolve(__dirname, 'src/styles/')
 }
},

قدم بعدی به روز رسانی فایل های اسکریپت و استایل به صورت زیر هست:

فایل index.js:

import application from 'CssFolder/application.scss';

و فایل admin.js:

import application from 'CssFolder/application.scss';

دستور npm run build رو اجرا می کنیم و میبینیم که عملیات build بدون مشکل انجام میشه.
موضوع بعدی resolve کردن لایبری ها از پوشه node_modules هست. برای مثال ما jquery رو بوسیله دستور زیر نصب می کنیم:

npm install jquery

و داخل فایل index.html دستور زیر رو می نویسیم:

import $ from 'jquery';

و در انتهاب فایل index.js دستور زیر رو اضافه می کنیم:

$('body').append('<p>This line is appended with jQuery!</p>')

و در نهایت اجرای دستور npm run build عملیات build رو انجام میدیم، اگر صفحه index.html رو باز کنیم میبینیم که متن به صفحه اضافه شده. نکته ای که اینجا وجود داره jquery به دلیل نصب توسط npm از داخل پوشه node_modules خونده میشه

اما اگر ما یک کتابخانه رو به صورت دستی دانلود کرده باشیم و به پروژه اضافه کنیم چطور؟ برای مثال، پوشه ای با نام mylibs به پوشه src اضافه کرده و فایل jquery.js رو داخلش کپی میکنیم و jquery رو بوسیله npm uninstall jquery حذف می کنیم. اگر دستور npm run build رو اجرا کنیم با پیام خطای زیر مواجعه میشیم:

ERROR in ./src/scripts/index.js
Module not found: Error: Can't resolve 'jquery' in 'D:\WebpackSample\src\scripts'
 @ ./src/scripts/index.js 3:0-23 5:0-1

برای رفع این مشکل، باید به webpack بگیم که علاوه بر پوشه node_modules، پوشه mylibs رو هم برای resolve کردن dependency ها در نظر بگیره. بخش resolve از فایل webpack.config.js رو به صورت زیر تغییر میدیم:

resolve: {
 alias: {
  CssFolder: path.resolve(__dirname, 'src/styles/')
 },
 modules: [path.resolve(__dirname,'src/mylibs'),'node_modules']
},

مجدد دستور npm run build رو اجرا می کنیم و این بار عملیات build بدون هیچگونه خطایی اجرا میشه.البته این نکته رو مد نظر داشته باشید که استفاده از npm برای نصب لایبری ها توصیه میشه و نکته گفته شده تنها برای این بود که بدونید چطور تحت شرایط خاص بتونید مسیر برای resolve کردن dependency ها رو مشخص کنید.نکته دیگه شاید در زمان build پیام هشدار مشابه پیام زیر دریافت کنید:

WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets:
  application-72bab53070a81fc4cd42.js (808 KiB)

WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
Entrypoints:
  application (808 KiB)
      application-a6f39414f54ee74e901e.css
      application-72bab53070a81fc4cd42.js


WARNING in webpack performance recommendations:
You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.
For more info visit https://webpack.js.org/guides/code-splitting/

دلیل این پیام این هست که حجم فایل application.js از حجم 244 کیلوبایت بیشتر شده (به دلیل include کردن jquery) که در بخش های بعدی در مورد برطرف کردن این مشکل صحبت می کنیم.

استفاده از DevServer

با یاد گیری مفاهیم Webpack و چگونگی استفاده از آن، وقتش رسیده که در مورد Development Server صحبت کنیم. تا اینجا ما با هر بار تغییر فایل ها دستور npm run build برای ایجاد فایل های خروجی اجرا میکردیم که با پیچیده شدن پروژه و افزایش تعداد فایل ها و ماژول هایی که استفاده میکنیم، مدت زمان build به شدت افزایش پیدا میکنه.

در کنار این موارد باید هر بار فایل index.html رو باز کنیم و رفرش کنیم تا خروجی تولید شده رو ببینیم. اما بوسیله استفاده از Webpack Development Server این مشکلات کامل حل میشه. زمانی که Development Server رو استفاده می کنیم، با هر بار تغییر فایل ها عملیات build با سرعت بالاتری انجام شده

و تغییرات برای ما به صورت خودکار نمایش داده می شوند. علاوه بر این موارد، به جای باز کردن فایل index.html، از یک آدرس مانند http://localhost:9000 برای مشاهده وب سایت استفاده میکنیم.در اولین قدم، developemt server رو بوسیله دستور زیر نصب می کنیم:

npm install webpack-dev-server --save-dev

داخل فایل webpack.config.js هم بخشی به نام devServer به صورت زیر اضافه میکنیم:

devServer: {
 port: 9000,
 contentBase: path.resolve(__dirname, 'build')
},

در تنظیمات بالا ما تنظیمات پورت رو روی 9000 قرار دادیم که هر عددی رو می تونید انتخاب کنید، بوسیله contentBase هم مشخص کردیم که محتویات سایت از چه پوشه ای باید خونده بشه. اگر از این گزینه استفاده نکنیم، پوشه اصلی که فایل webpack.config.js داخلش قرار گرفته در نظر گرفته میشه

اما به این خاطر که ما از html-webpack-plugin استفاده کردیم و فایل html داخل پوشه build قرار میگیره، مسیر contentBase رو مشخص کردیم.تغییر مورد نیاز بعدی اضافه کردن یک اسکریپت جدید به فایل package.json است که بتونیم dev server رو اجرا کنیم. بخش script رو به صورت زیر تغییر میدیم:

"scripts": {
"build": "webpack",
"start": "webpack-dev-server"
},

در انتها برای اجرای dev serve کافیه دستور npm run start رو اجرا کنیم تا خروجی زیر نمایش داده بشه:

i 「wds」: Project is running at http://localhost:9000/
i 「wds」: webpack output is served from /
i 「wds」: Content not from webpack is served from D:\WebpackSample\build
‼ 「wdm」: Hash: 7cf77f481bcbc5fca949

در پیام بالا مشخص شده که پروژه ما از آدرس localhost:9000 در دسترس است و اگر این آدرس رو داخل مرورگر باز کنیم، صفحه html برای ما باز میشه. موضوع دیگه ای که باید بهش اشاره کنیم، در صورت تغییر هر یک از فایل ها، دیگه نیازی به build کردن پروژه نیست و تغییرات به صورت خودکار اعمال میشن.

مباحث مربوط به Webpack خیلی گسترده هست و ما سعی کردیم در یکسری مطالب مهمترین ویژگی های این ابزار رو ذکر کنیم. دوستان می تونن با مراجعه به وب سایت Webpack و مطالعه مستندات داخل سایت با سایر ویژگی های این ابزار قدرتمند آشنا بشن.

  • وب پک | Webpack چیست؟

    وب پک | webpack یک ابزار بسیار کارآمد برای توسعه دهنده‌های بخش کاربری یا فرانت اند با زبان جاوا اسکریپت است که با استفاده از آن توسعه اپلیکیشن‌های وب سریع‌تر، کارآمدتر و انعطاف پذیرتر خواهد بود. وقتی وب پک برنامه تحت وب شما را پردازش می‌کند، یک گراف وابستگی از هر ماژولی که برنامه نیاز دارد را به صورت بازگشتی می‌سازد. سپس، تمام آن ماژول‌ها را در فایل‌های باندل کوچکی قرار می‌دهد که توسط مرورگر بارگزاری می‌شوند.
  • در کجا از Webpack استفاده کنیم؟

    وب پک بصورت تخصصی برای استفاده در توسعه نرم افزارهای نوشته شده به زبان جاوااسکریپت | JavaScript کاربرد دارد. پیشنهاد می کنم برای درک مفهوم بهتر آن حتما مقاله آموزش WebPack را کامل مطالعه کنید.

حسین احمدی
حسین احمدی

بنیانگذار توسینسو و برنامه نویس و توسعه دهنده ارشد وب

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

نظرات