Webpack – Le packager du futur pour des applications front modulaires



Webpack – Le packager du futur pour des applications front modulaires

0 1


xke-webpack-reveal


On Github antogyn / xke-webpack-reveal

Webpack

Le packager du futur pour des applications front modulaires

Par :

Slides :https://antogyn.github.io/xke-webpack-reveal/ Repo :https://github.com/antogyn/xke-webpack-boilerplates/

Webpack c'est quoi ?

“Webpack takes modules with dependencies and generates static assets representing those modules.”
N'importe quel type de fichiers Les gère comme des modules et comprend leurs dépendances

Un exemple simple

  • Un fichier html racine, notre template de base
  • Un fichier JS racine, celui de mon application
  • Un fichier JS, d'un composant
  • Un fichier css, le style du composant
  • Un fichier html, le template du composant
ES6, compilé avec babel
<!-- index.html -->
<my-foo></my-foo>
<script charset="utf-8" src="dist/app.js"></script>
// app.js
import './components/my-foo/my-foo';
<!-- my-foo.html -->
<p>Hello, je suis my-foo !</p>
/* my-foo.css */      
my-foo {
  background-color: blue; /* et je suis bleu ! */
  font-size: 3em;
}
// my-foo.js
import template from './my-foo.html';
import './my-foo.css';

// création du custom element my-foo
const MyFooProto = Object.create(HTMLElement.prototype);
MyFooProto.createdCallback = function() {
  this.innerHTML = template; // utilisation du template importé
};
document.registerElement('my-foo', {prototype: MyFooProto});

Le résultat ?

webpack.config.js

module.exports = {
  context: __dirname, // la racine de l'app (fs)
  entry: {
    app: './public/app.js' // le point d'entrée de l'app
  },
  output: {
    path: __dirname + '/dist', // le path absolu de l'output (fs)
    filename: 'app.js', // le nom de l'output
    publicPath: '/dist/' // l'url de l'output relatif au host
  },
  module: {
    loaders: [
      {
        test: /\.html$/, // si je rencontre un import de fichier html...
        loader: 'html' //... alors j'utilise le loader html
      },
      {
        test: /\.css/, // si je rencontre un import de fichier css...
        loader: 'style!css' //... alors j'utilise les loaders style et css
      },
      {
        test: /\.js$/, // si je rencontre un import de fichier js...
        exclude: /node_modules/,//... qui n'est pas dans node_module/...
        loader: 'babel' //... alors j'utilise le loader babel
      }
      // pour tout le reste, webpack utilise le js loader (built-in)
    ]
  }
};

Lancer Webpack

webpack
# Dev : sourcemaps
webpack -d [--watch]
# Prod : minification, optimisations
webpack -p

Allons plus loin...

Déplacer les styles dans un fichier à part

Dans notre config webpack :
var ExtractTextPlugin = require('extract-text-webpack-plugin');
plugins: [
  new ExtractTextPlugin('app.css')
],
{
  test: /\.css/,
  loader: ExtractTextPlugin.extract('style', 'css')
},
Dans notre html :
<!-- index.html -->
<link rel=stylesheet type="text/css" href="dist/app.css">

Linter le code

module: {
  preLoaders: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      loader: "eslint"
    }
  ],

Zipper le bundle

var CompressionPlugin = require('compression-webpack-plugin');
plugins: [
  new CompressionPlugin()
]

Et mes images dans mon html/css ? Mes fonts ?

