التحميل الكسول
يُعد التحميل الكسول، أو التحميل «عند الطلب»، طريقة ممتازة لتحسين موقعك أو تطبيقك. تقوم هذه الممارسة أساسًا على تقسيم الكود عند نقاط فصل منطقية، ثم تحميله عندما ينفذ المستخدم إجراءً يتطلب، أو سيتطلب، كتلة جديدة من الكود. يسرّع ذلك التحميل الأولي للتطبيق ويخفف وزنه الإجمالي، لأن بعض الكتل قد لا تُحمّل أصلًا.
مثال على الاستيراد الديناميكي
لنأخذ المثال من تقسيم الكود ونعدّله قليلًا لتوضيح هذا المفهوم بشكل أكبر. الكود هناك يؤدي فعلًا إلى إنشاء جزء مستقل، lodash.bundle.js، ويقوم تقنيًا بتحميله كسولًا بمجرد تشغيل السكربت. المشكلة أن تحميل الحزمة لا يتطلب أي تفاعل من المستخدم، أي أن الطلب سيُرسل في كل مرة تُحمّل فيها الصفحة. هذا لا يفيدنا كثيرًا، وسيؤثر سلبًا في الأداء.
لنجرب شيئًا مختلفًا. سنضيف تفاعلًا يطبع بعض النصوص في الكونسول عندما ينقر المستخدم زرًا. لكننا سنؤجل تحميل ذلك الكود (print.js) إلى أن يحدث التفاعل لأول مرة. للقيام بذلك، سنعود إلى المثال النهائي لـ الاستيراد الديناميكي من دليل تقسيم الكود ونعيد صياغته، مع إبقاء lodash داخل الجزء الرئيسي.
project
webpack-demo
├── package.json
├── package-lock.json
├── webpack.config.js
├── /dist
├── /src
│ ├── index.js
+│ └── print.js
└── /node_modulessrc/print.js
console.log(
"The print.js module has loaded! See the network tab in dev tools...",
);
export default () => {
console.log('Button Clicked: Here\'s "some text"!');
};src/index.js
+ import _ from 'lodash';
+
- async function getComponent() {
+ function component() {
const element = document.createElement('div');
- const _ = await import(/* webpackChunkName: "lodash" */ 'lodash');
+ const button = document.createElement('button');
+ const br = document.createElement('br');
+ button.innerHTML = 'Click me and look at the console!';
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
+ element.appendChild(br);
+ element.appendChild(button);
+
+ // لاحظ أنه بسبب وجود طلب شبكة، سيحتاج الموقع/التطبيق
+ // بمستوى إنتاج إلى إظهار مؤشر تحميل ما.
+ button.onclick = e => import(/* webpackChunkName: "print" */ './print').then(module => {
+ const print = module.default;
+
+ print();
+ });
return element;
}
- getComponent().then(component => {
- document.body.appendChild(component);
- });
+ document.body.appendChild(component());الآن لنشغّل webpack ونتحقق من وظيفة التحميل الكسول الجديدة:
...
Asset Size Chunks Chunk Names
print.bundle.js 417 bytes 0 [emitted] print
index.bundle.js 548 kB 1 [emitted] [big] index
index.html 189 bytes [emitted]
...مثال على الاستيراد المؤجل
في بعض الحالات، قد يكون تحويل كل استخدامات الوحدة إلى أسلوب غير متزامن مزعجًا أو صعبًا، لأنه يفرض تحويلًا غير ضروري لكل الدوال إلى غير متزامنة، من دون توفير إمكانية تأجيل عمل التقييم المتزامن فقط.
يهدف مقترح TC39 بعنوان Deferring Module Evaluation إلى حل هذه المشكلة.
يقترح ذلك وجود صيغة استيراد نحوية جديدة لا تُعيد إلا كائن namespace exotic. عند استخدامها، لن تُنفّذ الوحدة ولا تبعياتها، لكنها ستُحمّل بالكامل إلى حد تصبح فيه جاهزة للتنفيذ قبل اعتبار مخطط الوحدات محمّلًا.
لا تُنفذ عمليات التنفيذ إلا عند الوصول إلى خاصية من هذه الوحدة، إذا لزم ذلك.
تتوفر هذه الميزة بتفعيل experiments.deferImport.
project
webpack-demo
├── package.json
├── package-lock.json
├── webpack.config.js
├── /dist
├── /src
│ ├── index.js
+ │ └── print.js
└── /node_modulessrc/print.js
console.log(
"The print.js module has loaded! See the network tab in dev tools...",
);
export default () => {
console.log('Button Clicked: Here\'s "some text"!');
};src/index.js
import _ from 'lodash';
+ import defer * as print from './print';
function component() {
const element = document.createElement('div');
const button = document.createElement('button');
const br = document.createElement('br');
button.innerHTML = 'Click me and look at the console!';
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
element.appendChild(br);
element.appendChild(button);
- // Note that because a network request is involved, some indication
- // of loading would need to be shown in a production-level site/app.
+ // في هذا المثال، تُنزّل وحدة print لكنها لا تُقيّم،
+ // لذلك لا يوجد طلب شبكة بعد النقر على الزر.
- button.onclick = e => import(/* webpackChunkName: "print" */ './print').then(module => {
+ button.onclick = e => {
const print = module.default;
+ // ^ تُقيّم الوحدة هنا.
print();
- });
+ };
return element;
}
getComponent().then(component => {
document.body.appendChild(component);
});
document.body.appendChild(component());هذا مشابه لأسلوب CommonJS في التحميل الكسول:
src/index.js
import _ from 'lodash';
- import defer * as print from './print';
function component() {
const element = document.createElement('div');
const button = document.createElement('button');
const br = document.createElement('br');
button.innerHTML = 'Click me and look at the console!';
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
element.appendChild(br);
element.appendChild(button);
// في هذا المثال، تُنزّل وحدة print لكنها لا تُقيّم،
// لذلك لا يوجد طلب شبكة بعد النقر على الزر.
button.onclick = e => {
+ const print = require('./print');
+ // ^ تُقيّم الوحدة هنا.
const print = module.default;
- // ^ The module is evaluated here.
print();
};
return element;
}
getComponent().then(component => {
document.body.appendChild(component);
});
document.body.appendChild(component());استخدام import.defer() مع وحدات السياق
5.105.0+يعمل import.defer() أيضًا مع وحدات السياق؛ إذ يمكن أن يكون مسار الاستيراد تعبيرًا ديناميكيًا. يُضمّن webpack كل الوحدات المطابقة في مخطط الوحدات، لكن تقييم الوحدة المحددة يؤجل إلى أن يُوصل أولًا إلى خاصية على كائن namespace.
يوضح المثال التالي التقييم المؤجل لوحدات اللغة باستخدام مسار سياق ديناميكي:
src/locales/en.js
export const greeting = "Hello";src/locales/fr.js
export const greeting = "Bonjour";src/index.js
const language = navigator.language.split("-")[0]; // "en", "fr", etc.
const locale = import.defer("./locales/" + language + ".js");
document.getElementById("btn").addEventListener("click", () => {
// تُقيّم وحدة اللغة هنا، عند أول وصول إلى خاصية.
document.getElementById("output").textContent = locale.greeting;
});يُحضّر webpack كل وحدات اللغة المطابقة بحيث تكون جاهزة للتنفيذ، لكن الوحدة المحددة لا تُقيّم إلا عند أول وصول إلى locale.greeting. يتيح لك ذلك تحميل عدة ملفات لغة من دون تنفيذها مباشرة.
الأطر
لدى كثير من الأطر والمكتبات توصياتها الخاصة حول كيفية إنجاز ذلك ضمن منهجياتها. فيما يلي بعض الأمثلة:



