الاستفادة من التخزين المؤقّت الطويل المدى

كيفية مساعدة webpack في ميزة تخزين مواد العرض مؤقتًا

بعد تحسين حجم التطبيق، يمكن تحسين وقت تحميل التطبيق من خلال ميزة التخزين المؤقت. يمكنك استخدامها لحفظ أجزاء من التطبيق على العميل وتجنُّب إعادة تنزيلها في كل مرة.

استخدام عناوين ذاكرة التخزين المؤقت وإصدار الحِزمة

النهج الشائع لتنفيذ التخزين المؤقت هو:

  1. إخبار المتصفّح بتخزين ملف في ذاكرة التخزين المؤقت لفترة طويلة جدًا (مثلاً، سنة):

    # Server header
    Cache-Control: max-age=31536000
    

    إذا لم تكن على دراية بوظائف Cache-Control، يمكنك الاطّلاع على مقالة Jake Archibald الممتازة حول أفضل ممارسات التخزين المؤقت.

  2. وإعادة تسمية الملف عند تغييره لفرض إعادة التنزيل:

    <!-- Before the change -->
    <script src="./index-v15.js"></script>
    
    <!-- After the change -->
    <script src="./index-v16.js"></script>
    

يطلب هذا الأسلوب من المتصفّح تنزيل ملف JS وتخزينه مؤقتًا واستخدام النسخة المخزّنة مؤقتًا. لن يتصل المتصفّح بالشبكة إلا إذا تغيّر اسم الملف (أو بعد مرور عام).

باستخدام webpack، يمكنك إجراء الإجراء نفسه، ولكن بدلاً من رقم الإصدار، يمكنك تحديد تجزئة الملف. لتضمين القيمة التجزئية في اسم الملف، استخدِم رمز العميل [chunkhash]:

// webpack.config.js
module.exports = {
  entry: './index.js',
  output: {
    filename: 'bundle.[chunkhash].js' // → bundle.8e0d62a03.js
  }
};

إذا كنت بحاجة إلى اسم الملف لإرساله إلى العميل، استخدِم HtmlWebpackPlugin أو WebpackManifestPlugin.

HtmlWebpackPlugin هو نهج بسيط، ولكنه أقل مرونة. أثناء عملية الترجمة، ينشئ هذا المكوّن الإضافي ملف HTML يتضمّن جميع الموارد المجمّعة. إذا لم يكن منطق الخادم معقدًا، من المفترض أن يكون كافيًا لك:

<!-- index.html -->
<!DOCTYPE html>
<!-- ... -->
<script src="bundle.8e0d62a03.js"></script>

يُعدّ الرمز المميّز WebpackManifestPlugin أكثر مرونة، وهو مفيد إذا كان لديك جزء خادم معقّد. أثناء عملية الإنشاء، يتم إنشاء ملف JSON يتضمّن تعيينًا بين أسماء الملفات بدون التجزئة وأسماء الملفات التي تتضمّن التجزئة. استخدِم ملف JSON هذا على الخادم لمعرفة الملف الذي تريد العمل معه:

// manifest.json
{
  "bundle.js": "bundle.8e0d62a03.js"
}

مراجع إضافية

استخراج التبعيات ووقت التشغيل في ملف منفصل

التبعيات

وعادةً ما تتغيّر متطلّبات التطبيق بمعدل أقل من رمز التطبيق الفعلي. وإذا نقلتها إلى ملف منفصل، سيتمكّن المتصفّح من تخزينها مؤقتًا بشكل منفصل، ولن يعيد تنزيلها في كل مرة يتغيّر فيها رمز التطبيق.

