En tant que « lion de siège » frontal, Webpack n'est que trop familier. Webpack peut faire trop de choses. Il peut regrouper toutes les ressources (y compris JS, TS, JSX, images, polices, CSS, etc.) et les placer dans des dépendances. , vous permettant de référencer des dépendances pour utiliser les ressources en fonction de vos besoins. Webpack a fait un excellent travail en traduisant plusieurs ressources de fichiers sur le front-end et en analysant les dépendances de modules complexes. Nous pouvons également personnaliser le chargeur et charger nos propres ressources librement. Alors, comment Webpack implémente-t-il le packaging ? Venez jeter un oeil aujourd'hui.
1. Qu'est-ce qui est requis ?
Lorsqu'il s'agit de require, la première chose qui vient à l'esprit peut être import. Import est une norme de syntaxe d'es6
– require est un appel à l'exécution, donc require peut théoriquement être utilisé n'importe où dans le code
– import est un moment de compilation ; appel, il doit donc être placé au début du fichier ;
Lorsque nous utiliserons Webpack pour compiler, nous utiliserons babel pour traduire l'importation en require. Dans CommonJS, il existe une méthode globale require(), qui est utilisée pour charger les modules. . AMD et CMD utilisent également la méthode require pour faire référence.
Par exemple :
var add = require('./a.js');En termes simples,
add(1,2)
requiert en fait une fonction, et le ./a.js
référencé n'est qu'un paramètre de la fonction.
2. Que sont les exportations ?
Ici, nous pouvons considérer les exportations comme un objet. Vous pouvez voir l'utilisation spécifique de l'exportation MDN.
Examinons d'abord la structure du code après notre emballage. Nous pouvons constater que les exigences et les exportations apparaîtront après l'empaquetage.
Tous les navigateurs ne peuvent pas exécuter require exports. Vous devez implémenter require et exports vous-même pour garantir le fonctionnement normal du code. Le code packagé est une fonction auto-exécutable. Les paramètres ont des informations de dépendance et le code du fichier. Le corps de la fonction exécutée exécute le code via eval.
Le dessin de conception global est le suivant :
Étape 1 : Écrivez notre fichier de configuration.
Le fichier de configuration configure notre entrée empaquetée et notre sortie empaquetée pour préparer les fichiers générés ultérieurement.
const chemin = require("chemin"); module.exports = { entrée : "./src/index.js", sortir: { path: path.resolve(__dirname, "./dist"),//La sortie de l'adresse du fichier après l'empaquetage nécessite un chemin absolu, donc le chemin est requis nom de fichier : "main.js" }, mode : "développement"
Étape 2 : L'idée générale de l'analyse du module
: En résumé, il s'agit d'utiliser le fichier fs pour lire le fichier d'entrée et obtenir le chemin du fichier dépendant de l'import via AST Si le fichier dépendant est toujours. a des dépendances, continuez de manière récurrente jusqu'à ce que l'analyse des dépendances soit claire, maintenue dans une carte.
Détail détaillé : Certaines personnes peuvent se demander pourquoi AST est utilisé car AST est né avec cette fonction. Sa ImportDeclaration peut nous aider à filtrer rapidement la syntaxe d'importation. Bien sûr, il est également possible d'utiliser une correspondance régulière. Après tout, le fichier est un. chaîne après avoir été lue. En écrivant L'expression régulière géniale est utile pour obtenir les chemins de dépendance des fichiers, mais elle n'est pas assez élégante.
index.js file
import { str } from "./a.js" ; console.log(`${str} Webpack`)importation
de fichier a.js
{ b} depuis "./b.js" export const str = "hello"
b.js file
export const b="bbb"
l'analyse du module Webpack : utilisez @babel/parser d'AST pour convertir la chaîne lue dans le fichier en une arborescence AST, et @babel/traverse pour syntaxe Analysez et utilisez ImportDeclaration pour filtrer les importations et rechercher les dépendances de fichiers.
const content = fs.readFileSync(entryFile, "utf-8"); const ast = parser.parse(content, { sourceType: "module" }); const dirname = chemin.dirname(entryFile); const personnes à charge = {} ; traverser(ast, { ImportDeclaration({ noeud }) { //Filtrer les importations const newPathName = "./" + path.join(dirname, node.source.value); dependants[node.source.value] = newPathName; } }) const {code} = transformFromAst(ast, null, { préréglages : ["@babel/preset-env"] }) retour { fichier d'entrée, personnes à charge, code }
Les résultats sont les suivants :
Utilisez la récursivité ou la boucle pour importer les fichiers un par un pour l'analyse des dépendances. Notez ici que nous utilisons la boucle for pour analyser toutes les dépendances. La raison pour laquelle les boucles peuvent analyser toutes les dépendances est que la longueur des modules change lorsqu'il y a des dépendances.modules .push. nouvelles dépendances, modules.length changera.
pour (soit i = 0; i < this.modules.length; i++) { const item = this.modules[i]; const { personnes à charge } = élément ; si (personnes à charge) { pour (soit j dans les personnes à charge) { this.modules.push(this.parse(dependents[j])); } } }
Étape 3 : Écrivez la fonction WebpackBootstrap + générez le fichier de sortie.
Écrivez la fonction WebpackBootstrap : La première chose que nous devons faire ici est la fonction WebpackBootstrap. Après compilation, l'importation de notre code source sera analysée depuis le navigateur. ne reconnaît pas require, alors nous devons d'abord le déclarer. Après tout, require est une méthode lors de l'écriture de fonctions, vous devez également faire attention à l'isolement de la portée pour éviter la pollution variable. Nous devons également déclarer les exportations dans notre code pour nous assurer que les exportations existent déjà lorsque le code est exécuté.
Générer le fichier de sortie : Nous avons déjà écrit l'adresse du fichier généré dans le fichier de configuration, puis utilisons fs.writeFileSync pour l'écrire dans le dossier de sortie.
fichier(code) { const filePath = chemin.join(this.output.path, this.output.filename) const newCode = JSON.stringify(code); // Générer le contenu du fichier bundle const bundle = `(function(modules){ fonction require(module){ fonction pathRequire(relativePath){ return require(modules[module].dependents[relativePath]) } const exports={}; (fonction (exiger, exportations, code) { évaluer(code) })(pathRequire,exports,modules[module].code); retour des exportations } require('${this.entry}') })(${nouveauCode})`; // WebpackBoostrap // Génère un fichier. Mettez-le dans le répertoire dist fs.writeFileSync(filePath,bundle,'utf-8') }
Étape 4 : Analyser la séquence d'exécution
Nous pouvons exécuter le résultat packagé dans la console du navigateur. S'il fonctionne normalement, bonjour Webpack devrait être imprimé.
Grâce à l'analyse ci-dessus, nous devrions avoir une compréhension de base du processus général de Webpack. L'utilisation d'AST pour analyser le code n'est qu'un moyen de cette démonstration, et non la véritable implémentation de Webpack qui possède sa propre méthode d'analyse AST. en constante évolution. L'écosystème Webpack est très complet. Les enfants intéressés peuvent se poser les trois questions suivantes :