web-dev-qa-db-fra.com

Sur Webpack, comment puis-je importer un script sans l'évaluer?

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

enter image description here

↑↑↑↑↑↑↑↑ 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?

9
migcoder

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:

  • Rassemblez toutes les instructions import (args) dans le code
  • Si le import (args) contient/* prefetch: true */comment
  • Trouvez le chunkId à partir de import () instruction
  • Remplacez-le par Prefetcher.fetch (chunkId)

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

1
migcoder

[~ # ~] 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 Et chunk.d15e7fdfc91b34bb78c4.js, Les préchargements suivants seront injectés dans le document head

<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

  1. vous devrez d'abord nommer ce bloc asynchrone à l'aide de l'exemple webpack magic comment

    import(/* webpackChunkName: 'myAsyncPreloadChunk' */ './path/to/file')
    
  2. 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

  1. chaque fois qu'un navigateur rencontre une balise script, il la chargera, l'analysera et l'exécutera immédiatement
  2. vous ne pouvez retarder l'analyse et l'évaluation qu'avec l'aide des balises async et defer jusqu'à DOMContentLoaded un événement
  3. vous pouvez retarder l'exécution (évaluation) si vous n'insérez pas la balise de script (préchargez-la uniquement avec link)

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

1
Tripurari Shankar

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("...");
});
0
Eliya Cohen