بهینه‌سازی فرایند تحویل Front-End با Webpack4

گردآوری و تالیف : ارسطو عباسی
تاریخ انتشار : 01 مرداد 1397
دسته بندی ها : طراحی وب

با ارائه آخرین نسخه از وب‌پک یعنی نسخه 4.X ما دیگر برای شروع کار کردن با آن نیازی به انجام پیکربنی نداریم. این نسخه به صورت پیشفرض بهینه‌سازی شده است. بنابراین پلاگین‌هایی مانند CommonsChunkPlugin، UglifyjsWebpackPlugin و... که قبلا نیاز به نصب و پیکربندی دستی داشتند، حال به صورت خودکار پیکربندی می‌شوند. 

با این حال مواردی نیز وجود دارد که با پیکربندی آن‌ها از اینکه بیشترین استفاده را از وب‌پک می‌بریم مطمئن می‌شویم. بیایید آن‌ها را بررسی کنیم.

Mode: وب‌پک با دو حالت ارائه می‌شود: production و development. اجرای وب‌پک با پرچم --mode development|production به ما امکانات متفاوتی را ارائه می‌دهد:

در حالت Development

مقدار process.env.NODE_ENV را برابر development قرار می‌دهد. همچنین پلاگین‌های NamedChunksPlugin   و NamedModulesPlugin را فعال می‌کند. 

در حالت Production

مقدار process.env.NODE_ENV را برابر production قرار می‌دهد. همچنین پلاگین‌های FlagDependencyUsagePlugin، FlagIncludedChunksPlugin، ModuleConcatenationPlugin، NoEmitOnErrorsPlugin، OccurrenceOrderPlugin، SideEffectsFlagPlugin و UglifyJsPlugin را فعال می‌کند. 

بنابراین نیازی نیست که برای فعال‌سازی این پلاگین‌ها یا انتساب مقدار NODE_ENV با استفاده از DefinePlugin به صورت دستی روند را پیش‌ ببریم. تمام موارد از طریق mode انجام می‌شود.

با این حال اگر می‌خواهید روندی سفارشی را در پلاگینی مانند UglifyJsPlugin پیش ببرید، می‌توانید این کار را با نصب کردن با استفاده از NPM انجام داده و سپس پارامترهای سفارشی را وارد کنید:

npm install uglifyjs-webpack-plugin --save-dev

به این صورت می‌توانید پارامترهای سفارشی را در کانفیگ Webpack قرار دهید:

const UglifyJsPlugin = require('uglifyjs-webpack-plugin');  

if (process.env.NODE_ENV === 'production') {  
  config.optimization = {  
    minimizer: [  
      new UglifyJsPlugin({  
        parallel: true,  
        cache: true,  
        sourceMap: true,  
        uglifyOptions: {  
          compress: {  
            drop_console: true  
          }  
        },  
      }),  
    ],  
  };  
}

این قطعه کد جایگزین نمونه پیشفرضی که خود وب‌پک تنظیم کرده است می‌شود. بنابراین شما کنترل کاملی روی آن خواهید داشت. 

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

هش‌ها

