Webpack para Rápidos y Furiosos

También publicado en mi blog de tecnología

Esta es una guía que está destinada a ayudarlo a facilitar su flujo de trabajo de desarrollo y ahorrar tiempo mediante el uso de un montón de herramientas increíbles sobre las que ha leído en Internet (¿React Hot Loader le suena?)

También está destinado a ayudarlo con algunos de los problemas más comunes al usar Webpack, y ahorrar algo de tiempo en el proceso antes de comenzar a arrancarse los pelos. Después de todo, desea ir rápido y resolver otros problemas importantes.

Es probable que se haya encontrado con uno o más de los siguientes problemas:

  • ¿Cómo tengo varias entradas?
  • ¿Cómo calzo los módulos?
  • Una de las bibliotecas / complementos que uso depende de jQuery, ¿cómo lo manejo?
  • Sigo recibiendo $ no está definido o alguna estupidez como esa en uno de los complementos de jQuery
  • Mi paquete tarda una eternidad en terminar.
  • Leí un montón de tutoriales sobre Cómo reemplazar el módulo para ReactJS y creo que es realmente genial, pero sigo encontrando errores al configurarlo.

Si se encuentra con estas dificultades, termine este artículo antes de recurrir a publicar una de estas preguntas en Stack Overflow.

Supongo que ya conoce las ventajas de Webpack y para qué se utiliza. Si eres un principiante y no tienes ni idea de qué es Webpack, te recomiendo que lo leas aquí.

También supongo que está creando una aplicación web y no solo una página estática, lo que significa que tendrá un servidor web ejecutándose en Node y Express. Lo más probable es que también use un controlador NodeJS para comunicarse con su base de datos, probablemente MongoDB o Redis.

Así que así es como se ve un webpack.config.js típico :

/** * @Author Ashwin Hariharan * @Details Webpack config file for adding new vendors, defining entry points and shimming modules. */ var webpack = require('webpack'); var path = require("path"); var lib_dir = __dirname + '/public/libs', node_dir = __dirname + '/node_modules'; // bower_dir = __dirname + '/bower_components' var config = { resolve: { alias: { react: node_dir + '/react', reactDom: lib_dir + '/react-dom', jquery: lib_dir + '/jquery-1.11.2.min.js', magnificPopup: lib_dir + '/jquery.magnific-popup.js' //JQuery Plugin } }, entry: { app: ['./public/src/js/app-main'], vendors: ['react','reactDom','jquery','magnificPopup'] }, output: { path: path.join(__dirname, "public"), filename: "dist/js/[name].bundle.js" }, plugins: [ new webpack.ProvidePlugin({ jQuery: "jquery", 'window.jQuery': "jquery" }), new webpack.optimize.CommonsChunkPlugin('vendors', 'dist/js/vendors.js', Infinity), ], module: { noParse: [ new RegExp(lib_dir + '/react.js'), new RegExp(lib_dir +'/jquery-1.11.2.min.js') ], loaders: [ { test: /\.js$/, loader: 'babel', query: { presets: ['react', 'es2015'] } }, ] } }; module.exports = config;

Esta configuración asume que ha usado algunos módulos de nodo y la versión dist de algunas bibliotecas guardadas dentro de una carpeta pública / libs . Ahora, si ha leído otros tutoriales, comprende lo que hacen las configuraciones en este archivo, sin embargo, todavía voy a explicar brevemente para qué sirven algunas cosas en este archivo:

  • Alias ​​/ proveedores

    Aquí es donde incluye todas sus bibliotecas / módulos de nodo / otros proveedores y asigna cada uno de ellos a alias. Luego, si usa un módulo en cualquier parte de la lógica de su aplicación, puede escribir esto (en su app-main.js o cualquier otro archivo JS):

var React = require(‘react’); var ReactDom = require('reactDom'); var $ = require('jquery'); //Your application logic

O si prefiere AMD sobre CommonJS:

define( [ ‘react’, ’reactDom’, ’jquery’ ], function(React, ReactDom, $) { //Your application logic } );

O en ES6 también:

import React from 'react'; import ReactDom from 'reactDom'; import $ from 'jquery';
  • Definiendo sus puntos de entrada
entry: { }

Este bloque en su configuración le permite a Webpack determinar dónde comienza la ejecución de su aplicación y crea fragmentos de ella. Tener múltiples puntos de entrada en su aplicación siempre es una ventaja. En particular, puede agregar todos los archivos de su proveedor, como jQuery y ReactJS, en un solo fragmento. De esta forma, los archivos de su proveedor seguirán siendo los mismos, incluso cuando modifique sus archivos de origen.

Entonces, en la configuración anterior, hay dos puntos de entrada. Uno para la entrada de su aplicación donde comienza su JS, y otro para sus proveedores, cada uno de ellos asignado a un nombre de variable.

  • Su directorio de salida y nombres de archivo de paquete
output: { path: path.join(__dirname, “public”), filename: “dist/js/[name].bundle.js” },

Este bloque le dice a Webpack cómo nombrar sus archivos después del proceso de compilación y dónde colocarlos. En nuestro ejemplo, tenemos dos entradas denominadas aplicación y proveedores , por lo que después del proceso de compilación tendrá dos archivos llamados app.bundle.js y vendors.bundle.js dentro del directorio / public / dist / js .

  • Complementos

Webpack viene con un rico ecosistema de complementos para ayudar a satisfacer necesidades específicas. Explicaré brevemente algunos de los más utilizados:

  • Use CommonsChunkPlugin para que Webpack determine qué código / módulos usa más y colóquelo en un paquete separado para usar en cualquier lugar de su aplicación.
  • Opcionalmente, puede usar ProvidePlugin para inyectar globales. Hay muchos complementos de jQuery que dependen de una variable jQuery global como $, por lo que al usar este complemento, Webpack puede anteponer var $ = require ("jquery") cada vez que encuentra el identificador $ global . Lo mismo ocurre con cualquier otro complemento, como Bootstrap.

Al incluir noParse, puede decirle a Webpack que no analice ciertos módulos. Esto es útil cuando solo tiene la versión dist de estos módulos / bibliotecas. Mejora el tiempo de construcción.

  • Cargadores

Ahora, si escribe JSX en su código de React, puede usar jsx-loader o babel-loader para precompilar JSX en JavaScript. Entonces puede ejecutar npm install jsx-loader e incluir esto en su configuración:

loaders: [ { test: /\.js$/, loader: 'jsx-loader' }, ]

Sin embargo, si escribe su código en JSX y ES6, deberá usar el cargador de babel, junto con el complemento de babel para React. Así que ejecute npm install babel-core babel-loader babel-preset-es2015 babel-preset-react y luego agregue esto a su configuración en lugar de lo anterior.

loaders: [ { test: /\.js$/, loader: ‘babel’, query: { presets: [‘react’, ‘es2015’] }, include: path.join(__dirname, ‘public’) } ]

Asimismo, dispone de cargadores para compilar TypeScript, CoffeeScript, etc.

Ejemplo

  • Su archivo de servidor web:
var http = require("http"); var express = require("express"); var consolidate = require('consolidate'); var handlebars = require('handlebars'); var bodyParser = require('body-parser'); var routes = require('./routes'); var app = express(); //Set the folder-name from where you serve the html page. app.set('views', 'views'); //For using handlebars as the template engine. app.set('view engine', 'html'); app.engine('html', consolidate.handlebars); //Set the folder from where you serve all static files like images, css, javascripts, libraries etc app.use(express.static('./public')); app.use(bodyParser.urlencoded({ extended: true })); var portNumber = 8000; http.createServer(app).listen(portNumber, function(){ console.log('Server listening at port '+ portNumber); app.get('/', function(req, res){ console.log('request to / received'); res.render('index.html'); }); });
  • app-main.js desde donde comienza nuestra lógica de front-end:
define( [ ‘react’, ’reactDom’, ’./components/home-page’ ], function(React, ReactDom, HomePage){ console.log(‘Loaded the Home Page’); ReactDom.render(, document.getElementById(‘componentContainer’)); } );
  • home-page.js es nuestro componente principal de React que podría contener algo como esto:
define(['react', 'jquery', 'magnificPopup'], function(React, $) { var HomePage = React.createClass({ getInitialState: function() { return { userName: 'ashwin' } }, componentDidMount: function() { $('.test-popup-link').magnificPopup({ type: 'image' // other options }); }, render: function() { return ( {this.state.userName} Open popup ); } }); return HomePage; });

Opening your terminal, going to your project’s root folder and running webpack will create two files: vendors.bundle.js and app.bundle.js. Include these two files in your index.html and hit //localhost:8000 in your browser. This will render a component with your username displayed on the web page.

Now, as you work more on Webpack, you’ll get frustrated by constantly having to build your files manually to see changes reflected on your browser. Wouldn’t it be awesome if there was a way to automate the build process every time you make a change to a file? So if you’re tired of typing the command webpack and hitting the refresh button on your browser every time you change a class name, do read on…

Automating Builds with Webpack Dev Server and React Hot Loader

We will use this awesome module called Webpack Dev Server. It’s an express server which runs on port 8080 and emits information about the compilation state to the client via a socket connection. We will also use React Hot Loader which is plugin for Webpack that allows instantaneous live refresh without losing state while editing React components.

  • Step 1: So go run npm install webpack-dev-server — save-dev and then npm install react-hot-loader — save-dev

Then you need to tweak your Webpack config a little to use this plugin. In your loaders, add this before any other loader:

{ test: /\.jsx?$/, loaders: [‘react-hot’], include: path.join(__dirname, ‘public’) }

This tells Webpack to use React Hot Loader for your components. Make sure React Hot Loader comes before Babel in the loaders array. Also make sure you have include: path.join(__dirname, ‘public’) to avoid processing node_modules, or you may get an error like this:

Uncaught TypeError: Cannot read property ‘NODE_ENV’ of undefined

  • Step 2: Changes to your index.html

If your index.html has something like this:

Change this to point to your webpack-dev-server proxy:

  • Step 3: Run webpack-dev-server --hot --inline,

wait for the bundling to finish, then hit //localhost:8000 (your express server port) in your browser.

If you run into any errors while setting up React Hot Loader, you’ll find this troubleshooting guide and this awesome answer on Stack Overflow on Managing jQuery Plugin Dependency with Webpack very helpful. In addition, you can take a look at the Webpack setup for my projects here and here.

This is only meant for development. While in production, you need to minify all your files. Just running webpack -p will minify/uglify/concatenate all your files.

Wouldn’t it be awesome if there was a way to view all your file dependencies in a beautiful tree-like visualization? There is a web-app which does that.

In your terminal, run webpack — profile — json > stats.json. This will generate a JSON file called stats.json. Go to //webpack.github.io/analyse/ and upload the file, and you’ll see all dependencies in a tree like structure.

Liked what you read? You should subscribe. I won’t waste your time.