لاستخراج التبعيات في قطعة منفصلة، عليك اتّباع ثلاث خطوات:

  1. استبدِل اسم ملف الإخراج بـ [name].[chunkname].js:

    // webpack.config.js
    module.exports = {
      output: {
        // Before
        filename: 'bundle.[chunkhash].js',
        // After
        filename: '[name].[chunkhash].js'
      }
    };
    

    عندما ينشئ Webpack التطبيق، يستبدل [name] باسم قطعة. إذا لم نضيف الجزء [name]، علينا التمييز بين الأجزاء حسب علامة التجزئة الخاصة بها، وهو أمر صعب جدًا.

  2. حوِّل الحقل entry إلى عنصر:

    // webpack.config.js
    module.exports = {
      // Before
      entry: './index.js',
      // After
      entry: {
        main: './index.js'
      }
    };
    

    في هذا المقتطف، "main" هو اسم قطعة. سيتم استبدال هذا الاسم بدلاً من [name] من الخطوة 1.

    في الوقت الحالي، إذا أنشأت التطبيق، سيتضمّن هذا الجزء رمز التطبيق بالكامل، تمامًا كما لو لم ننفِّذ هذه الخطوات. ولكن سيتم تغيير ذلك بعد قليل.

  3. في webpack 4، أضِف الخيار optimization.splitChunks.chunks: 'all' إلى إعدادات webpack:

    // webpack.config.js (for webpack 4)
    module.exports = {
      optimization: {
        splitChunks: {
          chunks: 'all'
        }
      }
    };
    

    يُفعِّل هذا الخيار ميزة "تقسيم الرموز الذكية". باستخدام هذه الميزة، ستستخرج أداة webpack رمز المورّد إذا زاد حجمه عن 30 كيلوبايت (قبل التصغير واستخدام gzip). ستستخرج الأداة أيضًا الرمز المشترَك، وهو أمر مفيد إذا كان الإصدار ينتج عدة حِزم (مثل في حال تقسيم تطبيقك إلى مسارات).

    في webpack 3، أضِف CommonsChunkPlugin:

    // webpack.config.js (for webpack 3)
    module.exports = {
      plugins: [
        new webpack.optimize.CommonsChunkPlugin({
        // A name of the chunk that will include the dependencies.
        // This name is substituted in place of [name] from step 1
        name: 'vendor',
    
        // A function that determines which modules to include into this chunk
        minChunks: module => module.context && module.context.includes('node_modules'),
        })
      ]
    };
    

    يأخذ هذا المكوّن الإضافي جميع الوحدات التي تتضمّن node_modules وينقلها إلى ملف منفصل يُسمى vendor.[chunkhash].js.

بعد إجراء هذه التغييرات، سينشئ كلّ إصدار ملفّين بدلاً من ملفّ واحد: main.[chunkhash].js و vendor.[chunkhash].js (vendors~main.[chunkhash].js لاستخدام webpack 4). في حال استخدام webpack 4، قد لا يتم إنشاء حِزمة المورّد إذا كانت التبعيات صغيرة، وهذا أمر جيد:

$ webpack
Hash: ac01483e8fec1fa70676
Version: webpack 3.8.1
Time: 3816ms
                        Asset      Size  Chunks             Chunk Names
 ./main.00bab6fd3100008a42b0.js   82 kB       0  [emitted]  main
./vendor.d9e134771799ecdf9483.js  47 kB       1  [emitted]  vendor

سيخزّن المتصفّح هذه الملفات بشكل منفصل، ولن يُعيد تنزيل سوى الرمز البرمجي الذي يطرأ عليه تغيير.

رمز Webpack أثناء التشغيل

لسوء الحظ، لا يكفي استخراج رمز المورّد فقط. إذا حاولت تغيير أي شيء في رمز التطبيق:

// index.js
…
…

// E.g. add this:
console.log('Wat');

ستلاحظ أنّ تجزئة vendor تتغيّر أيضًا:

                           Asset   Size  Chunks             Chunk Names
./vendor.d9e134771799ecdf9483.js  47 kB       1  [emitted]  vendor

                            Asset   Size  Chunks             Chunk Names
./vendor.e6ea4504d61a1cc1c60b.js  47 kB       1  [emitted]  vendor

