Red de conocimiento informático - Problemas con los teléfonos móviles - Cómo opera Webpack el almacenamiento en caché

Cómo opera Webpack el almacenamiento en caché

Lo que les traigo esta vez es cómo Webpack opera el caché y qué precauciones se deben tomar cuando Webpack opera el caché. El siguiente es un caso práctico, echemos un vistazo.

Prólogo

Recientemente estuve mirando cómo Webpack realiza el almacenamiento en caché persistente y descubrí que todavía hay algunos malentendidos. Solo tengo tiempo para resolverlos y resumirlos. artículo, puede tener una comprensión general:

¿Qué es el caché persistente y por qué necesitamos el caché persistente?

¿Cómo realiza webpack el almacenamiento en caché persistente?

Algunas notas sobre los métodos de almacenamiento en caché de paquetes web.

Caché persistente

En primer lugar, debemos explicar qué es el caché persistente. En el contexto de la prevalencia actual de aplicaciones donde el front-end y el back-end están separados, el front-end. -end html, css y js a menudo existe en el servidor en forma de un archivo de recursos estático y obtiene datos a través de la interfaz para mostrar contenido dinámico. Esto implica la cuestión de cómo la empresa implementa el código de front-end, por lo que implica un problema de implementación de actualizaciones. ¿Debería implementarse primero la página o los recursos?

Primero implemente la página y luego implemente los recursos: durante el intervalo de tiempo entre las dos implementaciones, si el usuario accede a la página, los recursos antiguos se cargarán en la nueva estructura de la página y la versión anterior de los recursos se utilizarán como la nueva versión del almacenamiento en caché, el resultado es: el estilo de página visitado por el usuario está desordenado. A menos que la página se actualice manualmente, la página estará en un estado desordenado antes de que caduque el caché de recursos.

Primero implemente recursos, luego implemente páginas: durante el intervalo de implementación, si un usuario que usa el caché local de una versión anterior del recurso visita el sitio web, el navegador usará directamente el caché local porque la solicitud la página es una versión antigua y el recurso es La referencia no ha cambiado, por lo que esto es normal; sin embargo, si un usuario visita el sitio web sin un caché local o el caché ha caducado, la versión anterior de la página cargará la nueva versión; el recurso, provocando un error de ejecución de la página.

Por lo tanto, necesitamos una estrategia de implementación para garantizar que cuando actualicemos el código en línea, los usuarios en línea puedan realizar la transición sin problemas y abrir nuestro sitio web correctamente.

Se recomienda leer primero esta respuesta: ¿Cómo desarrollar e implementar código front-end en una gran empresa?

Cuando lea las respuestas anteriores, generalmente comprenderá que la solución de almacenamiento en caché persistente más madura es agregar un valor hash después del nombre del recurso estático, porque cada vez que se modifica el archivo, se genera un valor hash diferente. valor, la ventaja de esto es que el archivo se puede liberar de forma incremental para evitar sobrescribir el archivo anterior, lo que provoca una falla en el acceso de los usuarios en línea.

Porque siempre que los nombres de los recursos estáticos (css, js, img) publicados cada vez sean únicos, entonces puedo:

Para archivos html: no habilitar el almacenamiento en caché, y coloque html en su propio servidor, apague el caché del servidor y su servidor solo proporcionará archivos html e interfaces de datos

Para js, css, imágenes y otros archivos estáticos: habilite CDN y almacenamiento en caché, y cargue archivos estáticos recursos para Para los proveedores de servicios CDN, podemos habilitar el almacenamiento en caché a largo plazo de los recursos. Debido a que la ruta de cada recurso es única, no provocará que el recurso se sobrescriba, lo que garantiza la estabilidad del acceso en línea del usuario.

Cada vez que lanzamos una actualización, primero cargamos los recursos estáticos (js, css, img) al proveedor de servicios cdn y luego cargamos el archivo html. Esto no solo garantiza el acceso normal para los usuarios antiguos. pero también permite que los nuevos usuarios vean una nueva página.

Lo anterior es una introducción aproximada a las principales soluciones de almacenamiento en caché persistente de front-end, entonces, ¿por qué necesitamos el almacenamiento en caché persistente?

Cuando un usuario visita nuestro sitio web utilizando un navegador por primera vez, la página introducirá una gran cantidad de recursos estáticos si utilizamos /happylindz/blog.git

