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

أهمية حزمة الويب في التخزين المؤقت لمواد العرض

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

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

تتمثل الطريقة الشائعة للتخزين المؤقت في:

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

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

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

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

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

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

مع 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'
      }
    };
    

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

    الآن، إذا أنشأت التطبيق، فسيتضمن هذا المقطع رمز التطبيق بأكمله - تمامًا كما لم نفعل هذه الخطوات. وسيتغير هذا الأمر في غضون ثانية.

  3. في حزمة الويب 4، أضِف الخيار optimization.splitChunks.chunks: 'all' إلى إعدادات حزمة الويب:

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

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

    في حزمة الويب 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 لحزمة الويب 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

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

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

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

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

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

في حزمة الويب 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>

محتوى إضافي للقراءة

وقت تشغيل حزمة الويب المضمّن لحفظ طلب HTTP إضافي

لتحسين الأمر، جرِّب تضمين وقت تشغيل حزمة الويب في استجابة 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 باستخدام منطق خادم مخصّص

باستخدام حزمة الويب 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>
        …
      `);
    });
    

أو باستخدام حزمة الويب 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() يحدد أنك تريد تحميل وحدة معينة بشكل ديناميكي. عندما ترى حزمة الويب 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"، ما يؤدي إلى تحسين مدّة التحميل الأولية. علاوة على ذلك، فإنه سيحسن التخزين المؤقت - إذا قمت بتغيير التعليمات البرمجية في المقطع الرئيسي، فلن يتأثر مقطع التعليقات.

محتوى إضافي للقراءة

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

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

صفحة رئيسية على WebFundamentals

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

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

لتطبيقات الصفحة الواحدة

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

للتطبيقات التقليدية المتعددة الصفحات

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

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

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

$ 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 3، استخدِم CommonsChunkPlugin، وستنقل التبعيات الشائعة إلى ملف محدد جديد:

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

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

محتوى إضافي للقراءة

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

عند إنشاء التعليمة البرمجية، تعين حزمة الويب معرّفًا لكل وحدة. لاحقًا، يتم استخدام المعرّفات هذه في 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
  • تقسيم الرمز حسب المسارات أو الصفحات لتجنُّب تحميل عناصر غير ضرورية