ويحدث ذلك لأنّ حِزمة webpack، بالإضافة إلى رمز الوحدات، تتضمّن مرحلت تنفيذ، وهي عبارة عن قطعة صغيرة من الرمز المبرمَج تدير تنفيذ الوحدة. عند تقسيم الرمز البرمجي إلى ملفات متعددة، تبدأ هذه القطعة من الرمز البرمجي في تضمين تعيين بين أرقام تعريف المقاطع والملفّات المقابلة:

// vendor.e6ea4504d61a1cc1c60b.js
script.src = __webpack_require__.p + chunkId + "." + {
    "0": "2f2269c7f0a55a5c1871"
}[chunkId] + ".js";

يُدرِج Webpack وقت التشغيل هذا في آخر قطعة تم إنشاؤها، وهي vendor في حالتنا. وفي كل مرة يتغيّر فيها أيّ جزء، تتغيّر هذه القطعة من الرمز البرمجي أيضًا، مما يؤدي إلى تغيُّر الجزء vendor بأكمله.

لحلّ هذه المشكلة، لننقل وقت التشغيل إلى ملف منفصل. في webpack 4، يتم تحقيق ذلك من خلال تفعيل الخيار optimization.runtimeChunk:

// webpack.config.js (for webpack 4)
module.exports = {
  optimization: {
    runtimeChunk: true
  }
};

في webpack 3، يمكنك إجراء ذلك من خلال إنشاء مجموعة إضافية فارغة باستخدام CommonsChunkPlugin:

// webpack.config.js (for webpack 3)
module.exports = {
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks: module => module.context && module.context.includes('node_modules')
    }),
    // This plugin must come after the vendor one (because webpack
    // includes runtime into the last chunk)
    new webpack.optimize.CommonsChunkPlugin({
      name: 'runtime',
      // minChunks: Infinity means that no app modules
      // will be included into this chunk
      minChunks: Infinity
    })
  ]
};

بعد إجراء هذه التغييرات، سينشئ كل إصدار ثلاثة ملفات:

$ webpack
Hash: ac01483e8fec1fa70676
Version: webpack 3.8.1
Time: 3816ms
                            Asset     Size  Chunks             Chunk Names
   ./main.00bab6fd3100008a42b0.js    82 kB       0  [emitted]  main
 ./vendor.26886caf15818fa82dfa.js    46 kB       1  [emitted]  vendor
./runtime.79f17c27b335abc7aaf4.js  1.45 kB       3  [emitted]  runtime

أدرِج هذه العناصر في index.html بالترتيب العكسي، وانتهت المهمة:

<!-- index.html -->
<script src="./runtime.79f17c27b335abc7aaf4.js"></script>
<script src="./vendor.26886caf15818fa82dfa.js"></script>
<script src="./main.00bab6fd3100008a42b0.js"></script>

مراجع إضافية

تضمين وقت تشغيل webpack لتوفير طلب HTTP إضافي

لتحسين الأداء، جرِّب تضمين وقت تشغيل webpack في ملف HTML المستلَم. أي بدلاً من هذا:

<!-- index.html -->
<script src="./runtime.79f17c27b335abc7aaf4.js"></script>

اتّبِع الخطوات التالية:

<!-- index.html -->
<script>
!function(e){function n(r){if(t[r])return t[r].exports;…}} ([]);
</script>

وقت التشغيل صغير، وسيساعدك تضمين المحتوى في توفير طلب HTTP (مهم جدًا مع HTTP/1، وأقل أهمية مع HTTP/2، ولكن قد يظل له تأثير).

إليك كيفية إجراء ذلك.

في حال إنشاء صفحات HTML باستخدام HtmlWebpackPlugin

إذا كنت تستخدم HtmlWebpackPlugin لإنشاء ملف HTML، كل ما تحتاجه هو InlineSourcePlugin:

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

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      inlineSource: 'runtime~.+\\.js',
    }),
    new InlineSourcePlugin()
  ]
};

