اگر یادتان باشد در جلسات اولیه این دوره آموزشی گزینه ای به نام mode در فایل webpack.config.js وجود داشت و من آن را روی none قرار دادم:
module.exports = { entry: './src/index.js', output: { filename: 'bundle.[contenthash].js', path: path.resolve(__dirname, './dist'), publicPath: '' }, mode: 'none', // بقیه کد ها //
این گزینه از نسخه چهارم webpack اضافه شده است و کار ما را بسیار ساده تر کرده است. mode یعنی حالت کاری webpack که می تواند یکی از دو نوع زیر باشد:
من با production شروع می کنم. این حالت پلاگین های بسیاری را فعال می کند (به طور مثال Terser) شما می توانید لیست کاملی از این پلاگین ها را در قسمت mode از وب سایت رسمی webpack پیدا کنید. این وب سایت حالت production را به شکل زیر تعریف می کند:
// webpack.production.config.js module.exports = { + mode: 'production', - performance: { - hints: 'warning' - }, - output: { - pathinfo: false - }, - optimization: { - namedModules: false, - namedChunks: false, - nodeEnv: 'production', - flagIncludedChunks: true, - occurrenceOrder: true, - sideEffects: true, - usedExports: true, - concatenateModules: true, - splitChunks: { - hidePathInfo: true, - minSize: 30000, - maxAsyncRequests: 5, - maxInitialRequests: 3, - }, - noEmitOnErrors: true, - checkWasmTypes: true, - minimize: true, - }, - plugins: [ - new TerserPlugin(/* ... */), - new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }), - new webpack.optimize.ModuleConcatenationPlugin(), - new webpack.NoEmitOnErrorsPlugin() - ] }
تمام این ها خصوصیات و پلاگین هایی هستند که در حالت production فعال می شوند. البته نیازی نیست همه آن ها را یاد بگیریم. من فعلا کد خود را روی حالت production می گذارم. برای این کار به webpack.config.js بروید و مثل من عمل کنید:
mode: 'production',
یکی از کارهایی که حالت production می کند، فعالسازی ثابت خاصی به نام process.env.NODE_ENV و تعیین مقدار production برای آن است. بدین صورت می توانیم از برنامه خود به این مقدار دسترسی داشته باشیم و بفهمیم در حالت production یا development هستیم. برای تست این موضوع می توانید به index.js رفته و کد زیر را به آن اضافه کنید:
if (process.env.NODE_ENV === 'production') { console.log('Production mode'); } else if (process.env.NODE_ENV === 'development') { console.log('Development mode'); }
این کد ثابت process.env.NODE_ENV را بررسی کرده و اگر در حالت development بودیم عبارت development mode را در کنسول چاپ می کنیم، برعکس آن برای حالت production نیز صحیح است. یکی از تفاوت های بین حالت production و development نحوه بررسی و گزارش خطا ها است. برای تست این موضوع، متدی را صدا می زنم که وجود نداشته باشد تا به خطا برخورد کنیم:
if (process.env.NODE_ENV === 'production') { console.log('Production mode'); } else if (process.env.NODE_ENV === 'development') { console.log('Development mode'); } helloWorldButton.SomeMethod();
حالا دستور npm run build را اجرا کنید. سپس به مرورگر رفته و قسمت کنسول را باز کنید:
می بینید که علاوه بر چاپ عبارت production mode، خطای ما نیز برایمان نمایش داده شده است اما از آنجایی که تمام کدهای ما درون یک فایل bundle جمع شده است، فقط می بینیم که خط اول از این فایل دارای خطا است. مشکل اینجاست که کد ما در حالت production کوچک و minify می شود بنابراین تمام کد های ما در خط اول خواهد بود! متوجه مشکل شدید؟ امکان ندارد بفهمیم که خطا از کدام قسمت بوده است.
حالا به webpack.config.js برگشته و حالت برنامه را روی Development بگذارید:
mode: 'development',
سپس دوباره npm run build را اجرا کرده و به مرورگر برگردید:
همانطور که می بینید حالا مشخص شده است که خطای ما از خط 16 بوده است. این یکی از تفاوت های حالت production با development است. شما می توانید در وب سایت رسمی Webpack در این باره بیشتر مطالعه کنید.
در حال حاضر مشکل کوچکی داریم. هر بار که بخواهیم از برنامه در حالت development استفاده کنیم باید به فایل پیکربندی رفته و از آنجا mode را روی development قرار بدهیم، و برای رفتن به حالت production نیز باید همین پروسه را دوباره اجرا کنیم. این فرآیند کمی خسته کننده و وقت گیر است به همین دلیل webpack راه حلی برای این مشکل ارائه داده است. ما می توانیم دو فایل پیکربندی مختلف بسازیم که یکی برای production و دیگری برای Development باشند. بر این اساس فایل webpack.config.js را به webpack.production.config.js تغییر نام دهید. یک فایل دیگر به نام webpack.dev.config.js را نیز در کنار این فایل ایجاد کنید. سپس همه کد های فایل webpack.production.config.js را کپی کرده و درون این فایل قرار دهید.
من از فایل webpack.production.config.js شروع می کنم. اولین کار این است که mode را روی production بگذاریم. سپس از قسمت plugins کد مربوط به new TerserPlugin را حذف کنید چرا که در حالت production این پلاگین به صورت پیش فرض فعال می باشد (طبیعتا دستور require آن را نیز از ابتدای فایل حذف کنید). بقیه موارد این فایل مناسب حالت production هستند و به آن ها دست نمی زنیم، بنابراین کد نهایی این فایل به شکل زیر است:
const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const HtmlWebpackPlugin = require('html=webpack-plugin'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.[contenthash].js', path: path.resolve(__dirname, './dist'), publicPath: '' }, mode: 'production', module: { rules: [ { test: /\.(png|jpg)$/, use: [ { loader: 'file-loader', options: { name: '[name][contenthash:5].[ext]' } } ] }, { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, 'css-loader' ] }, { test: /\.scss$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader' ] }, { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/env'], plugins: ['transform-class-properties'] } } }, { test: /\.hbs$/, use: { 'handlebars-loader' } } ] }, plugins: [ new MiniCssExtractPlugin({ filename: 'styles.[contenthash].css' }), new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'Hello World', description: 'your description here', template: 'src/index.hbs' }) ] }
حالا نوبت webpack.dev.config.js است. ابتدا mode را روی development بگذارید. از طرفی می توانید contenthash را از قسمت filename (در output) حذف کنید چرا که در حالت توسعه نیازی به نگرانی برای browser caching نیست مگر آنکه در هنگام کدنویسی مرورگر شما مکررا کدهایتان را کش می کند. در چنین حالتی می توانید با ctrl + f5 از شر cache خلاص شوید اما اگر دوست دارید contenthash باقی بماند نیز مشکلی نیست. در مرحله بعد Terser را مانند فایل قبلی حذف کنید چرا که در حالت توسعه نیازی به minify کردن کد ها نیست، بلکه باعث سخت تر شدن debugging نیز می شود. همچنین در حالت توسعه نیازی نداریم که کدهای CSS را درون یک فایل واحد ادغام کنیم بنابراین MiniCssExtractPlugin را نیز حذف می کنم (هم از قسمت plugins و هم دستور require مربوط به آن). حالا که MiniCssExtractPlugin را حذف کرده ایم به قسمت rules مربوط به css و scss بروید و به جای آن از style-loader استفاده کنید. بنابراین محتویات این فایل به شکل زیر در می آید:
const path = require('path'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const HtmlWebpackPlugin = require('html=webpack-plugin'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, './dist'), publicPath: '' }, mode: 'development', module: { rules: [ { test: /\.(png|jpg)$/, use: [ { loader: 'file-loader', options: { name: '[name][contenthash:5].[ext]' } } ] }, { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.scss$/, use: [ 'style-loader', 'css-loader', 'sass-loader' ] }, { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/env'], plugins: ['transform-class-properties'] } } }, { test: /\.hbs$/, use: { 'handlebars-loader' } } ] }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'Hello World', description: 'your description here', template: 'src/index.hbs' }) ] }
با استفاده کمتر از پلاگین ها، فرآیند گرفتن خروجی از پروژه بسیار سریع تر می شود. البته در برنامه بسیار کوچک ما تغییر محسوسی را حس نخواهیم کرد اما در پروژه های بزرگ تر، تغییر واقعا قابل دیدن است.
از آنجایی که دو فایل پیکربندی جداگانه داریم باید دو دستور جداگانه برای Webpack تعریف کنیم تا هر بار بر اساس یکی از این فایل های پیکربندی کار کند. برای این کار به package.json بروید و مثل من عمل کنید:
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --config webpack.production.config.js", "dev": "webpack --config webpack.dev.config.js" },
در قسمت Scripts تعیین کرده ایم که فایل webpack.production.config.js مخصوص دستور build است بنابراین برای production از آن استفاده می کنیم (این کار با config-- انجام می شود). فایل webpack.dev.config.js را نیز برای حالت توسعه در نظر گرفته ایم. از این به بعد برای گرفتن خروجی در حالت production از دستور npm run build و برای گرفتن خروجی در حالت توسعه از دستور npm run dev استفاده خواهیم کرد.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.