Je travaille récemment sur des travaux d'optimisation de site Web et je commence à utiliser le fractionnement de code dans Webpack en utilisant une instruction d'importation comme celle-ci:
import(/* webpackChunkName: 'pageB-chunk' */ './pageB')
Ce qui crée correctement le pageB-chunk.js , disons maintenant que je veux prefetch ce morceau dans la pageA, je peux le faire en ajoutant cette déclaration dans la pageA:
import(/* webpackChunkName: 'pageB-chunk' */ /* webpackPrefetch: true */ './pageB')
Ce qui se traduira par un
<link rel="prefetch" href="pageB-chunk.js">
étant ajouté à la tête de HTML, le navigateur le prélèvera, jusqu'ici tout va bien.
Le problème est l'instruction import que j'utilise ici non seulement pour pré-extraire le fichier js, mais également évaluer le fichier js, cela signifie que le code de ce fichier js est analysé & compiler en bytecodes, le code de niveau supérieur de ce JS est exécuté.
C'est une opération très chronophage sur un appareil mobile et je veux l'optimiser, je ne veux que la partie prefetch , je ne veux pas la évalue et exécute la partie , car plus tard, lorsque certaines interactions de l'utilisateur se produisent, je déclencherai l'analyse et évaluer moi-même
↑↑↑↑↑↑↑↑ Je ne veux déclencher que les deux premières étapes, les images proviennent de https://calendar.perfplanet.com/2011/lazy-evaluation-of-commonjs-modules/ ↑↑↑↑↑↑↑↑↑↑
Bien sûr, je peux le faire en ajoutant moi-même le lien de prélecture, mais cela signifie que j'ai besoin de savoir quelle URL je dois mettre dans le lien de prélecture, webpack connaît définitivement cette URL, comment puis-je l'obtenir à partir de webpack?
Webpack a-t-il un moyen simple d'y parvenir?
Mises à jour: J'inclus toutes les choses dans un paquet npm, vérifiez-le! https://www.npmjs.com/package/webpack-prefetcher
Après quelques jours de recherche, je finis par écrire un plugin babel customisé ...
En bref, le plugin fonctionne comme ceci:
Prefetcher est une classe d'assistance qui contient le manifeste de la sortie du pack Web et peut nous aider à insérer le lien de prélecture:
export class Prefetcher {
static manifest = {
"pageA.js": "/pageA.hash.js",
"app.js": "/app.hash.js",
"index.html": "/index.html"
}
static function fetch(chunkId) {
const link = document.createElement('link')
link.rel = "prefetch"
link.as = "script"
link.href = Prefetcher.manifest[chunkId + '.js']
document.head.appendChild(link)
}
}
Un exemple d'utilisation:
const pageAImporter = {
prefetch: () => import(/* prefetch: true */ './pageA.js')
load: () => import(/* webpackChunkName: 'pageA' */ './pageA.js')
}
a.onmousehover = () => pageAImporter.prefetch()
a.onclick = () => pageAImporter.load().then(...)
Le détail de ce plugin peut être trouvé ici:
Prefetch - Prenez le contrôle du webpack
Encore une fois, c'est une manière vraiment piratée et je ne l'aime pas, si vous voulez que l'équipe Webpack l'implémente, veuillez voter ici:
Fonctionnalité: pré-extraction de l'importation dynamique à la demande
[~ # ~] mise à jour [~ # ~]
Vous pouvez utiliser preload-webpack-plugin avec html-webpack-plugin cela vous permettra de définir ce qu'il faut précharger dans la configuration et il insérera automatiquement des balises pour précharger votre morceau
note si vous utilisez webpack v4 à partir de maintenant, vous devrez installer ce plugin en utilisant preload-webpack-plugin@next
exemple
plugins: [
new HtmlWebpackPlugin(),
new PreloadWebpackPlugin({
rel: 'preload',
include: 'asyncChunks'
})
]
Pour un projet générant deux scripts asynchrones avec des noms générés dynamiquement, tels que
chunk.31132ae6680e598f8879.js
Etchunk.d15e7fdfc91b34bb78c4.js
, Les préchargements suivants seront injectés dans le documenthead
<link rel="preload" as="script" href="chunk.31132ae6680e598f8879.js">
<link rel="preload" as="script" href="chunk.d15e7fdfc91b34bb78c4.js">
MISE À JOUR 2
si vous ne voulez pas précharger tous les morceaux asynchrones mais uniquement une fois que vous pouvez le faire aussi
soit vous pouvez utiliser plugin babel de migcoder ou avec preload-webpack-plugin
comme suit
vous devrez d'abord nommer ce bloc asynchrone à l'aide de l'exemple webpack magic comment
import(/* webpackChunkName: 'myAsyncPreloadChunk' */ './path/to/file')
puis dans la configuration du plugin, utilisez ce nom comme
plugins: [
new HtmlWebpackPlugin(),
new PreloadWebpackPlugin({
rel: 'preload',
include: ['myAsyncPreloadChunk']
})
]
Voyons tout d'abord le comportement du navigateur lorsque nous spécifions la balise script
ou la balise link
pour charger le script
script
, il la chargera, l'analysera et l'exécutera immédiatementasync
et defer
jusqu'à DOMContentLoaded
un événementlink
)maintenant il y en a d'autres non recommandés façon de hackey est que vous expédiez votre script entier et string
ou comment
(parce que l'évaluation l'heure du commentaire ou de la chaîne est presque négligeable) et lorsque vous devez l'exécuter, vous pouvez utiliser Function() constructor
ou eval
les deux ne sont pas recommandés
Une autre approche Service Workers: (cela préservera votre événement de cache après le rechargement de la page ou l'utilisateur se déconnecte après le chargement du cache)
Dans un navigateur moderne, vous pouvez utiliser service worker
Pour récupérer et mettre en cache un recours (JavaScript, image, css n'importe quoi) et lorsque le thread principal demande ce recours, vous pouvez intercepter cette demande et renvoyer le recours depuis le cache de cette façon, vous n'êtes pas analyse et évaluation du script lorsque vous le chargez dans le cache en savoir plus sur les techniciens de service ici
exemple
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open('v1').then(function(cache) {
return cache.addAll([
'/sw-test/',
'/sw-test/index.html',
'/sw-test/style.css',
'/sw-test/app.js',
'/sw-test/image-list.js',
'/sw-test/star-wars-logo.jpg',
'/sw-test/gallery/bountyHunters.jpg',
'/sw-test/gallery/myLittleVader.jpg',
'/sw-test/gallery/snowTroopers.jpg'
]);
})
);
});
self.addEventListener('fetch', function(event) {
event.respondWith(caches.match(event.request).then(function(response) {
// caches.match() always resolves
// but in case of success response will have value
if (response !== undefined) {
return response;
} else {
return fetch(event.request).then(function (response) {
// response may be used only once
// we need to save clone to put one copy in cache
// and serve second one
let responseClone = response.clone();
caches.open('v1').then(function (cache) {
cache.put(event.request, responseClone);
});
return response;
}).catch(function () {
// any fallback code here
});
}
}));
});
comme vous pouvez le voir, ce n'est pas un dépendant du webpack ceci est hors de portée du webpack mais avec l'aide de webpack, vous pouvez diviser votre bundle ce qui vous aidera à utiliser travailleur de service mieux
En supposant que j'ai compris ce que vous essayez d'accomplir, vous voulez analyser et exécuter un module après un événement donné (par exemple, cliquez sur un bouton). Vous pouvez simplement mettre l'instruction import à l'intérieur de cet événement:
element.addEventListener('click', async () => {
const module = await import("...");
});