إذا كنت تنشئ صفحات HTML باستخدام منطق خادم مخصّص

مع webpack 4:

  1. أضِف الرمز WebpackManifestPlugin لمعرفة الاسم الذي تم إنشاؤه للقطعة في وقت التشغيل:

    // webpack.config.js (for webpack 4)
    const ManifestPlugin = require('webpack-manifest-plugin');
    
    module.exports = {
      plugins: [
        new ManifestPlugin()
      ]
    };
    

    سيؤدي إنشاء إصدار باستخدام هذا المكوّن الإضافي إلى إنشاء ملف بالشكل التالي:

    // manifest.json
    {
      "runtime~main.js": "runtime~main.8e0d62a03.js"
    }
    
  2. تضمين محتوى القطعة في وقت التشغيل بطريقة ملائمة على سبيل المثال، باستخدام Node.js وExpress:

    // server.js
    const fs = require('fs');
    const manifest = require('./manifest.json');
    const runtimeContent = fs.readFileSync(manifest['runtime~main.js'], 'utf-8');
    
    app.get('/', (req, res) => {
      res.send(`
        …
        <script>${runtimeContent}</script>
        …
      `);
    });
    

أو باستخدام webpack 3:

  1. اجعل اسم وقت التشغيل ثابتًا من خلال تحديد filename:

    module.exports = {
      plugins: [
        new webpack.optimize.CommonsChunkPlugin({
          name: 'runtime',
          minChunks: Infinity,
          filename: 'runtime.js'
        })
      ]
    };
    
  2. تضمين محتوى runtime.js بطريقة ملائمة على سبيل المثال، باستخدام Node.js وExpress:

    // server.js
    const fs = require('fs');
    const runtimeContent = fs.readFileSync('./runtime.js', 'utf-8');
    
    app.get('/', (req, res) => {
      res.send(`
        …
        <script>${runtimeContent}</script>
        …
      `);
    });
    

رمز التحميل البطيء الذي لا تحتاج إليه الآن

في بعض الأحيان، تحتوي الصفحة على أجزاء أكثر أهمية وأجزاء أقل أهمية:

  • إذا حمّلت صفحة فيديو على YouTube، يعني ذلك أنّك مهتم بالفيديو أكثر من التعليقات. في هذه الحالة، يكون الفيديو أكثر أهمية من التعليقات.
  • إذا فتحت مقالة على موقع إلكتروني إخباري، يعني ذلك أنّك مهتم بنص المقالة أكثر من الإعلانات. في هذه الحالة، يكون النص أكثر أهمية من الإعلانات.

في هذه الحالات، يمكنك تحسين أداء التحميل الأولي من خلال تنزيل المحتوى الأكثر أهمية أولاً، وتحميل الأجزاء المتبقية لاحقًا باستخدام ميزة "التحميل البطيء". استخدِم دالة import() وتقسيم الرمز البرمجي لإجراء ذلك:

// videoPlayer.js
export function renderVideoPlayer() { … }

// comments.js
export function renderComments() { … }

// index.js
import {renderVideoPlayer} from './videoPlayer';
renderVideoPlayer();

// …Custom event listener
onShowCommentsClick(() => {
  import('./comments').then((comments) => {
    comments.renderComments();
  });
});

يحدِّد import() أنّك تريد تحميل وحدة معيّنة بشكل ديناميكي. عندما يرصد webpack الرمز import('./module.js')، ينقل هذه الوحدة إلى قطعة منفصلة:

$ webpack
Hash: 39b2a53cb4e73f0dc5b2
Version: webpack 3.8.1
Time: 4273ms
                            Asset     Size  Chunks             Chunk Names
      ./0.8ecaf182f5c85b7a8199.js  22.5 kB       0  [emitted]
   ./main.f7e53d8e13e9a2745d6d.js    60 kB       1  [emitted]  main
 ./vendor.4f14b6326a80f4752a98.js    46 kB       2  [emitted]  vendor