cd blog. /code/multiple-page- webpack-demo

npm install Antes de leer lo siguiente, le recomiendo encarecidamente que lea mi artículo anterior: Comprensión profunda del mecanismo de empaquetado de archivos webpack.

Comprender el mecanismo de agrupación de archivos del paquete web le ayudará a implementar mejor el almacenamiento en caché persistente.

La descripción del ejemplo es la siguiente: consta de dos páginas, a saber, páginaA y páginaB

// src/pageA.js

importa el componenteA desde '. / common/componentA';

// Se utiliza la biblioteca de terceros jquery, que debe abstraerse para evitar archivos de empaquetado empresarial de gran tamaño

import $ from 'jquery';

p>

// Carga el archivo css, parte del cual se usa para el estilo public**** y la otra parte se usa para el estilo exclusivo, que debe abstraerse

// Cargue el archivo css, donde una parte se usa para el estilo público *** y la otra parte se usa para el estilo exclusivo, que debe abstraerse.

importar './css/common.css'

importar './css/pageA.css';

console.log(componentA);

console.log($.trim(' hacer algo '));

// src/pageB.js

// Tanto la página A como la página B Se utiliza el módulo público * ** componenteA, que debe abstraerse para evitar cargas repetidas

importar componenteA desde './common/componentA';

importar componenteB desde './ común/componenteB';

importar './css/common.css'

importar './css/pageB.css'; (componentA);

console.log(componentB);

// Se utiliza para cargar el módulo asyncComponent de forma asincrónica. Debe desconectarse para mejorar la velocidad de carga del primero. pantalla

document.getElementById('xxxxx').addEventListener('click', () =gt; {

import( /* webpackChunkName: "async" */

'. /common /asyncComponent.js').luego((async) =gt; {

async();

})

})

// El módulo público*** es básicamente así

exportar el "componente ** bibliotecas predeterminado, carga bajo demanda y división de módulos públicos ***.

Luego necesitas configurar el paquete web:

const path = require('path');

const webpack = require('webpack'); = require('extraer-text-webpack-plugin');

module.exports = {

entrada: {

páginaA: [path.resolve( __dirname, './src/pageA.js')],

páginaB: ruta.resolve(__dirname, './src/pageB.js'),

},

salida: {

ruta: ruta.resolve(__dirname, './dist'),

nombre de archivo: 'js/[nombre].[chunkhash: 8].js',

chunkFilename: 'js/[nombre].[chunkhash: 8].js'

},

módulo: {

reglas: [

{

// Regularidades que coinciden con los archivos CSS que este cargador convertirá

prueba: /. /,

use.ExtractTextPlugin.extract({

respaldo: "style-loader",

use: ["css-loader"]

})

}

]

},

complementos: [

nuevo paquete web .optimize.CommonsChunkPlugin({

nombre: 'común',

minChunks: 2,

}),

nuevo paquete web. CommonsChunkPlugin({

nombre: 'proveedor',

minChunks: ({ recurso }) =gt; (

recurso amp; recurso. indexOf(' node_modules ') gt;= 0 amp; recurso.match(/.js$/)

)

}),

nuevo ExtractTextPlugin({

nombre de archivo: `css/[nombre].css`,

}),

]

} Primero Se utiliza un CommonsChunkPlugin para extraer archivos comunes *** módulos, lo que equivale a que el paquete web diga: "Hombre del paquete web, si ve un módulo cargado más de dos veces, muévalo al fragmento común". Aquí, minChunks es 2, que es la granularidad más fina, por lo que puede elegir cuántas veces usar un módulo antes de extraerlo de los fragmentos, según su situación.

El segundo CommonsChunkPlugin se utiliza para extraer código de terceros y extraerlo. Determinará si los recursos provienen de node_modules y, de ser así, determinará si son módulos de terceros y luego los extraerá. Esto es como decirle a la gente del paquete web que si ve módulos con nombres que terminan en .js en el directorio node_modules, muévalos a un fragmento de proveedor o cree un nuevo fragmento si el fragmento de proveedor no existe.

¿Cuáles son los beneficios de esta configuración? A medida que nuestro negocio se desarrolla, podemos depender de más y más bibliotecas de terceros. Si especificamos una entrada para código de terceros, nuestro webpack.config.js se verá así:

