איך חבילת Webpack עוזרת לשמור נכסים במטמון
הדבר הבא (אחרי אופטימיזציה של גודל האפליקציה שמשפר את זמן הטעינה של האפליקציה, הוא שמירה במטמון. אפשר להשתמש בו כדי לשמור חלקים מהאפליקציה בלקוח ולהימנע מהורדה שלהם מחדש בכל פעם.
שימוש בניהול גרסאות של חבילות ובכותרות מטמון
הגישה הנפוצה לביצוע שמירה במטמון היא:
להורות לדפדפן לשמור קובץ במטמון למשך זמן רב מאוד (למשל, שנה):
# Server header Cache-Control: max-age=31536000
אם אתם לא יודעים מה
Cache-Control
עושה, עיינו בפוסט המעולה של ג'ייק ארצ'יבלד על שיטות מומלצות לשמירה במטמון.ולשנות את שם הקובץ לאחר השינוי כדי לאלץ את ההורדה מחדש:
<!-- Before the change --> <script src="./index-v15.js"></script> <!-- After the change --> <script src="./index-v16.js"></script>
כך הוא מורה לדפדפן להוריד את קובץ ה-JS, לשמור אותו במטמון ולהשתמש בעותק שנשמר במטמון. הדפדפן יתחבר לרשת רק אם שם הקובץ ישתנה (או אם חולפת שנה).
ב-Webpack מבצעים את אותו הדבר, אבל במקום מספר גרסה מציינים את הגיבוב (hash) של הקובץ. כדי לכלול את הגיבוב בשם הקובץ, משתמשים ב-[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
יותר גמישה, וכדאי להשתמש בה במקרים של חלק מורכב בשרת.
במהלך ה-build נוצר קובץ JSON עם מיפוי בין שמות הקבצים ללא גיבוב (hash) לבין שמות הקבצים עם גיבוב. השתמשו ב-JSON הזה בשרת כדי לבחור עם איזה קובץ לעבוד:
// manifest.json
{
"bundle.js": "bundle.8e0d62a03.js"
}
קריאה נוספת
- ג'ייק ארצ'יבלד על שיטות מומלצות לשמירה במטמון
חילוץ יחסי תלות וזמן ריצה לקובץ נפרד
יחסי תלות
יחסי תלות של אפליקציות משתנים בדרך כלל לעיתים רחוקות יותר מקוד האפליקציה בפועל. אם תעבירו אותם לקובץ נפרד, לדפדפן תהיה אפשרות לשמור אותם במטמון בנפרד – ולא יורידו אותם מחדש בכל פעם שקוד האפליקציה משתנה.
כדי לחלץ את יחסי התלות לחלק נפרד, מבצעים 3 שלבים:
החליפו את שם קובץ הפלט ב-
[name].[chunkname].js
:// webpack.config.js module.exports = { output: { // Before filename: 'bundle.[chunkhash].js', // After filename: '[name].[chunkhash].js' } };
כש-Webpack יוצר את האפליקציה, הוא מחליף את
[name]
בשם של מקטע אם לא נוסיף את החלק[name]
, נצטרך להבחין בין מקטעים באמצעות הגיבוב שלהם – זה די קשה!ממירים את השדה
entry
לאובייקט:// webpack.config.js module.exports = { // Before entry: './index.js', // After entry: { main: './index.js' } };
בקטע הקוד הזה, 'main' הוא שם של מקטע. השם הזה יוחלף ב-
[name]
משלב 1.אם אתם כבר מפתחים את האפליקציה, הקטע הזה יכלול את כל הקוד של האפליקציה, בדיוק כמו שלא עשינו עדיין. אבל זה ישתנה ברגע.
ב-webpack 4, מוסיפים את האפשרות
optimization.splitChunks.chunks: 'all'
להגדרה של Webpack:// webpack.config.js (for webpack 4) module.exports = { optimization: { splitChunks: { chunks: 'all' } } };
האפשרות הזו מפעילה פיצול חכם באמצעות קוד. באמצעותו, ה-webpack מחלץ את קוד הספק אם הוא גדול מ-30KB (לפני ההקטנה ו-gzip). הפעולה הזו גם תחלץ את הקוד הנפוץ – האפשרות הזו שימושית אם ה-build מייצר כמה חבילות (למשל, אם מפצלים את האפליקציה למסלולים).
בחבילה 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
.
אחרי השינויים האלה, כל build ייצור שני קבצים במקום אחד: 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
}
};
בחבילה 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
})
]
};
לאחר השינויים האלה, כל build ייצור שלושה קבצים:
$ 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 לשמירה במטמון לטווח ארוך
- מסמכי Webpack על זמן ריצה ומניפסט של Webpack
- "הפקת המרב מ-CommonsChunkPlugin"
- איך
optimization.splitChunks
וoptimization.runtimeChunk
פועלים
זמן ריצה מוטבע של 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:
כדי לדעת מה השם שנוצר של מקטע זמן הריצה, מוסיפים את
WebpackManifestPlugin
:// webpack.config.js (for webpack 4) const ManifestPlugin = require('webpack-manifest-plugin'); module.exports = { plugins: [ new ManifestPlugin() ] };
גרסת build עם הפלאגין הזה תיצור קובץ שנראה כך:
// manifest.json { "runtime~main.js": "runtime~main.8e0d62a03.js" }
מטמיעים את התוכן של מקטע זמן הריצה בצורה נוחה. לדוגמה, עם 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:
כדי ששם זמן הריצה יהיה סטטי, צריך לציין
filename
:module.exports = { plugins: [ new webpack.optimize.CommonsChunkPlugin({ name: 'runtime', minChunks: Infinity, filename: 'runtime.js' }) ] };
הצבת התוכן ב-
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
, ותשפר את זמן הטעינה הראשונית.
זה ישפר גם את השמירה במטמון – אם משנים את הקוד בקטע הראשי, מקטע התגובות לא יושפע מכך.
קריאה נוספת
- מסמכי Webpack לפונקציה
import()
- ההצעה של JavaScript להטמעת התחביר
import()
פיצול הקוד לנתיבים ולדפים
אם באפליקציה יש כמה מסלולים או דפים, אבל יש רק קובץ JS אחד עם הקוד (מקטע main
אחד), סביר להניח שאתם שולחים בייטים נוספים לכל בקשה. לדוגמה, כשמשתמש מבקר בדף הבית באתר:
![דף הבית של WebFundamentals](https://web.dev/static/articles/use-long-term-caching/image/a-webfundamentals-home-pa-086040f923b7f.png?authuser=5&hl=he)
הם לא צריכים לטעון את הקוד כדי לעבד מאמר שנמצא בדף אחר, אבל הם ייטענו אותו. בנוסף, אם המשתמש תמיד מבקר רק בדף הבית ותבצע שינוי בקוד המאמר, ה-webpack יבטל את כל החבילה, והמשתמש יצטרך להוריד מחדש את כל האפליקציה.
אם אנחנו מפצלים את האפליקציה לדפים (או למסלולים, אם מדובר באפליקציה עם דף אחד), המשתמש יוריד רק את הקוד הרלוונטי. בנוסף, הדפדפן ישמור את קוד האפליקציה במטמון בצורה טובה יותר: אם תשנו את הקוד של דף הבית, ה-webpack יבטל את התוקף רק של המקטע המתאים.
לאפליקציות בדף יחיד
כדי לפצל אפליקציות של דף יחיד לפי מסלולים, משתמשים ב-import()
(עיינו בקטע 'קוד לטעינה מושהית שאין צורך בו כרגע'). אם אתם משתמשים ב-framework, יכול להיות שיש לו פתרון לכך:
- "פיצול קוד"
במסמכים של
react-router
(ל-React) - "נתיבי טעינה מושהית" במסמכים של
vue-router
(ל-Vue.js)
באפליקציות מסורתיות מרובות דפים
כדי לפצל אפליקציות מסורתיות לפי דפים, משתמשים בנקודות הכניסה של 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 יחפש באופן אוטומטי קוד נפוץ ויחלץ אותו לקבצים נפרדים.
לחלופין, בחבילה 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, יותר מדי מודולים ייכנסו לקובץ המשותף, וכתוצאה מכך הוא יופחת יותר מדי.
קריאה נוספת
- מסמכי Webpack על המושג של נקודות כניסה
- מסמכי Webpack בנוגע ל-CommonsChunkPlugin
- "הפקת המרב מ-CommonsChunkPlugin"
- איך
optimization.splitChunks
וoptimization.runtimeChunk
פועלים
הפיכת מזהי המודולים ליציבים יותר
במהלך יצירת הקוד, ה-webpack מקצה לכל מודול מזהה. לאחר מכן, המזהים האלה יהיו בשימוש ב-require()
בתוך החבילה. בדרך כלל מזהים בפלט ה-build ממש לפני נתיבי המודול:
$ 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
.
הוא מחליף את המזהים המבוססים על מונה בגיבובים (hash) של נתיבי מודולים:
$ 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()
]
};
קריאה נוספת
- מסמכי Webpack מידע על HashedModuleIdsPlugin
סיכום
- לשמור את החבילה במטמון ולהבדיל בין גרסאות על ידי שינוי שם החבילה
- פיצול החבילה לקוד אפליקציה, קוד ספק וזמן ריצה
- להכניס שורה של סביבת זמן הריצה כדי לשמור בקשת HTTP
- טעינה מדורגת של קוד שאינו קריטי באמצעות
import
- פיצול הקוד לפי מסלולים/דפים כדי למנוע טעינה של דברים מיותרים