Les loaders html et style sont capables de reconnaître leurs dépendances
<!-- url relative dans mes sources -->
<img src="my-foo.png" />
@font-face {
  font-family: myFont;
  src: url(my-font.woff); /* idem */
}
Et on les charge grâce au file loader
file loader fait 2 choses : Copie des fichiers dans dist/ Changer les src/url dans img/font
loaders: [
  {
    test: /\.png$/,
    loader: "file?name=img/[name].[ext]"
  },
  {
    test: /\.woff$/,
    loader: 'file?name=fonts/[name].[ext]'
  },

Vous vous souvenez de lui ?

output: {
  path: __dirname + '/dist',
  filename: 'app.js',
  publicPath: '/dist/' // <===== ici
}
Webpack préfixe les urls par le public path !
<img src="my-foo.png">
devient
<img src="/dist/img/my-foo.png">
Webpack a besoin de savoir à quelle url sont servis les assets

Ajouter un hash au nom du bundle

Dans notre config webpack :
output: {
  ...
  filename: 'app-[hash].js',
var BundleTracker = require('webpack-bundle-tracker');
plugins: [
  new BundleTracker({
    path: __dirname,
    filename: 'webpack-manifest.json'})
],
Un fichier json est créé, dans lequel on retrouve le bundle
"name": "app-0a277e942b25.js",
On utilise une librairie de templating (ici mustache)
<!-- index.mustache -->
<my-foo></my-foo>
{{#chunks.app}}
<script charset="utf-8" src="dist/{{name}}"></script>
{{/chunks.app}}
Notre index compilé récupère le nom hashé
mustache webpack-manifest.json index.mustache > index.html
<!-- index.html -->
<my-foo></my-foo>
<script charset="utf-8" src="dist/app-0a277e942b25.js"></script>

Compiler des fichiers sass

Dans notre config webpack :
{
  test: /\.scss$/,  // si je rencontre un import de fichier scss...
  loaders: ["style", "css", "sass"] //... alors j'utilise les loaders sass,
                                    // css et style
}
Pour ajouter les sources map (en plus de webpack -d)
{
  test: /\.scss$/,
  loaders: ["style", "css?sourceMap", "sass?sourceMap"]
}

Séparer les vendors (libs) de mon app

var webpack = require('webpack');
  entry: {
    app: './public/app.js',
    vendors: ['jquery'] // <= va faire un require('jquery');
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin('vendors', 'vendors.js')
  ],
// app.js
import $ from 'jquery';
$('body').css('background-color', 'yellow');
<!-- index.html -->
<script charset="utf-8" src="dist/vendors.js"></script>
<script charset="utf-8" src="dist/app.js"></script>
Le code de jQuery n'est présent que dans vendors.js !

Méthode 2 : créer son propre module important tous les vendors

// vendors.js
import 'jquery';
entry: {
  app: './public/app.js',
  vendors: './public/vendors.js' // <=
},

Lazy loading

Tout notre bundle est chargé, même les modules qui ne sont pas immédiatement utiles
Pour avoir un chargement initial plus rapide, j'aimerais découper mon bundle et charger les modules à la volée
<!-- index.html -->
<button id="mybutton">Make everything yellow !</button>
<script charset="utf-8" src="dist/app.js"></script>
// mybutton.js
import $ from 'jquery';

export default {
  makeItYellow() {
    $('body').css('background-color', 'yellow');
  }
};
// app.js
import $ from 'jquery'

$('#mybutton').click(() => {
  require.ensure([], () => {
    const mybutton = require('./mybutton').default;
    mybutton.makeItYellow();
  });
});
(démo)

Mettre en place un server de dev qui met à jour le browser à chaque changement

Après avoir installé webpack-dev-server :
webpack-dev-server --inline --hot --content dist/
Le server démarre, les assets sont compilés et placés en mémoire.
Les modifications se feront à chaud (hot module replacement) si possible, sinon en livereload.
(démo)
(react-hot-loader)

En fait, Webpack...

...convient à tous les projets !

Quelle que soit la taille Petit = clareté du code Grand = partage de composants/modules Evolution de la config de manière incrémentale Evite d'avoir X tâches gulp/grunt
tada

Questions ?

Webpack Le packager du futur pour des applications front modulaires Par : Anthony Giniers / @aginiers Antoine le Taxin / @modulom Slides : https://antogyn.github.io/xke-webpack-reveal/ Repo : https://github.com/antogyn/xke-webpack-boilerplates/