// No propicio. a expansión

module.exports = {

entrada: {

aplicación: './src/main.js',

proveedor: [

'vue',

'axio',

'vue-router',

'vuex ',

// más

],

},

} El tercer complemento ExtractTextPlugin se utiliza para extraer archivos js empaquetados de Extract. el CSS y genere un archivo CSS separado. Imagínese, si modifica el estilo de la página en lugar de la lógica funcional, entonces no desea que cambie el valor hash del archivo js, ​​pero desea que los archivos css y js sean independientes entre sí y no se afecten entre sí. otro.

Después de ejecutar webpack, puede ver el efecto del paquete:

├── css

│ ├─ common.2beb7387.css

│ ├─ páginaA.d178426d.css

│ └── páginaB.33931188.css

└─── js

├── async .03f28faf.js

├─ common.2beb7387.js

├─ pageA.d178426d.js

├─ pageB.33931188.js

└── pageB.vendor.22a1d956.js Puedes ver que css y js están separados, dividimos el módulo para asegurarnos de que el bloque del módulo sea único y se genere uno diferente cada vez que se actualice el código Valor hash .

Con la unicidad, debemos garantizar la estabilidad del valor hash. Imagínese este escenario. Definitivamente no desea modificar una determinada parte del código (módulo, CSS) para provocar el valor hash. del archivo a cambiar Si se cambian todos, entonces esto obviamente no es prudente, entonces, ¿cómo minimizamos los cambios en los valores hash?

En otras palabras, necesitamos descubrir qué causa el error de almacenamiento en caché en las compilaciones de paquetes web y descubrir cómo solucionarlo u optimizarlo.

Hay cuatro factores principales que afectan los cambios de chunkhash:

El código fuente que contiene el módulo

El código de tiempo de ejecución utilizado por webpack para iniciar y ejecutar

El ID del módulo generado por el paquete web (incluido el ID del módulo incluido y el ID del módulo dependiente al que se hace referencia)

chunkID

No importa si alguna de estas cuatro partes cambia, el ID del módulo generado Todos los archivos fragmentados serán diferentes y el caché no será válido, lo cual se describirá en las siguientes cuatro partes:

Primero, cambios en el código fuente:

Obviamente, no hace falta decirlo. que el caché debe actualizarse; de ​​lo contrario, habrá problemas

En segundo lugar, el código de tiempo de ejecución utilizado por webpack para iniciar el código de tiempo de ejecución:

Consulte mi artículo anterior: Comprensión profunda de Mecanismo de empaquetado de archivos del paquete web. Sabrá que es necesario ejecutar algún código de inicio cuando se inicia el paquete web.

(función(módulos) {

ventana["webpackJsonp"] = función webpackJsonpCallback(chunkIds, moreModules) {

// ...

};

función __webpack_require__(moduleId) {

// ...

}

__webpack_require__.e = función requireEnsure(chunkId, devolución de llamada) {

// ...

script.src = __webpack_require__.p "" chunkId "." ({"0": "páginaA". , "1": "páginaB", "3": "proveedor"}[chunkId]||chunkId) "." {"0": "e72ce7d4", "1": "69f6bbe3", "2": "9adbbaa0 ", "3": "53fa02a7"}[chunkId] ".js";

};

})([]); en términos generales, se parecen al contenido anterior. es parte del código de inicio de webpack. Estas funciones le indican al navegador cómo cargar módulos definidos por el paquete web.

Hay una línea de código que cambia cada vez que actualizas, esto se debe a que el código de inicio necesita saber exactamente los valores de chunkid y chunkhash para poder unir correctamente las rutas de los archivos al cargar js asíncronos. archivos de forma asincrónica.

Entonces, ¿en qué archivo aparecerá finalmente este código? Debido a que solo configuramos el módulo de bloque público al final, esta parte del código de tiempo de ejecución se integrará directamente en él, lo que hace que el bloque público continúe cambiando cada vez que actualizamos el código comercial (páginaA, páginaB, módulo), pero Esto obviamente no cumple con nuestra visión, porque solo queremos usar el bloque público para almacenar el módulo de dios público (aquí está el componente A), por lo que mi componente A no necesita ser modificado, ¿por qué necesita el bloque? para ser cambiado.