./runtime.79f17c27b335abc7aaf4.js  1.45 kB       3  [emitted]  runtime

ولا يتم تنزيله إلا عندما يصل التنفيذ إلى الدالة import().

سيؤدي ذلك إلى تصغير حزمة main، ما يُحسِّن من وقت التحميل الأوّلي. بالإضافة إلى ذلك، سيؤدي ذلك إلى تحسين التخزين المؤقت. فإذا غيّرت الرمز في الجزء الرئيسي، لن يتأثّر ملف التعليقات.

مراجع إضافية

تقسيم الرمز إلى مسارات وصفحات

إذا كان تطبيقك يتضمّن صفحات أو مسارات متعددة، ولكن لا يتضمّن سوى ملف JS واحد يحتوي على الرمز البرمجي (قطعة واحدة من main)، من المرجّح أنك تعرض وحدات بايت إضافية في كل طلب. على سبيل المثال، عندما يزور أحد المستخدِمين الصفحة الرئيسية لموقعك الإلكتروني:

صفحة رئيسية في &quot;أساسيات الويب&quot;

ولن يحتاجوا إلى تحميل الرمز لعرض مقالة على صفحة مختلفة، ولكن سيتم تحميله. بالإضافة إلى ذلك، إذا كان المستخدم يزور دائمًا الصفحة الرئيسية فقط، وأجريت تغييرًا في رمز المقالة، سيبطل Webpack الحزمة بأكملها، وسيضطر المستخدم إلى إعادة تنزيل التطبيق بأكمله.

إذا قسمنا التطبيق إلى صفحات (أو مسارات، إذا كان تطبيقًا مكوّنًا من صفحة واحدة)، لن ينزّل المستخدم سوى الرمز برمجي المعني. بالإضافة إلى ذلك، سيخزِّن المتصفّح رمز التطبيق في ذاكرة التخزين المؤقت بشكلٍ أفضل: إذا غيّرت رمز الصفحة الرئيسية، لن تلغي أداة webpack سوى المقطع المعني.

بالنسبة إلى التطبيقات من صفحة واحدة

لتقسيم التطبيقات المكوّنة من صفحة واحدة حسب المسارات، استخدِم import() (اطّلِع على قسم "رمز التحميل البطيء الذي لا تحتاج إليه الآن"). إذا كنت تستخدم إطار عمل، قد يتضمّن حلًا حاليًا لهذه المشكلة:

بالنسبة إلى التطبيقات التقليدية المتعدّدة الصفحات

لتقسيم التطبيقات التقليدية حسب الصفحات، استخدِم نقاط الدخول في webpack. إذا كان تطبيقك يتضمّن ثلاثة أنواع من الصفحات: الصفحة الرئيسية وصفحة المقالة وصفحة حساب المستخدم، يجب أن يتضمّن ثلاثة إدخالات:

// webpack.config.js
module.exports = {
  entry: {
    home: './src/Home/index.js',
    article: './src/Article/index.js',
    profile: './src/Profile/index.js'
  }
};

لكل ملف إدخال، سينشئ Webpack شجرة تبعية منفصلة وينشئ حِزمة تتضمّن فقط الوحدات التي يستخدمها هذا الإدخال:

$ webpack
Hash: 318d7b8490a7382bf23b
Version: webpack 3.8.1
Time: 4273ms
                            Asset     Size  Chunks             Chunk Names
      ./0.8ecaf182f5c85b7a8199.js  22.5 kB       0  [emitted]
   ./home.91b9ed27366fe7e33d6a.js    18 kB       1  [emitted]  home
./article.87a128755b16ac3294fd.js    32 kB       2  [emitted]  article
./profile.de945dc02685f6166781.js    24 kB       3  [emitted]  profile
 ./vendor.4f14b6326a80f4752a98.js    46 kB       4  [emitted]  vendor