به صورت پیشفرض وب‌پک هش مربوط به بیلدها را در خروجی نام آن‌ها قرار نمی‌دهد )به عنوان مثال هش index.7eeea311f7235e3b9a17.js(. بنابراین با این کار وقتی بیلد جدیدی ایجاد شود کاربران‌تان می‌تواند آخرین نسخه را دریافت کنند. این باعث بوجود امدن باگ‌ها و رفتارهای عجیب و غریبی می‌شود. 

بنابراین در جهت نوسازی تمام موارد جانبی در کدها بعد از هر بیلد، می‌توانید یک هش را به نام فایل‌ها اضافه کنید:

module.exports = {
  entry: {
    vendor: './src/vendor.js',
    main: './src/index.js'
  },
  output: {
    path: path.join(__dirname, 'build'),
    filename: '[name].[hash].js'
  }
};

البته اگر به این موضوع فکر کنید ممکن است کمی پیچیده به نظر برسد. اما با این حال می‌توانید وب‌پک را متوجه سازید که تنها تغییرات را بروزرسانی کند. بنابراین کاربران دیگر لازم ندارند که تمام موارد جانبی را باری دیگر دانلود نمایند بلکه تنها قسمت‌های تغییریافته را دانلود می‌کنند. 

برای اینکه از چنین موضوعی مطمئن شوید، وب‌پک chunkhash را به شما پیشنهاد می‌کند. chunkhash بجای اینکه روی تمام محتوای فایل تمرکز داشته باشد تنها روی محتوای مربوط به هر قسمت از داده تمرکز دارد. برای استفاده از آن به سادگی زیر عمل کنید:

module.exports = {
  ...
  output: {
    ...
    filename: '[name].[chunkhash].js'
  }
};

با این کار کاربران تنها تغییرات جدید را دانلود می‌کنند و دیگر قسمت‌های تغییر نیافته برای‌شان از کش اجرا می‌شود.

Babel

از آنجایی که تمام مرورگرها ویژگی‌های ES6/7/8 را پشتیبانی نمی‌کنند، بنابراین باید یک نقطه امن برای فرایند توسعه را بوجود بیاوریم. 

منظور از این نقطه امن Babel است. با استفاده از این تکنولوژی می‌توانید ویژگی‌های جدید جاوااسکریپت را به چیزی که تمام مرورگرها بتوانند آن را درک کنند تبدیل کنید. 

babel

می‌توانید آن را با دستور زیر نصب کنید:

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

می‌توانید به Babel بگویید که اپلیکیشن ما روی چه مرورگرهایی اجرا خواهد شد. برای اینکار وارد فایل .babelrc در پوشه روت پروژه‌تان شوید:

{  
  "presets": [  
    ["env", {  
      "targets": {  
        "browsers": ["last 2 versions", "safari >= 9"]  
      }  
    }]  
  ]
}

این کار از طریق env preset انجام می‌شود. 

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

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {  
            cacheDirectory: true  
          }
        }
      }
    ]
  }
};

حال شما بدون هیچ نگرانی می‌توانید از آخرین ویژگی‌های جاوااسکریپت بدون مشکل استفاده کنید.

Dynamic import

مزیت بعدی که از Babel خواهیم برد،‌ مربوط به کارایی آن است. می‌توانیم از پلاگین Dynamic import برای بارگذاری حجم زیادی از الزامات برنامه استفاده کنیم. این الزامات تنها زمانی که لازم به استفاده باشند بارگذاری می‌شوند -lazy loading-. این پلاگین می‌تواند تاثیر بسیار  زیادی روی کارایی یک قسمت از برنامه داشته باشد، بنابراین وب‌پک مجبور نیست که برای یک برنامه، تمام الزامات موجود را بارگذاری کند.

می‌توانید این پلاگین را از طریق دستور زیر نصب کنید:

npm install syntax-dynamic-import --save-dev

پس از آن می‌توانید به فایل .babelrc پلاگین را اضافه کنید:

{  
  "presets": [  
    ...
  ]
  "plugins": ["syntax-dynamic-import"]
}

حال یک ماژول مانند زیر:

import foo from 'foo'
import bar from 'bar'
import baz from 'baz'

const myfun = () => {  
  //Do something with the modules here  
}

به ماژول زیر تبدیل می‌شود:

const myfun = () => {  
  return Promise.all([  
    import('foo'),  
    import('bar'),  
    import('baz'),  
  ]).then(([foo, bar, baz]) => {  
    //Do something with the modules here  
  });  
};

وب‌پک می‌تواند importهای پویا را شناسایی کند و کد را در قسمت‌های مختلف قرار می‌دهد. این importها زمانی فراخوانی می‌شود که شما تابع myfun را اجرا کنید. این کار به ما این اطمینان را می‌دهد که برنامه در ابتدای اجرا حجم کمی دارد و تنها در زمانی که نیاز به یک ابزار باشد آن فراخوانی می‌شود. 

اگر از ویوجی‌اس استفاده کنید چنین قابلیتی به صورت پیشفرض تحت عنوان Async Components پشتیبانی می‌شود. 

Preload

قبلا گفتیم که وب‌پک برنامه را به قطعات کوچکتری تبدیل می‌کند و در آن‌ها لزومات را نصب می‌کنیم. حال در این قسمت نیاز است که این قطعه کدها را بهینه‌سازی کنیم. یکی از معایب روش Dynamic import این است که سرعت واکنش‌پذیری را بسیار کاهش می‌دهد. در مثال بالا، وقتی که قصد فراخوانی myfun را داشته باشیم، کلاینت باید کتابخانه‌های foo, bar و baz را قبل از اجرای تابع فراخوانی کند. 

نظرتان چیست اگر بتوانیم این لزومات را به صورت پس‌زمینه‌ای فراهم کنیم؟ با این کار هرگاه که کلاینت تابع myfun را فراخوانی کند، همه لزومات برای اجرا موجود هستند. برای چنین کاری نیاز است که از پلاگین preload استفاده کنیم. 

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

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

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