Por lo tanto, necesitamos extraer este código de ejecución en un archivo separado.

module.exports = {

// ...

complementos: [

// ...

// Colocar después de otro CommonsChunkPlugin

new webpack.optimize.CommonsChunkPlugin({

nombre: 'runtime',

minChunks: Infinity,

p>

}),

]

} Esto equivale a decirle a webpack que me ayude a extraer el código de tiempo de ejecución y colocarlo en un archivo separado.

├── css

│ ├─ common.4cc08e4d.css

│ ├─ pageA.d178426d.css

│ └ ── pageB.33931188.css

│ └── js

├── async.03f28faf.js

├── common.4cc08e4d.js

├─ pageA.d178426d.js

├─ pageB.33931188.js

├─ runtime.8c79fdcd.js

└─ ─vendor.cef44292.js generará un runtime.xxxx adicional. Dado que esta parte del código es dinámica, puede insertarlo en html a través de chunk-manifest-webpack-plugin para reducir la cantidad de solicitudes de red.

En tercer lugar, webpack genera el ID de módulo del módulo.

webpack2 carga el complemento OccurrenceOrderPlugin de forma predeterminada. El complemento OccurrenceOrderPlugin ordenará los módulos que se han introducido con más frecuencia. el moduleId del módulo que se ha introducido más se hará más pequeño. El moduleId del módulo más importado se hará más pequeño, pero aún es inestable. A medida que aumenta la cantidad de código que escribe, el moduleId del módulo con más referencias de código se hará más pequeño y será menos probable que cambie, pero inevitablemente seguirá siendo inestable.

De forma predeterminada, la identificación de un módulo es el índice del módulo en la matriz de módulos.

OccurenceOrderPlugin coloca el módulo más referenciado primero, de modo que el orden de los módulos sea el mismo cada vez que compila, si agrega o elimina módulos al cambiar su código, esto puede afectar los identificadores de todos los módulos.

La mejor práctica es utilizar HashedModuleIdsPlugin, que genera una cadena de cuatro dígitos como ID del módulo en función de la ruta relativa del módulo, ocultando así la información de la ruta del módulo y reduciendo la longitud de la ID del módulo. .

De esta manera, la única forma de cambiar el ID del módulo es cambiar la ruta del archivo. Mientras el valor de la ruta del archivo no cambie, la cadena de cuatro dígitos resultante no cambiará, ni el valor hash. Agregar o eliminar módulos de código comercial no tiene ningún impacto en moduleid.

module.exports = {

complementos: [

new webpack.HashedModuleIdsPlugin(),

// Ponlo en la parte superior

// ...

]

}4. chunkID

De hecho, durante el proceso de compilación, el orden de los número de fragmentos La mayoría son fijos y es poco probable que cambien.

Lo que está involucrado aquí es solo la división básica del módulo. Hay otras situaciones que no se tienen en cuenta. Por ejemplo, cuando un componente que contiene un módulo **** público se carga de forma asincrónica, el ** público. ** el módulo se puede dividir nuevamente. Extraerlo. Forme un módulo de bloque de dios público asincrónico. Si quieres saber más al respecto, puedes leer este artículo: División de código de Webpack

Algunas notas sobre el almacenamiento en caché de webpack

Problema de invalidación del valor hash de archivos CSS

No se recomienda utilizar el complemento DllPlugin para publicaciones en línea

El problema de invalidación del valor hash de los archivos CSS

El problema de invalidación del valor hash de los archivos CSS

El problema de invalidación del valor hash de los archivos CSS

El problema de invalidación del valor hash de los archivos CSS

Problema:

ExtractTextPlugin tiene un problema grave, es decir, usa El [chunkhash] para el nombre del archivo generado se toma directamente del fragmento js que hace referencia al fragmento CSS; en otras palabras, si simplemente cambio el fragmento CSS sin mover el código js, ​​el nombre del archivo CSS generado seguirá siendo el mismo; . Cambiar.

Por lo tanto, necesitamos cambiar chunkhash en ExtractTextPlugin a contenthash. Como sugiere el nombre, representa el valor hash del contenido del archivo de texto, es decir, solo representa el valor hash del archivo de estilo. De esta manera, los archivos js y css compilados tienen valores hash independientes.

module.exports = {

complementos: [

// ...

nuevo ExtractTextPlugin({