./runtime.318d7b8490a7382bf23b.js  1.45 kB       5  [emitted]  runtime

وبالتالي، إذا كانت صفحة المقالة فقط هي التي تستخدم Lodash، لن تتضمّن حِزم home وprofile هذه المكتبة، ولن يحتاج المستخدم إلى تنزيل هذه المكتبة عند زيارة الصفحة الرئيسية.

ومع ذلك، فإنّ أشجار التبعية المنفصلة لها عيوبها. إذا كانت نقطتا دخول تستخدمان Lodash ولم تنقل تبعاتك إلى حِزمة موفِّر، ستتضمّن كلتا نقطتَي الدخل نسخة من Lodash. لحلّ هذه المشكلة، في webpack 4، أضِف الخيار optimization.splitChunks.chunks: 'all' إلى إعدادات webpack:

// webpack.config.js (for webpack 4)
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  }
};

يُفعِّل هذا الخيار ميزة "تقسيم الرموز الذكية". باستخدام هذا الخيار، سيبحث Webpack تلقائيًا عن الرمز المشترَك ويستخرجه إلى ملفات منفصلة.

أو في webpack 3، استخدِم CommonsChunkPlugin – سيؤدي ذلك إلى نقل الملحقات الشائعة إلى ملف جديد محدّد:

module.exports = {
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: 'common',
      minChunks: 2    // 2 is the default value
    })
  ]
};

يمكنك تغيير قيمة minChunks للعثور على القيمة الأفضل. بشكل عام، يُفضَّل إبقاء حجمها صغيرًا، ولكن يجب زيادتها إذا زاد عدد الأجزاء. على سبيل المثال، إذا كانت هناك 3 أجزاء، قد يكون minChunks هو 2، ولكن إذا كانت هناك 30 جزءًا، قد يكون minChunks هو 8، لأنّه في حال إبقاءه على 2، ستتم إضافة عدد كبير جدًا من الوحدات إلى الملف المشترك، مما يؤدي إلى تضخيم حجمه بشكل كبير.

مراجع إضافية

جعل أرقام تعريف الوحدات أكثر ثباتًا

عند إنشاء الرمز، يخصّص Webpack رقم تعريف لكل وحدة. وفي وقت لاحق، يتم استخدام هذه المعرّفات في require() داخل الحِزمة. تظهر لك عادةً أرقام التعريف في ناتج عملية الإنشاء قبل مسارات الوحدات مباشرةً:

$ webpack
Hash: df3474e4f76528e3bbc9
Version: webpack 3.8.1
Time: 2150ms
                           Asset      Size  Chunks             Chunk Names
      ./0.8ecaf182f5c85b7a8199.js  22.5 kB       0  [emitted]
   ./main.4e50a16675574df6a9e9.js    60 kB       1  [emitted]  main
 ./vendor.26886caf15818fa82dfa.js    46 kB       2  [emitted]  vendor
./runtime.79f17c27b335abc7aaf4.js  1.45 kB       3  [emitted]  runtime

↓ هنا

[0] ./index.js 29 kB {1} [built]
[2] (webpack)/buildin/global.js 488 bytes {2} [built]
[3] (webpack)/buildin/module.js 495 bytes {2} [built]
[4] ./comments.js 58 kB {0} [built]
[5] ./ads.js 74 kB {1} [built]
+ 1 hidden module

يتم احتساب الأرقام التعريفية تلقائيًا باستخدام مُعدّل (أي أنّ الوحدة الأولى لها رقم التعريف 0، والوحدة الثانية لها رقم التعريف 1، وهكذا). تكمن المشكلة في أنّه عند إضافة وحدة جديدة، قد تظهر في منتصف قائمة الوحدات، ما يؤدي إلى تغيير جميع أرقام تعريف الوحدات التالية:

