Sul webpack come posso importare uno script senza valutarlo?


9

Di recente sto lavorando ad alcuni lavori di ottimizzazione del sito Web e inizio a utilizzare la suddivisione del codice nel webpack utilizzando la dichiarazione di importazione in questo modo:

import(/* webpackChunkName: 'pageB-chunk' */ './pageB')

Che crea correttamente il pageB-chunk.js , ora diciamo che voglio precaricare questo pezzo nella pagina A, posso farlo aggiungendo questa istruzione nella pagina A:

import(/* webpackChunkName: 'pageB-chunk' */ /* webpackPrefetch: true */ './pageB')

Che si tradurrà in a

<link rel="prefetch" href="pageB-chunk.js">

essendo aggiunto alla testa dell'HTML, il browser lo precaricherà, finora tutto bene.

Il problema è la dichiarazione di importazione che uso qui non solo per precaricare il file js, ma anche per valutare il file js, significa che il codice di quel file js viene analizzato e compilato in bytecode, viene eseguito il codice di livello superiore di quel JS.

Questa operazione richiede molto tempo su un dispositivo mobile e voglio ottimizzarla, voglio solo la parte di prefetch , non voglio la parte di valutazione ed esecuzione , perché in seguito, quando si verificano alcune interazioni dell'utente, attiverò l' analisi e valuto me stesso

inserisci qui la descrizione dell'immagine

↑↑↑↑↑↑↑↑ Voglio solo innescare i primi due passaggi, le immagini provengono da https://calendar.perfplanet.com/2011/lazy-evaluation-of-commonjs-modules/ ↑↑↑↑↑↑↑ ↑↑

Certo che posso farlo aggiungendo io stesso il link di prefetch, ma questo significa che devo sapere quale URL dovrei inserire nel link di prefetch, il webpack sicuramente conosce questo URL, come posso ottenerlo dal webpack?

Il webpack ha un modo semplice per raggiungere questo obiettivo?


if (false) import(…)- Dubito che il webpack analizzi il codice morto?
Bergi,

Dove / quando non si vuole realmente valutare il modulo? Ecco dove importdovrebbe andare il codice dinamico .
Bergi,

Sono così confuso ora. Perché la valutazione è importante? perché alla fine, il file JS dovrebbe essere valutato dal dispositivo browser client. O non capisco correttamente la domanda.
Amerllic,

@AmerllicA alla fine sì, i js dovrebbero essere valutati, ma pensa a questo caso: il mio sito web ha A, B due pagine, i visitatori nella pagina A spesso visitano la pagina B dopo aver "fatto alcuni lavori" a pagina A. Quindi è ragionevole precaricare la pagina B JS, ma se riesco a controllare il tempo in cui viene valutata la JS di questa B, posso essere sicuro al 100% di non bloccare il thread principale che crea anomalie quando il visitatore sta cercando di "fare i propri lavori" a pagina A. Posso valutare Il JS di B dopo che il visitatore fa clic su un collegamento che punta alla pagina B, ma in quel momento il JS di B viene probabilmente scaricato, ho solo bisogno di dedicare un po 'di tempo per valutarlo.
migcoder

Sicuro secondo il blog di Chrome v8: v8.dev/blog/cost-of-javascript-2019 , hanno fatto molte ottimizzazioni per ottenere il tempo di analisi JS velocissimo, utilizzando il thread di lavoro e molti altri tecnici, i dettagli qui youtube.com / watch? v = D1UJgiG4_NI . Ma altri browser non implementano ancora tale ottimizzazione.
migcoder

Risposte:


2

AGGIORNARE

Puoi usare preload-webpack-plugin con html-webpack-plugin ti permetterà di definire cosa precaricare nella configurazione e inserirà automaticamente i tag per precaricare il tuo pezzo

nota se stai usando webpack v4 fin da ora dovrai installare questo plugin usando preload-webpack-plugin@next

esempio

plugins: [
  new HtmlWebpackPlugin(),
  new PreloadWebpackPlugin({
    rel: 'preload',
    include: 'asyncChunks'
  })
]

Per un progetto che genera due script asincroni con nomi generati dinamicamente, come chunk.31132ae6680e598f8879.jse chunk.d15e7fdfc91b34bb78c4.js, i seguenti precarichi verranno iniettati nel documentohead

<link rel="preload" as="script" href="chunk.31132ae6680e598f8879.js">
<link rel="preload" as="script" href="chunk.d15e7fdfc91b34bb78c4.js">

AGGIORNAMENTO 2

se non vuoi precaricare tutto il pezzo asincrono ma solo una volta specifico puoi farlo

o puoi usare il plug-in babel di migcoder o con il preload-webpack-pluginseguente seguito

  1. per prima cosa dovrai nominare quel pezzo asincrono con l'aiuto di webpack magic commentesempio

    import(/* webpackChunkName: 'myAsyncPreloadChunk' */ './path/to/file')
  2. e poi nella configurazione del plugin usa quel nome come

    plugins: [
      new HtmlWebpackPlugin(),   
      new PreloadWebpackPlugin({
        rel: 'preload',
        include: ['myAsyncPreloadChunk']
      }) 
    ]

