Le concept de modularisation fait partie intégrante de la plupart des langages de programmation modernes. JavaScript, cependant, manquait d'approche formelle de la modularisation jusqu'à l'arrivée de la dernière version d'ECMAScript ES6.
Dans Node.js, l'un des frameworks JavaScript les plus populaires d'aujourd'hui, les bundleurs de modules permettent le chargement Modules NPM dans les navigateurs Web et les bibliothèques orientées composants (comme React) encouragent et facilitent la modularisation du code JavaScript.
Webpack est l'un des bundlers de modules disponibles qui traite JavaScript code, ainsi que tous les actifs statiques, tels que les feuilles de style, les images et les polices, dans un fichier groupé. Le traitement peut inclure toutes les tâches nécessaires pour gérer et optimiser les dépendances de code, telles que la compilation, la concaténation, la minification et la compression.
Cependant, la configuration de Webpack et de ses dépendances peut être stressante et n'est pas toujours un processus simple, en particulier pour les débutants.
qu'est-ce que le pom dans le sélénium
Cet article de blog fournit des instructions, avec des exemples, sur la manière de configurer Webpack pour différents scénarios et souligne les pièges les plus courants liés au regroupement des dépendances de projet à l'aide de Webpack.
La première partie de ce billet de blog explique comment simplifier la définition des dépendances dans un projet. Ensuite, nous discutons et démontrons la configuration pour le fractionnement de code d'applications à plusieurs pages et à une seule page. Enfin, nous expliquons comment configurer Webpack, si nous voulons inclure des bibliothèques tierces dans notre projet.
Les chemins relatifs ne sont pas directement liés aux dépendances, mais nous les utilisons lorsque nous définissons des dépendances. Si une structure de fichier de projet est complexe, il peut être difficile de résoudre les chemins de module pertinents. L'un des avantages les plus fondamentaux de la configuration Webpack est qu'elle permet de simplifier la définition des chemins relatifs dans un projet.
Supposons que nous ayons la structure de projet suivante:
- Project - node_modules - bower_modules - src - script - components - Modal.js - Navigation.js - containers - Home.js - Admin.js
Nous pouvons référencer les dépendances par des chemins relatifs vers les fichiers dont nous avons besoin, et si nous voulons importer des composants dans des conteneurs de notre code source, cela ressemble à ceci:
Home.js
Import Modal from ‘../components/Modal’; Import Navigation from ‘../components/Navigation’;
Modal.js
import {datepicker} from '../../../../bower_modules/datepicker/dist/js/datepicker';
Chaque fois que nous voulons importer un script ou un module, nous devons connaître l'emplacement du répertoire courant et trouver le chemin relatif de ce que nous voulons importer. Nous pouvons imaginer comment ce problème peut devenir plus complexe si nous avons un gros projet avec une structure de fichiers imbriquée ou si nous voulons refactoriser certaines parties d'une structure de projet complexe.
Nous pouvons facilement gérer ce problème avec le Webpack resolve.alias
option. Nous pouvons déclarer des soi-disant alias - le nom d’un répertoire ou d’un module avec son emplacement, et nous ne nous appuyons pas sur des chemins relatifs dans le code source du projet.
webpack.config.js
resolve: { alias: { 'node_modules': path.join(__dirname, 'node_modules'), 'bower_modules': path.join(__dirname, 'bower_modules'), } }
Dans le Modal.js
fichier, nous pouvons maintenant importer un sélecteur de date beaucoup plus simple:
import {datepicker} from 'bower_modules/datepicker/dist/js/datepicker';
Nous pouvons avoir des scénarios où nous devons ajouter un script dans le bundle final, ou diviser le bundle final, ou nous voulons charger des bundles séparés à la demande. La configuration de notre projet et de la configuration de Webpack pour ces scénarios peut ne pas être simple.
Dans la configuration Webpack, le Entry
L'option indique à Webpack où se trouve le point de départ du bundle final. Un point d'entrée peut avoir trois types de données différents: chaîne, tableau ou objet.
Si nous n'avons qu'un seul point de départ, nous pouvons utiliser n'importe lequel de ces formats et obtenir le même résultat.
Si nous voulons ajouter plusieurs fichiers et qu'ils ne dépendent pas les uns des autres, nous pouvons utiliser un format Array. Par exemple, nous pouvons ajouter analytics.js
à la fin du bundle.js
:
webpack.config.js
module.exports = { // creates a bundle out of index.js and then append analytics.js entry: ['./src/script/index.jsx', './src/script/analytics.js'], output: { path: './build', filename: bundle.js ' } };
Supposons que nous ayons une application multi-pages avec plusieurs fichiers HTML, tels que index.html
et admin.html
. Nous pouvons générer plusieurs bundles en utilisant le point d'entrée comme type d'objet. La configuration ci-dessous génère deux bundles JavaScript:
webpack.config.js
module.exports = { entry: { index: './src/script/index.jsx', admin: './src/script/admin.jsx' }, output: { path: './build', filename: '[name].js' // template based on keys in entry above (index.js & admin.js) } };
index.html
admin.html
CommonsChunkPlugin
webpack.config.js
Les deux ensembles JavaScript peuvent partager des bibliothèques et des composants communs. Pour cela, nous pouvons utiliser var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js'); module.exports = { entry: { index: './src/script/index.jsx', admin: './src/script/admin.jsx' }, output: { path: './build', filename: '[name].js' // template based on keys in entry above (index.js & admin.js) }, plugins: [commonsPlugin] };
, qui recherche les modules qui se produisent dans plusieurs blocs d'entrée et crée un bundle partagé qui peut être mis en cache entre plusieurs pages.
require.ensure
System.import
Maintenant, nous ne devons pas oublier d'ajouter avant les scripts fournis.
Webpack peut diviser les actifs statiques en morceaux plus petits, et cette approche est plus flexible que la concaténation standard. Si nous avons une grande application d'une seule page (SPA), la simple concaténation en un seul bundle n'est pas une bonne approche car le chargement d'un énorme bundle peut être lent et les utilisateurs n'ont généralement pas besoin de toutes les dépendances sur chaque vue.
qui rend compte au directeur financier
Nous avons expliqué précédemment comment diviser une application en plusieurs bundles, concaténer des dépendances communes et bénéficier du comportement de mise en cache du navigateur. Cette approche fonctionne très bien pour les applications multipages, mais pas pour les applications monopages.
Pour le SPA, nous ne devons fournir que les actifs statiques nécessaires au rendu de la vue actuelle. Le routeur côté client dans l'architecture SPA est un endroit idéal pour gérer le fractionnement de code. Lorsque l'utilisateur entre une route, nous ne pouvons charger que les dépendances nécessaires pour la vue résultante. Alternativement, nous pouvons charger des dépendances lorsque l'utilisateur fait défiler une page.
Pour cela, nous pouvons utiliser admin.jsx
ou import React, {Component} from 'react'; export default class Admin extends Component { render() { return Admin ; } }
fonctions, que Webpack peut détecter de manière statique. Webpack peut générer un bundle séparé basé sur ce point de partage et l'appeler à la demande.
Dans cet exemple, nous avons deux conteneurs React; une vue d'administration et une vue de tableau de bord.
dashboard.jsx
import React, {Component} from 'react'; export default class Dashboard extends Component { render() { return Dashboard ; } }
/dashboard
/admin
Si l'utilisateur entre le index.jsx
ou if (window.location.pathname === '/dashboard') { require.ensure([], function() { require('./containers/dashboard').default; }); } else if (window.location.pathname === '/admin') { require.ensure([], function() { require('./containers/admin').default; }); }
URL, seul le bundle JavaScript requis correspondant est chargé. Ci-dessous, nous pouvons voir des exemples avec et sans le routeur côté client.
index.jsx
ReactDOM.render( {props.children} }> { require.ensure([], function (require) { cb(null, require('./containers/dashboard').default) }, 'dashboard')}} /> { require.ensure([], function (require) { cb(null, require('./containers/admin').default) }, 'admin')}} /> , document.getElementById('content') );
style-loader
css-loader
Dans Webpack, chargeurs , comme ExtractTextWebpackPlugin
et webpack.config.js
, prétraitez les feuilles de style et intégrez-les dans le bundle JavaScript de sortie, mais dans certains cas, elles peuvent provoquer le Flash de contenu sans style (FOUC) .
On peut éviter le FOUC avec var ExtractTextPlugin = require('extract-text-webpack-plugin'); module.exports = { module: { loaders: [{ test: /.css/, loader: ExtractTextPlugin.extract('style', 'css’)' }], }, plugins: [ // output extracted CSS to a file new ExtractTextPlugin('[name].[chunkhash].css') ] }
qui permet de générer tous les styles dans des bundles CSS séparés au lieu de les intégrer dans le bundle JavaScript final.
$
jQuery
Souvent, nous devons utiliser des bibliothèques tierces, divers plugins ou des scripts supplémentaires, car nous ne voulons pas passer du temps à développer les mêmes composants à partir de zéro. Il existe de nombreuses bibliothèques et plugins hérités disponibles qui ne sont pas activement maintenus, ne comprennent pas les modules JavaScript et supposent la présence de dépendances globalement sous des noms prédéfinis.
Vous trouverez ci-dessous quelques exemples avec les plugins jQuery, avec une explication sur la façon de configurer correctement Webpack pour pouvoir générer le bundle final.
La plupart des plugins tiers reposent sur la présence de dépendances globales spécifiques. Dans le cas de jQuery, les plugins reposent sur $(‘div.content’).pluginFunc()
ou ProvidePlugin
variable en cours de définition, et nous pouvons utiliser les plugins jQuery en appelant var $ = require('jquery')
dans notre code.
Nous pouvons utiliser le plugin Webpack $
ajouter webpack.config.js
chaque fois qu'il rencontre le global webpack.ProvidePlugin({ ‘$’: ‘jquery’, })
identifiant.
$
require
Lorsque Webpack traite le code, il recherche la présence $
et fournit une référence aux dépendances globales sans importer le module spécifié par this
fonction.
Certains plugins jQuery supposent que window
dans l'espace de noms global ou s'appuyer sur imports-loader
étant le example.js
objet. Pour cela, nous pouvons utiliser $(‘div.content’).pluginFunc();
qui injecte des variables globales dans les modules.
portefeuille de concepteurs ux / ui
$
imports-loader
Ensuite, nous pouvons injecter le require('imports?$=jquery!./example.js');
variable dans le module en configurant le var $ = require('jquery');
:
example.js
Ceci ajoute simplement webpack.config.js
à module: { loaders: [{ test: /jquery-plugin/, loader: 'imports?jQuery=jquery,$=jquery,this=>window' }] }
.
Dans le deuxième cas d'utilisation:
=>
this
En utilisant le window
symbole (à ne pas confondre avec le Fonctions ES6 Arrow ), nous pouvons définir des variables arbitraires. La dernière valeur redéfinit la variable globale (function () { ... }).call(window);
pour pointer vers le this
objet. Cela revient à envelopper tout le contenu du fichier avec window
et appel // CommonJS var $ = require('jquery'); // jquery is available // AMD define([‘jquery’], function($) { // jquery is available });
fonction avec jquery-plugin.js
comme argument.
Nous pouvons également exiger des bibliothèques utilisant le format de module CommonJS ou AMD:
(function(factory) { if (typeof define === 'function' && define.amd) { // AMD format is used define(['jquery'], factory); } else if (typeof exports === 'object') { // CommonJS format is used module.exports = factory(require('jquery')); } else { // Neither AMD nor CommonJS used. Use global variables. } });
Certaines bibliothèques et modules peuvent prendre en charge différents formats de module.
Dans l'exemple suivant, nous avons un plugin jQuery qui utilise le format de module AMD et CommonJS et a une dépendance jQuery:
webpack.config.js
exemple de document de conception pour application Web
module: { loaders: [{ test: /jquery-plugin/, loader: 'imports?define=>false,exports=>false' }] }
define
false
Nous pouvons choisir le format de module que nous voulons utiliser pour la bibliothèque spécifique. Si nous déclarons exports
égale à false
, Webpack n'analyse pas le module au format de module AMD, et si nous déclarons la variable expose-loader
à égal à webpack.config.js
, Webpack n'analyse pas le module au format de module CommonJS.
Si nous avons besoin d'exposer un module au contexte global, nous pouvons utiliser module: { loaders: [ test: require.resolve('jquery'), loader: 'expose-loader?jQuery!expose-loader?$' ] }
. Cela peut être utile, par exemple, si nous avons des scripts externes qui ne font pas partie de la configuration de Webpack et reposent sur le symbole dans l'espace de noms global, ou si nous utilisons des plug-ins de navigateur qui doivent accéder à un symbole dans la console du navigateur.
window.$ window.jQuery
externals
La bibliothèque jQuery est désormais disponible dans l'espace de noms global pour les autres scripts de la page Web.
webpack.config.js
Si nous voulons inclure des modules provenant de scripts hébergés en externe, nous devons les définir dans la configuration. Sinon, Webpack ne peut pas générer le bundle final.
Nous pouvons configurer des scripts externes en utilisant le externals: { react: 'React', 'react-dom': 'ReactDOM' }
option dans la configuration Webpack. Par exemple, nous pouvons utiliser une bibliothèque à partir d'un CDN via un séparateur, tout en la déclarant explicitement comme une dépendance de module dans notre projet.
project | |-- node_modules | |-- react |-- react-plugin | |--node_modules | |--react
react-plugin
C’est formidable d’utiliser le gestionnaire de packages NPM dans le développement frontal pour gérer les bibliothèques et dépendances tierces. Cependant, nous pouvons parfois avoir plusieurs instances de la même bibliothèque avec des versions différentes, et elles ne fonctionnent pas bien ensemble dans un même environnement.
Cela pourrait arriver, par exemple, avec la bibliothèque React, où nous pouvons installer React à partir de NPM et plus tard une version différente de React peut devenir disponible avec un package ou un plugin supplémentaire. Notre structure de projet peut ressembler à ceci:
webpack.config.js
Composants provenant du module.exports = { resolve: { alias: { 'react': path.join(__dirname, './node_modules/react'), 'react/addons': path.join(__dirname, '/node_modules/react/addons'), } } }
ont une instance de React différente de celle des autres composants du projet. Nous avons maintenant deux copies distinctes de React, et elles peuvent être des versions différentes. Dans notre application, ce scénario peut perturber notre DOM mutable global et nous pouvons voir des messages d'erreur dans le journal de la console Web. La solution à ce problème est d'avoir la même version de React tout au long du projet. Nous pouvons le résoudre par des alias Webpack.
react-plugin
node_modules
Quand console.log(React.version)
tente d'exiger React, il utilise la version du projet
|_+_|. Si nous voulons savoir quelle version de React nous utilisons, nous pouvons ajouter
|_+_|dans le code source.
Ce message ne fait qu'effleurer la surface de la puissance et de l'utilité de Webpack.
Il existe de nombreux autres Webpack chargeurs et plugins cela vous aidera à optimiser et à rationaliser le regroupement JavaScript.
Même si vous êtes débutant, ce guide vous donne une base solide pour commencer à utiliser Webpack, ce qui vous permettra de vous concentrer davantage sur le développement et moins sur la configuration de groupage.
En relation: Gardez le contrôle: un guide pour Webpack et React, Pt. 1