$ webpack
Hash: df3474e4f76528e3bbc9
Version: webpack 3.8.1
Time: 2150ms
                           Asset      Size  Chunks             Chunk Names
      ./0.5c82c0f337fcb22672b5.js    22 kB       0  [emitted]
   ./main.0c8b617dfc40c2827ae3.js    82 kB       1  [emitted]  main
 ./vendor.26886caf15818fa82dfa.js    46 kB       2  [emitted]  vendor
./runtime.79f17c27b335abc7aaf4.js  1.45 kB       3  [emitted]  runtime
   [0] ./index.js 29 kB {1} [built]
   [2] (webpack)/buildin/global.js 488 bytes {2} [built]
   [3] (webpack)/buildin/module.js 495 bytes {2} [built]

↓ لقد أضفنا ملفًا برمجيًا جديدًا…

[4] ./webPlayer.js 24 kB {1} [built]

↓ ونريد أن نشارك معك النتائج التي حقّقناها. أصبح لدى comments.js الآن رقم التعريف 5 بدلاً من 4.

[5] ./comments.js 58 kB {0} [built]

↓ أصبح لدى ads.js الآن المعرّف 6 بدلاً من 5.

[6] ./ads.js 74 kB {1} [built]
       + 1 hidden module

يؤدي ذلك إلى إلغاء صلاحية جميع الأجزاء التي تتضمّن وحدات ذات معرّفات تم تغييرها أو تعتمد عليها، حتى إذا لم يتغيّر رمزها الفعلي. في حالتنا، يتم إبطال صحة قطعة 0 (القطعة التي تحتوي على comments.js) وقطعة main (القطعة التي تحتوي على رمز التطبيق الآخر)، في حين كان من المفترض أن يتم إبطال صحة قطعة main فقط.

لحلّ هذه المشكلة، يمكنك تغيير طريقة احتساب أرقام تعريف الوحدات باستخدام HashedModuleIdsPlugin. ويحلّ محل المعرّفات المستندة إلى العدّة مجموعات تشفيرية لمسارات الوحدات:

$ webpack
Hash: df3474e4f76528e3bbc9
Version: webpack 3.8.1
Time: 2150ms
                           Asset      Size  Chunks             Chunk Names
      ./0.6168aaac8461862eab7a.js  22.5 kB       0  [emitted]
   ./main.a2e49a279552980e3b91.js    60 kB       1  [emitted]  main
 ./vendor.ff9f7ea865884e6a84c8.js    46 kB       2  [emitted]  vendor
./runtime.25f5d0204e4f77fa57a1.js  1.45 kB       3  [emitted]  runtime

↓ هنا

[3IRH] ./index.js 29 kB {1} [built]
[DuR2] (webpack)/buildin/global.js 488 bytes {2} [built]
[JkW7] (webpack)/buildin/module.js 495 bytes {2} [built]
[LbCc] ./webPlayer.js 24 kB {1} [built]
[lebJ] ./comments.js 58 kB {0} [built]
[02Tr] ./ads.js 74 kB {1} [built]
    + 1 hidden module

باستخدام هذا النهج، لا يتغيّر معرّف الوحدة إلا في حال إعادة تسمية تلك الوحدة أو نقلها. لن تؤثّر الوحدات الجديدة في أرقام تعريف الوحدات الأخرى.

لتفعيل المكوّن الإضافي، أضفه إلى قسم plugins في ملف الإعدادات:

// webpack.config.js
module.exports = {
  plugins: [
    new webpack.HashedModuleIdsPlugin()
  ]
};

مراجع إضافية

ملخّص

  • تخزين الحِزمة مؤقتًا والتفريق بين الإصدارات من خلال تغيير اسم الحِزمة
  • تقسيم الحِزمة إلى رمز التطبيق ورمز المورّد ووقت التشغيل
  • تضمين وقت التشغيل لحفظ طلب HTTP
  • تحميل الرمز غير المهم بشكل بطيء باستخدام import
  • تقسيم الرمز حسب المسارات أو الصفحات لتجنُّب تحميل المحتوى غير الضروري