Prima di tutto vediamo il comportamento del browser quando specifichiamo scripttag o linktag per caricare lo script

  1. ogni volta che un browser incontra un scripttag, lo carica lo analizza e lo esegue immediatamente
  2. puoi solo ritardare l'analisi e la valutazione con l'aiuto asynce defertag solo finoDOMContentLoaded all'evento
  3. puoi ritardare l'esecuzione (valutazione) se non inserisci il tag dello script (precaricalo solo con link)

ora ci sono altri modi di hackey non raccomandati se spedisci l'intero script e stringo comment(perché il tempo di valutazione del commento o della stringa è quasi trascurabile) e quando è necessario eseguire ciò che è possibile utilizzare Function() constructoro evalentrambi non sono consigliati


Operatori del servizio Approach : (questo manterrà l'evento cache dopo il ricaricamento della pagina o l'utente non sarà in linea dopo il caricamento della cache)

Nel browser moderno è possibile utilizzare service workerper recuperare e memorizzare nella cache un ricorso (JavaScript, immagine, css qualunque) e quando la richiesta principale del thread per quel ricorso è possibile intercettare quella richiesta e restituire il ricorso dalla cache in questo modo non si analizza e si valuta lo script quando lo stai caricando nella cache leggi di più sugli operatori del servizio qui

esempio

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
      });
    }
  }));
});

come vedi non è una cosa dipendente dal webpack , questo non rientra nell'ambito del webpack, tuttavia con l'aiuto del webpack puoi dividere il tuo bundle che ti aiuterà a utilizzare meglio il lavoratore dell'assistenza


ma il mio punto dolente è che non riesco a ottenere facilmente l'URL del file dal webpack, anche se uso SW, devo ancora far sapere a SW quali file dovrebbero essere pre-cache ... un plugin manifest webpack può generare informazioni manifest in SW, ma è tutto in funzione, significa che SW non ha altra scelta che pre-cache tutti i file elencati in manifest ...
migcoder

Idealmente, spero che il webpack possa aggiungere un altro commento magico come / * webpackOnlyPrefetch: true * /, quindi posso chiamare due volte l'istruzione import per ogni pezzo pigro caricabile, uno per il prefetch, uno per la valutazione del codice e tutto accade su richiesta.
migcoder

1
@migcoder è un punto valido (non è possibile ottenere il nome del file perché generato dinamicamente in fase di esecuzione) cercherà qualsiasi soluzione se ne trovo
Tripurari Shankar

@migcoder Ho aggiornato la risposta per favore vedi che risolve il tuo problema
Tripurari Shankar

risolve parte del problema, può filtrare i pezzi asincroni che va bene, ma il mio obiettivo finale è solo quello di precaricare i pezzi asincroni richiesti. Sto attualmente guardando questo plugin github.com/sebastian-software/babel-plugin-smart-webpack-import , mi mostra come raccogliere tutte le dichiarazioni di importazione e fare alcune modifiche al codice in base ai commenti magici, forse posso creare un plug-in simile per inserire il codice prefetch nelle dichiarazioni di importazione con il commento magico "webpackOnlyPrefetch: true".
migcoder,

1

Aggiornamenti: includo tutto in un pacchetto npm, dai un'occhiata! https://www.npmjs.com/package/webpack-prefetcher


Dopo alcuni giorni di ricerche, finisco con la scrittura di un plug-in babel personalizzato ...

In breve, il plugin funziona così:

  • Raccogli tutte le istruzioni import (args) nel codice
  • Se l' importazione (args) contiene / * prefetch: true * / comment
  • Trova il chunkId dal import () dichiarazione
  • Sostituiscilo con Prefetcher.fetch (chunkId)

Prefetcher è una classe di supporto che contiene il manifest dell'output del webpack e può aiutarci a inserire il link di prefetch:

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 esempio di utilizzo:

const pageAImporter = {
  prefetch: () => import(/* prefetch: true */ './pageA.js')
  load: () => import(/* webpackChunkName: 'pageA' */ './pageA.js')
}

a.onmousehover = () => pageAImporter.prefetch()

a.onclick = () => pageAImporter.load().then(...)

I dettagli di questo plugin sono disponibili qui:

Prefetch - Assumi il controllo dal webpack

Ancora una volta, questo è un modo davvero confuso e non mi piace, se vuoi che il team di webpack lo attui, ti preghiamo di votare qui:

Funzionalità: precarica importazione dinamica su richiesta


0

Supponendo di aver capito cosa stai cercando di ottenere, vuoi analizzare ed eseguire un modulo dopo un determinato evento (ad esempio, fai clic su un pulsante). Potresti semplicemente inserire la dichiarazione di importazione all'interno di quell'evento:

element.addEventListener('click', async () => {
  const module = await import("...");
});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.