برای اضافه کردن به کانفیگ وب‌پک به صورت زیر عمل کنید:

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

plugins: [
  new HtmlWebpackPlugin(),
  new PreloadWebpackPlugin({
    rel: 'preload',
    include: 'asyncChunks'
  })
]

به همین سادگی! حال تمام قطعات کدی که وب‌پک ساخته است به HTML در حالت preload اضافه می‌شود:

<link rel="preload" as="script" href="chunk.31132ae6680e598f8879.js">
<link rel="preload" as="script" href="chunk.d15e7fdfc91b34bb78c4.js">
<link rel="preload" as="script" href="chunk.acd07bf4b982963ba814.js">

در وب‌پک نسخه ۴.۶ و نسخه‌های بالاتر تمام این مراحل به صورت خودکار انجام می‌شود و جزو ویژگی‌های داخلی وب‌پک به حساب می‌آید. وقتی که یک فرایند import را انجام می‌دهید در کنار آن می‌توانید به صورت دستی preload شدن را تعیین کنید.

بنابراین تنها تغییری که باید انجام دهید این است که بجای:

import("foo");
import("bar");

کد زیر را قرار دهید:

import(/* webpackPrefetch: true */ "foo");
import(/* webpackPreload: true */ "bar");

با این کار تمام روندPreload/Prefetch کردن به صورت خودکار انجام می‌شود.

یک موضوع دیگر: به اینکه از Preload یا Prefetch استفاده می‌کنید دقت کنید:

Preload مربوط به محتوایی می‌شود که شما اعتماد کامل به اینکه در برگه کنونی استفاده می‌شود دارید. اما Prefetch مربوط به منابعی می‌شود که قرار است در آینده و یا در قسمت‌های بعدی اجرا شود.

آنالیزرها

تا اینجای کار به چند راه برای بهینه‌سازی وب‌پک نگاه کردیم، حال قصد داریم یک حالت نظاره‌گر روی کدها اعمال کنیم که متوجه شویم لزومات و کدها در وضعیتی بهینه قرار دارند یا خیر. دو مورد از ابزارهای مورد علاقه من را می‌توانید در زیر مشاهده کنید:

Webpack Bundle Analyzer 

می‌توانید این ابزار را با دستور زیر نصب کنید:

npm install --save-dev webpack-bundle-analyzer

بعد از آن کانفیگ وب‌پک را به صورت تنظیم کنید:

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

if (process.env.NODE_ENV !== 'production') {
  config.plugins.push(new BundleAnalyzerPlugin())
}

اگر دفعه بعدی webpack-dev-server را در حالت development اجرا کردید، می‌توانید آدرس http://localhost:8888 را برای مشاهده ابزار باز کنید. 

Webpack Monitor

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

می‌توانید این ابزار را با دستور زیر نصب کنید:

npm install --save-dev webpack-monitor

بعد از آن کانفیگ وب‌پک را به صورت تنظیم کنید:

const WebpackMonitor = require('webpack-monitor');

// ...

plugins: [
  new WebpackMonitor({
    capture: true, // -> default 'true'
    target: '../monitor/myStatsStore.json', // default -> '../monitor/stats.json'
    launch: true, // -> default 'false'
    port: 3030, // default -> 8081
    excludeSourceMaps: true // default 'true'
  }),
],

می‌توانید این ابزار را نیز مانند مورد قبلی در حالت development اجرا کرده و تغییرات را در مرورگر مشاهده کنید.

در پایان

امیدوارم با استفاده از این تکنیک‌ها و دیگر موارد بتوایند سایز باندل‌ها را کم کنید و کارایی را افزایش دهید. راهکارهای دیگری را نیز سراغ دارید؟ می‌توانید آن‌ها را با ما به اشتراک بگذارید.

منبع

مقالات پیشنهادی

حس اتوماتیک سازی کارهای front-end با gulp

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

بازاریابی محتوای موبایلی

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

اکوسیستم محتوا در بازاریابی و فروش

اکوسیستم به معنی محیط‌زیست یک منطقه و تمام موجوداتی است که در آن زندگی و از آن محافظت می‌کنند. درخت شاخه‌هایی دارد که پرندگان روی آن‌ها زندگی کرده و ا...

بازاریابی محتوا در نمایشگاه ها

بازاریابی محتوا یکی از مهم‌ترین شاخه‌های بازاریابی است که در سالیان اخیر از سوی شرکت‌ها و کسب و کارها مورد استقبال گسترده قرار گرفته است. اگرچه اهمیت...