Keeping the frontend under control with Symfony and Webpack

Ignacio Martín
Ignacio MartínSenior software developer at Limenius um Limenius
Keeping the frontend
under control with
Symfony and Webpack
Nacho Martín
@nacmartin
nacho@limenius.com
Munich Symfony Meetup
October’16
I write code at Limenius
We build tailor made projects
using mainly Symfony
and React.js
So we have been figuring out how to organize better
the frontend
Nacho Martín
nacho@limenius.com @nacmartin
Why do we need this?
Assetic?
No module loading
No bundle orientation
Not a standard solution for frontenders
Other tools simply have more manpower
behind
Written before the Great Frontend Revolution
Building the Pyramids: 130K man years
Writing JavaScript: 10 man days
JavaScript
Making JavaScript great: NaN man years
JavaScript
Tendencies
Asset managers
Tendency
Task runners
Tendency
Task runners Bundlers
Task runners + understanding of
require(ments)
Tendency
Task runners Bundlers
Task runners + understanding of
require(ments)
Package management in JS
Server Side (node.js)
Bower
Client side (browser)
Used to be
Package management in JS
Server Side (node.js)
Bower
Client side (browser)
Used to be Now
Everywhere
Module loaders
Server Side (node.js)
Client side (browser)
Used to be
Module loaders
Server Side (node.js)
Client side (browser)
Used to be Now
Everywhere
&ES6 Style
Summarizing
Package manager Module loader
Module bundler
Setup
Directory structure
app/
bin/
src/
tests/
var/
vendor/
web/
assets/
Directory structure
app/
bin/
src/
tests/
var/
vendor/
web/
assets/
client/
js/
scss/
images/
Directory structure
app/
bin/
src/
tests/
var/
vendor/
web/
assets/
client/
js/
scss/
images/
NPM setup
$ npm init
$ cat package.json
{
"name": "webpacksf",
"version": "1.0.0",
"description": "Webpack & Symfony example",
"main": "client/js/index.js",
"directories": {
"test": "client/js/tests"
},
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"author": "Nacho Martín",
"license": "MIT"
}
Install Webpack
$ npm install -g webpack
$ npm install --save-dev webpack
Or, to install it globally
First example
var greeter = require('./greeter.js')
greeter('Nacho');
client/js/index.js
First example
var greeter = require('./greeter.js')
greeter('Nacho');
client/js/index.js
var greeter = function(name) {
console.log('Hi '+name+'!');
}
module.exports = greeter;
client/js/greeter.js
First example
var greeter = require('./greeter.js')
greeter('Nacho');
client/js/index.js
var greeter = function(name) {
console.log('Hi '+name+'!');
}
module.exports = greeter;
client/js/greeter.js
Webpack without configuration
$ webpack client/js/index.js web/assets/build/hello.js
Hash: 4f4f05e78036f9dc67f3
Version: webpack 1.13.2
Time: 100ms
Asset Size Chunks Chunk Names
hi.js 1.59 kB 0 [emitted] main
[0] ./client/js/index.js 57 bytes {0} [built]
[1] ./client/js/greeter.js 66 bytes {0} [built]
Webpack without configuration
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>{% block title %}Webpack & Symfony!{% endblock %}</title>
{% block stylesheets %}{% endblock %}
<link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}" />
</head>
<body>
{% block body %}{% endblock %}
{% block javascripts %}
<script src="{{ asset('assets/build/hello.js') }}"></script>
{% endblock %}
</body>
</html>
app/Resources/base.html.twig
Webpack config
module.exports = {
entry: {
hello: './client/js/index.js'
},
output: {
publicPath: '/assets/build/',
path: './web/assets/build',
filename: '[name].js'
}
};
webpack.config.js
Webpack config
module.exports = {
entry: {
hello: './client/js/index.js'
},
output: {
publicPath: '/assets/build/',
path: './web/assets/build',
filename: '[name].js'
}
};
webpack.config.js
Loaders
Now that we have modules,
What about using modern JavaScript?
(without caring about IE support)
Now that we have modules,
What about using modern JavaScript?
(without caring about IE support)
JavaScript ES2015
•Default Parameters
•Template Literals
•Arrow Functions
•Promises
•Block-Scoped Constructs Let and Const
•Classes
•Modules
•…
Why Babel matters
import Greeter from './greeter.js';
let greeter = new Greeter('Hi');
greeter.greet('gentlemen');
class Greeter {
constructor(salutation = 'Hello') {
this.salutation = salutation;
}
greet(name = 'Nacho') {
const greeting = `${this.salutation}, ${name}!`;
console.log(greeting);
}
}
export default Greeter;
client/js/index.js
client/js/greeter.js
Install babel
$ npm install --save-dev babel-core 
babel-loader babel-preset-es2015
Install babel
$ npm install --save-dev babel-core 
babel-loader babel-preset-es2015
module.exports = {
entry: {
hello: './client/js/index.js'
},
output: {
publicPath: '/assets/build/',
path: './web/assets/build',
filename: '[name].js'
},
module: {
loaders: [
{
test: /.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
]
}
};
webpack.config.js
Install babel
$ npm install --save-dev babel-core 
babel-loader babel-preset-es2015
module.exports = {
entry: {
hello: './client/js/index.js'
},
output: {
publicPath: '/assets/build/',
path: './web/assets/build',
filename: '[name].js'
},
module: {
loaders: [
{
test: /.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
]
}
};
webpack.config.js
Install babel
$ npm install --save-dev babel-core 
babel-loader babel-preset-es2015
module.exports = {
entry: {
hello: './client/js/index.js'
},
output: {
publicPath: '/assets/build/',
path: './web/assets/build',
filename: '[name].js'
},
module: {
loaders: [
{
test: /.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
]
}
};
webpack.config.js .babelrc
{
"presets": ["es2015"]
}
Install babel
$ npm install --save-dev babel-core 
babel-loader babel-preset-es2015
module.exports = {
entry: {
hello: './client/js/index.js'
},
output: {
publicPath: '/assets/build/',
path: './web/assets/build',
filename: '[name].js'
},
module: {
loaders: [
{
test: /.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
]
}
};
webpack.config.js .babelrc
{
"presets": ["es2015"]
}
Loaders
SASS
Markdown
Base64
React
Image
Uglify
…
https://webpack.github.io/docs/list-of-loaders.html
Loader gymnastics: (S)CSS
Loading styles
require(‘../css/layout.css');
//…
client/js/index.js
Loading styles: raw*
loaders: [
//…
{ test: /.css$/i, loader: 'raw'},
]
exports.push([module.id, "body {n line-height: 1.5;n padding: 4em 1em;n}nnh2 {n
margin-top: 1em;n padding-top: 1em;n}nnh1,nh2,nstrong {n color: #333;n}nna
{n color: #e81c4f;n}nn", ""]);
Embeds it into JavaScript, but…
*(note: if you are reading the slides, don’t use this loader for css. Use css loader, that will be explained later)
Chaining styles: style
loaders: [
//…
{ test: /.css$/i, loader: ’style!raw'},
]
CSS loader
header {
background-image: url("../img/header.jpg");
}
Problem
CSS loader
header {
background-image: url("../img/header.jpg");
}
url(image.png) => require("./image.png")
url(~module/image.png) => require("module/image.png")
We want
Problem
CSS loader
header {
background-image: url("../img/header.jpg");
}
url(image.png) => require("./image.png")
url(~module/image.png) => require("module/image.png")
We want
Problem
loaders: [
//…
{ test: /.css$/i, loader: ’style!css'},
]
Solution
File loaders
{ test: /.jpg$/, loader: 'file-loader' },
{ test: /.png$/, loader: 'url-loader?limit=10000' },
Copies file as [hash].jpg, and returns the public url
If file < 10Kb: embed it in data URL.
If > 10Kb: use file-loader
Using loaders
When requiring a file
In webpack.config.js, verbose
{
test: /.png$/,
loader: "url-loader",
query: { limit: "10000" }
}
require("url-loader?limit=10000!./file.png");
{ test: /.png$/, loader: 'url-loader?limit=10000' },
In webpack.config.js, compact
SASS
{ test: /.scss$/i, loader: 'style!css!sass'},
In webpack.config.js, compact
$ npm install --save-dev sass-loader node-sass
Also
{
test: /.scss$/i,
loaders: [ 'style', 'css', 'sass' ]
},
Embedding CSS in JS is good in
Single Page Apps
What if I am not writing a Single Page App?
ExtractTextPlugin
var ExtractTextPlugin = require("extract-text-webpack-plugin");
const extractCSS = new ExtractTextPlugin('stylesheets/[name].css');
const config = {
//…
module: {
loaders: [
{ test: /.css$/i, loader: extractCSS.extract(['css'])},
//…
]
},
plugins: [
extractCSS,
//…
]
};
{% block stylesheets %}
<link href="{{asset('assets/build/stylesheets/hello.css')}}"
rel="stylesheet">
{% endblock %}
app/Resources/base.html.twig
webpack.config.js
ExtractTextPlugin
var ExtractTextPlugin = require("extract-text-webpack-plugin");
const extractCSS = new ExtractTextPlugin('stylesheets/[name].css');
const config = {
//…
module: {
loaders: [
{ test: /.css$/i, loader: extractCSS.extract(['css'])},
//…
]
},
plugins: [
extractCSS,
//…
]
};
{% block stylesheets %}
<link href="{{asset('assets/build/stylesheets/hello.css')}}"
rel="stylesheet">
{% endblock %}
app/Resources/base.html.twig
webpack.config.js
{ test: /.scss$/i, loader: extractCSS.extract(['css','sass'])},
Also
Dev tools
Webpack-watch
$ webpack --watch
Simply watches for changes and recompiles the bundle
Webpack-dev-server
$ webpack-dev-server —inline
http://localhost:8080/webpack-dev-server/
Starts a server.
The browser opens a WebSocket connection with it
and reloads automatically when something changes.
Webpack-dev-server config Sf
{% block javascripts %}
<script src="{{ asset('assets/build/hello.js', 'webpack') }}"></script>
{% endblock %}
app/Resources/base.html.twig
framework:
assets:
packages:
webpack:
base_urls:
- "%assets_base_url%"
app/config/config_dev.yml
parameters:
#…
assets_base_url: 'http://localhost:8080'
app/config/parameters.yml
Webpack-dev-server config Sf
{% block javascripts %}
<script src="{{ asset('assets/build/hello.js', 'webpack') }}"></script>
{% endblock %}
app/Resources/base.html.twig
framework:
assets:
packages:
webpack:
base_urls:
- "%assets_base_url%"
app/config/config_dev.yml
framework:
assets:
packages:
webpack: ~
app/config/config.yml
parameters:
#…
assets_base_url: 'http://localhost:8080'
app/config/parameters.yml
Optional web-dev-server
Kudos Ryan Weaver
class AppKernel extends Kernel
{
public function registerContainerConfiguration(LoaderInterface $loader)
{
//…
$loader->load(function($container) {
if ($container->getParameter('use_webpack_dev_server')) {
$container->loadFromExtension('framework', [
'assets' => [
'base_url' => 'http://localhost:8080'
]
]);
}
});
}
}
Hot module replacement
output: {
publicPath: 'http://localhost:8080/assets/build/',
path: './web/assets/build',
filename: '[name].js'
},
Will try to replace the code without even page reload
$ webpack-dev-server --hot --inline
Hot module replacement
output: {
publicPath: 'http://localhost:8080/assets/build/',
path: './web/assets/build',
filename: '[name].js'
},
Will try to replace the code without even page reload
Needs full URL (so only in dev), or…
$ webpack-dev-server --hot --inline
Hot module replacement
output: {
publicPath: 'http://localhost:8080/assets/build/',
path: './web/assets/build',
filename: '[name].js'
},
$ webpack-dev-server --hot --inline --output-public-path
http://localhost:8080/assets/build/
Will try to replace the code without even page reload
Needs full URL (so only in dev), or…
$ webpack-dev-server --hot --inline
SourceMaps
const devBuild = process.env.NODE_ENV !== ‘production';
/…
if (devBuild) {
console.log('Webpack dev build');
config.devtool = 'eval-source-map';
} else {
SourceMaps
const devBuild = process.env.NODE_ENV !== ‘production';
/…
if (devBuild) {
console.log('Webpack dev build');
config.devtool = 'eval-source-map';
} else {
eval
source-map
hidden-source-map
inline-source-map
eval-source-map
cheap-source-map
cheap-module-source-map
Several options:
Notifier
$ npm install --save-dev webpack-notifier
module.exports = {
//…
plugins: [
new WebpackNotifierPlugin(),
]
};
webpack.config.js
Notifier
$ npm install --save-dev webpack-notifier
module.exports = {
//…
plugins: [
new WebpackNotifierPlugin(),
]
};
webpack.config.js
Notifier
$ npm install --save-dev webpack-notifier
module.exports = {
//…
plugins: [
new WebpackNotifierPlugin(),
]
};
webpack.config.js
Optimize for production
Optimization options
var WebpackNotifierPlugin = require('webpack-notifier');
var webpack = require(‘webpack');
const devBuild = process.env.NODE_ENV !== 'production';
const config = {
entry: {
hello: './client/js/index.js'
},
//…
};
if (devBuild) {
console.log('Webpack dev build');
config.devtool = 'eval-source-map';
} else {
console.log('Webpack production build');
config.plugins.push(
new webpack.optimize.DedupePlugin()
);
config.plugins.push(
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
);
}
module.exports = config;
Optimization options
var WebpackNotifierPlugin = require('webpack-notifier');
var webpack = require(‘webpack');
const devBuild = process.env.NODE_ENV !== 'production';
const config = {
entry: {
hello: './client/js/index.js'
},
//…
};
if (devBuild) {
console.log('Webpack dev build');
config.devtool = 'eval-source-map';
} else {
console.log('Webpack production build');
config.plugins.push(
new webpack.optimize.DedupePlugin()
);
config.plugins.push(
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
);
}
module.exports = config;
$ export NODE_ENV=production; webpack
Optimization options
var WebpackNotifierPlugin = require('webpack-notifier');
var webpack = require(‘webpack');
const devBuild = process.env.NODE_ENV !== 'production';
const config = {
entry: {
hello: './client/js/index.js'
},
//…
};
if (devBuild) {
console.log('Webpack dev build');
config.devtool = 'eval-source-map';
} else {
console.log('Webpack production build');
config.plugins.push(
new webpack.optimize.DedupePlugin()
);
config.plugins.push(
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
);
}
module.exports = config;
$ export NODE_ENV=production; webpack
Bundle visualizer
$ webpack --json > stats.json
https://chrisbateman.github.io/webpack-visualizer/
Bundle visualizer
$ webpack --json > stats.json
https://chrisbateman.github.io/webpack-visualizer/
More than one bundle
Separate entry points
var config = {
entry: {
front: './assets/js/front.js',
admin: './assets/js/admin.js',
},
output: {
publicPath: '/assets/build/',
path: './web/assets/build/',
filename: '[name].js'
},
Vendor bundles
var config = {
entry: {
front: './assets/js/front.js',
admin: './assets/js/admin.js',
'vendor-admin': [
'lodash',
'moment',
'classnames',
'react',
'redux',
]
},
plugins: [
extractCSS,
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor-admin',
chunks: ['admin'],
filename: 'vendor-admin.js',
minChunks: Infinity
}),
Common Chunks
var CommonsChunkPlugin = require(“webpack/lib/optimize/CommonsChunkPlugin”);
module.exports = {
entry: {
page1: "./page1",
page2: "./page2",
},
output: {
filename: "[name].chunk.js"
},
plugins: [
new CommonsChunkPlugin("commons.chunk.js")
]
}
Produces page1.chunk.js, page2.chunk.js and commons.chunk.js
On demand loading
class Greeter {
constructor(salutation = 'Hello') {
this.salutation = salutation;
}
greet(name = 'Nacho', goodbye = true) {
const greeting = `${this.salutation}, ${name}!`;
console.log(greeting);
if (goodbye) {
require.ensure(['./goodbyer'], function(require) {
var goodbyer = require('./goodbyer');
goodbyer(name);
});
}
}
}
export default Greeter;
module.exports = function(name) {
console.log('Goodbye '+name);
}
greeter.js
goodbyer.js
On demand loading
class Greeter {
constructor(salutation = 'Hello') {
this.salutation = salutation;
}
greet(name = 'Nacho', goodbye = true) {
const greeting = `${this.salutation}, ${name}!`;
console.log(greeting);
if (goodbye) {
require.ensure(['./goodbyer'], function(require) {
var goodbyer = require('./goodbyer');
goodbyer(name);
});
}
}
}
export default Greeter;
module.exports = function(name) {
console.log('Goodbye '+name);
}
greeter.js
goodbyer.js
Hashes
output: {
publicPath: '/assets/build/',
path: './web/assets/build',
filename: '[name].js',
chunkFilename: "[id].[hash].bundle.js"
},
Chunks are very configurable
https://webpack.github.io/docs/optimization.html
Practical cases
Provide plugin
plugins: [
new webpack.ProvidePlugin({
_: 'lodash',
$: 'jquery',
}),
]
$("#item")
_.find(users, { 'age': 1, 'active': true });
These just work without requiring them:
Exposing jQuery
{ test: require.resolve('jquery'), loader: 'expose?$!expose?jQuery' },
Exposes $ and jQuery globally in the browser
Dealing with a mess
require('imports?define=>false&exports=>false!blueimp-file-upload/js/vendor/jquery.ui.widget.js');
require('imports?define=>false&exports=>false!blueimp-load-image/js/load-image-meta.js');
require('imports?define=>false&exports=>false!blueimp-file-upload/js/jquery.iframe-transport.js');
require('imports?define=>false&exports=>false!blueimp-file-upload/js/jquery.fileupload.js');
require('imports?define=>false&exports=>false!blueimp-file-upload/js/jquery.fileupload-process.js');
require('imports?define=>false&exports=>false!blueimp-file-upload/js/jquery.fileupload-image.js');
require('imports?define=>false&exports=>false!blueimp-file-upload/js/jquery.fileupload.js');
require('imports?define=>false&exports=>false!blueimp-file-upload/js/jquery.fileupload-validate.js');
require('imports?define=>false&exports=>false!blueimp-file-upload/js/jquery.fileupload-ui.js');
Broken packages that are famous
have people that have figured out how to work
with them
CopyWebpackPlugin for messes
new CopyWebpackPlugin([
{ from: './client/messyvendors', to: './../mess' }
]),
For vendors that are broken,
can’t make work with Webpack but I still need them
(during the transition)
Summary:
Summary:
• What is Webpack
• Basic setup
• Loaders are the bricks of Webpack
• Have nice tools in Dev environment
• Optimize in Prod environment
• Split your bundle as you need
• Tips and tricks
MADRID · NOV 27-28 · 2015
Thanks!
@nacmartin
nacho@limenius.com
http://limenius.com
1 von 90

Recomendados

Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C... von
Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...
Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...Ryan Weaver
13.7K views103 Folien
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and more von
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and moreSymfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and moreRyan Weaver
32.1K views114 Folien
Symfony: Your Next Microframework (SymfonyCon 2015) von
Symfony: Your Next Microframework (SymfonyCon 2015)Symfony: Your Next Microframework (SymfonyCon 2015)
Symfony: Your Next Microframework (SymfonyCon 2015)Ryan Weaver
7.6K views90 Folien
Symfony tips and tricks von
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricksJavier Eguiluz
54.3K views197 Folien
The Coolest Symfony Components you’ve never heard of - DrupalCon 2017 von
The Coolest Symfony Components you’ve never heard of - DrupalCon 2017The Coolest Symfony Components you’ve never heard of - DrupalCon 2017
The Coolest Symfony Components you’ve never heard of - DrupalCon 2017Ryan Weaver
1.4K views85 Folien
Introducing Assetic: Asset Management for PHP 5.3 von
Introducing Assetic: Asset Management for PHP 5.3Introducing Assetic: Asset Management for PHP 5.3
Introducing Assetic: Asset Management for PHP 5.3Kris Wallsmith
33.7K views110 Folien

Más contenido relacionado

Was ist angesagt?

Symfony & Javascript. Combining the best of two worlds von
Symfony & Javascript. Combining the best of two worldsSymfony & Javascript. Combining the best of two worlds
Symfony & Javascript. Combining the best of two worldsIgnacio Martín
19.3K views136 Folien
Laravel 101 von
Laravel 101Laravel 101
Laravel 101Commit University
3.5K views77 Folien
Guard Authentication: Powerful, Beautiful Security von
Guard Authentication: Powerful, Beautiful SecurityGuard Authentication: Powerful, Beautiful Security
Guard Authentication: Powerful, Beautiful SecurityRyan Weaver
9.4K views102 Folien
New Symfony Tips & Tricks (SymfonyCon Paris 2015) von
New Symfony Tips & Tricks (SymfonyCon Paris 2015)New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)Javier Eguiluz
23.6K views204 Folien
[Bristol WordPress] Supercharging WordPress Development von
[Bristol WordPress] Supercharging WordPress Development[Bristol WordPress] Supercharging WordPress Development
[Bristol WordPress] Supercharging WordPress DevelopmentAdam Tomat
889 views68 Folien
Laravel 8 export data as excel file with example von
Laravel 8 export data as excel file with exampleLaravel 8 export data as excel file with example
Laravel 8 export data as excel file with exampleKaty Slemon
193 views42 Folien

Was ist angesagt?(20)

Symfony & Javascript. Combining the best of two worlds von Ignacio Martín
Symfony & Javascript. Combining the best of two worldsSymfony & Javascript. Combining the best of two worlds
Symfony & Javascript. Combining the best of two worlds
Ignacio Martín19.3K views
Guard Authentication: Powerful, Beautiful Security von Ryan Weaver
Guard Authentication: Powerful, Beautiful SecurityGuard Authentication: Powerful, Beautiful Security
Guard Authentication: Powerful, Beautiful Security
Ryan Weaver9.4K views
New Symfony Tips & Tricks (SymfonyCon Paris 2015) von Javier Eguiluz
New Symfony Tips & Tricks (SymfonyCon Paris 2015)New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
Javier Eguiluz23.6K views
[Bristol WordPress] Supercharging WordPress Development von Adam Tomat
[Bristol WordPress] Supercharging WordPress Development[Bristol WordPress] Supercharging WordPress Development
[Bristol WordPress] Supercharging WordPress Development
Adam Tomat889 views
Laravel 8 export data as excel file with example von Katy Slemon
Laravel 8 export data as excel file with exampleLaravel 8 export data as excel file with example
Laravel 8 export data as excel file with example
Katy Slemon193 views
Building Single Page Application (SPA) with Symfony2 and AngularJS von Antonio Peric-Mazar
Building Single Page Application (SPA) with Symfony2 and AngularJSBuilding Single Page Application (SPA) with Symfony2 and AngularJS
Building Single Page Application (SPA) with Symfony2 and AngularJS
Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The Word... von Caldera Labs
Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The Word...Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The Word...
Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The Word...
Caldera Labs6.6K views
Refresh Austin - Intro to Dexy von ananelson
Refresh Austin - Intro to DexyRefresh Austin - Intro to Dexy
Refresh Austin - Intro to Dexy
ananelson668 views
How Kris Writes Symfony Apps von Kris Wallsmith
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
Kris Wallsmith13.5K views
Introduction to AngularJS For WordPress Developers von Caldera Labs
Introduction to AngularJS For WordPress DevelopersIntroduction to AngularJS For WordPress Developers
Introduction to AngularJS For WordPress Developers
Caldera Labs5.6K views
Bootstrat REST APIs with Laravel 5 von Elena Kolevska
Bootstrat REST APIs with Laravel 5Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5
Elena Kolevska3.9K views
HTML5 JavaScript APIs von Remy Sharp
HTML5 JavaScript APIsHTML5 JavaScript APIs
HTML5 JavaScript APIs
Remy Sharp54.8K views
Caldera Learn - LoopConf WP API + Angular FTW Workshop von CalderaLearn
Caldera Learn - LoopConf WP API + Angular FTW WorkshopCaldera Learn - LoopConf WP API + Angular FTW Workshop
Caldera Learn - LoopConf WP API + Angular FTW Workshop
CalderaLearn12.4K views
Creating the interfaces of the future with the APIs of today von gerbille
Creating the interfaces of the future with the APIs of todayCreating the interfaces of the future with the APIs of today
Creating the interfaces of the future with the APIs of today
gerbille8K views
OSCON Google App Engine Codelab - July 2010 von ikailan
OSCON Google App Engine Codelab - July 2010OSCON Google App Engine Codelab - July 2010
OSCON Google App Engine Codelab - July 2010
ikailan7K views
The road to Ember.js 2.0 von Codemotion
The road to Ember.js 2.0The road to Ember.js 2.0
The road to Ember.js 2.0
Codemotion838 views

Destacado

Migrating to Symfony 3.0 von
Migrating to Symfony 3.0Migrating to Symfony 3.0
Migrating to Symfony 3.0nicolas.grekas
10.5K views68 Folien
High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014 von
High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014
High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014Matthias Noback
12.4K views142 Folien
Integrating React.js Into a PHP Application von
Integrating React.js Into a PHP ApplicationIntegrating React.js Into a PHP Application
Integrating React.js Into a PHP ApplicationAndrew Rota
108.6K views78 Folien
Introduction to Redux von
Introduction to ReduxIntroduction to Redux
Introduction to ReduxIgnacio Martín
7.4K views57 Folien
Get Soaked - An In Depth Look At PHP Streams von
Get Soaked - An In Depth Look At PHP StreamsGet Soaked - An In Depth Look At PHP Streams
Get Soaked - An In Depth Look At PHP StreamsDavey Shafik
12.1K views43 Folien
Diving deep into twig von
Diving deep into twigDiving deep into twig
Diving deep into twigMatthias Noback
7K views66 Folien

Destacado(20)

Migrating to Symfony 3.0 von nicolas.grekas
Migrating to Symfony 3.0Migrating to Symfony 3.0
Migrating to Symfony 3.0
nicolas.grekas10.5K views
High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014 von Matthias Noback
High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014
High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014
Matthias Noback12.4K views
Integrating React.js Into a PHP Application von Andrew Rota
Integrating React.js Into a PHP ApplicationIntegrating React.js Into a PHP Application
Integrating React.js Into a PHP Application
Andrew Rota108.6K views
Get Soaked - An In Depth Look At PHP Streams von Davey Shafik
Get Soaked - An In Depth Look At PHP StreamsGet Soaked - An In Depth Look At PHP Streams
Get Soaked - An In Depth Look At PHP Streams
Davey Shafik12.1K views
Automation using-phing von Rajat Pandit
Automation using-phingAutomation using-phing
Automation using-phing
Rajat Pandit4.1K views
Electrify your code with PHP Generators von Mark Baker
Electrify your code with PHP GeneratorsElectrify your code with PHP Generators
Electrify your code with PHP Generators
Mark Baker3.7K views
The quest for global design principles (SymfonyLive Berlin 2015) von Matthias Noback
The quest for global design principles (SymfonyLive Berlin 2015)The quest for global design principles (SymfonyLive Berlin 2015)
The quest for global design principles (SymfonyLive Berlin 2015)
Matthias Noback3.8K views
WordCamp Cantabria - Código mantenible con WordPress von Asier Marqués
WordCamp Cantabria  - Código mantenible con WordPressWordCamp Cantabria  - Código mantenible con WordPress
WordCamp Cantabria - Código mantenible con WordPress
Asier Marqués1.4K views
Top tips my_sql_performance von afup Paris
Top tips my_sql_performanceTop tips my_sql_performance
Top tips my_sql_performance
afup Paris3.1K views
Why elasticsearch rocks! von tlrx
Why elasticsearch rocks!Why elasticsearch rocks!
Why elasticsearch rocks!
tlrx4.4K views
Understanding Craftsmanship SwanseaCon2015 von Marcello Duarte
Understanding Craftsmanship SwanseaCon2015Understanding Craftsmanship SwanseaCon2015
Understanding Craftsmanship SwanseaCon2015
Marcello Duarte2.4K views
Si le tdd est mort alors pratiquons une autopsie mix-it 2015 von Bruno Boucard
Si le tdd est mort alors pratiquons une autopsie mix-it 2015Si le tdd est mort alors pratiquons une autopsie mix-it 2015
Si le tdd est mort alors pratiquons une autopsie mix-it 2015
Bruno Boucard3.8K views
Writing infinite scalability web applications with PHP and PostgreSQL von Gabriele Bartolini
Writing infinite scalability web applications with PHP and PostgreSQLWriting infinite scalability web applications with PHP and PostgreSQL
Writing infinite scalability web applications with PHP and PostgreSQL
Gabriele Bartolini23.3K views

Similar a Keeping the frontend under control with Symfony and Webpack

Building Universal Web Apps with React ForwardJS 2017 von
Building Universal Web Apps with React ForwardJS 2017Building Universal Web Apps with React ForwardJS 2017
Building Universal Web Apps with React ForwardJS 2017Elyse Kolker Gordon
945 views123 Folien
Django + Vue, JavaScript de 3ª generación para modernizar Django von
Django + Vue, JavaScript de 3ª generación para modernizar DjangoDjango + Vue, JavaScript de 3ª generación para modernizar Django
Django + Vue, JavaScript de 3ª generación para modernizar DjangoJavier Abadía
4.6K views40 Folien
Vue routing tutorial getting started with vue router von
Vue routing tutorial getting started with vue routerVue routing tutorial getting started with vue router
Vue routing tutorial getting started with vue routerKaty Slemon
42 views35 Folien
Building and deploying React applications von
Building and deploying React applicationsBuilding and deploying React applications
Building and deploying React applicationsAstrails
2.1K views67 Folien
Death of a Themer von
Death of a ThemerDeath of a Themer
Death of a ThemerJames Panton
4.3K views90 Folien
The Future of CSS with Web components von
The Future of CSS with Web componentsThe Future of CSS with Web components
The Future of CSS with Web componentsdevObjective
310 views45 Folien

Similar a Keeping the frontend under control with Symfony and Webpack(20)

Building Universal Web Apps with React ForwardJS 2017 von Elyse Kolker Gordon
Building Universal Web Apps with React ForwardJS 2017Building Universal Web Apps with React ForwardJS 2017
Building Universal Web Apps with React ForwardJS 2017
Django + Vue, JavaScript de 3ª generación para modernizar Django von Javier Abadía
Django + Vue, JavaScript de 3ª generación para modernizar DjangoDjango + Vue, JavaScript de 3ª generación para modernizar Django
Django + Vue, JavaScript de 3ª generación para modernizar Django
Javier Abadía4.6K views
Vue routing tutorial getting started with vue router von Katy Slemon
Vue routing tutorial getting started with vue routerVue routing tutorial getting started with vue router
Vue routing tutorial getting started with vue router
Katy Slemon42 views
Building and deploying React applications von Astrails
Building and deploying React applicationsBuilding and deploying React applications
Building and deploying React applications
Astrails2.1K views
The Future of CSS with Web components von devObjective
The Future of CSS with Web componentsThe Future of CSS with Web components
The Future of CSS with Web components
devObjective310 views
Front End Development for Back End Java Developers - Jfokus 2020 von Matt Raible
Front End Development for Back End Java Developers - Jfokus 2020Front End Development for Back End Java Developers - Jfokus 2020
Front End Development for Back End Java Developers - Jfokus 2020
Matt Raible181 views
20130528 solution linux_frousseau_nopain_webdev von Frank Rousseau
20130528 solution linux_frousseau_nopain_webdev20130528 solution linux_frousseau_nopain_webdev
20130528 solution linux_frousseau_nopain_webdev
Frank Rousseau1.3K views
How Bitbucket Pipelines Loads Connect UI Assets Super-fast von Atlassian
How Bitbucket Pipelines Loads Connect UI Assets Super-fastHow Bitbucket Pipelines Loads Connect UI Assets Super-fast
How Bitbucket Pipelines Loads Connect UI Assets Super-fast
Atlassian10.6K views
Single Page JavaScript WebApps... A Gradle Story von Kon Soulianidis
Single Page JavaScript WebApps... A Gradle StorySingle Page JavaScript WebApps... A Gradle Story
Single Page JavaScript WebApps... A Gradle Story
Kon Soulianidis4K views
A Gentle Introduction to Angular Schematics - Angular SF 2019 von Matt Raible
A Gentle Introduction to Angular Schematics - Angular SF 2019A Gentle Introduction to Angular Schematics - Angular SF 2019
A Gentle Introduction to Angular Schematics - Angular SF 2019
Matt Raible458 views
How to Webpack your Django! von David Gibbons
How to Webpack your Django!How to Webpack your Django!
How to Webpack your Django!
David Gibbons2.9K views
RESS – Responsive Webdesign and Server Side Components von Sven Wolfermann
RESS – Responsive Webdesign and Server Side ComponentsRESS – Responsive Webdesign and Server Side Components
RESS – Responsive Webdesign and Server Side Components
Sven Wolfermann6.1K views
Build Your Own CMS with Apache Sling von Bob Paulin
Build Your Own CMS with Apache SlingBuild Your Own CMS with Apache Sling
Build Your Own CMS with Apache Sling
Bob Paulin15.8K views

Más de Ignacio Martín

Elixir/OTP for PHP developers von
Elixir/OTP for PHP developersElixir/OTP for PHP developers
Elixir/OTP for PHP developersIgnacio Martín
986 views74 Folien
Introduction to React Native Workshop von
Introduction to React Native WorkshopIntroduction to React Native Workshop
Introduction to React Native WorkshopIgnacio Martín
1.5K views126 Folien
Server side rendering with React and Symfony von
Server side rendering with React and SymfonyServer side rendering with React and Symfony
Server side rendering with React and SymfonyIgnacio Martín
2.8K views76 Folien
Symfony 4 Workshop - Limenius von
Symfony 4 Workshop - LimeniusSymfony 4 Workshop - Limenius
Symfony 4 Workshop - LimeniusIgnacio Martín
914 views88 Folien
Server Side Rendering of JavaScript in PHP von
Server Side Rendering of JavaScript in PHPServer Side Rendering of JavaScript in PHP
Server Side Rendering of JavaScript in PHPIgnacio Martín
2.1K views125 Folien
Extending Redux in the Server Side von
Extending Redux in the Server SideExtending Redux in the Server Side
Extending Redux in the Server SideIgnacio Martín
1.2K views87 Folien

Más de Ignacio Martín(15)

Introduction to React Native Workshop von Ignacio Martín
Introduction to React Native WorkshopIntroduction to React Native Workshop
Introduction to React Native Workshop
Ignacio Martín1.5K views
Server side rendering with React and Symfony von Ignacio Martín
Server side rendering with React and SymfonyServer side rendering with React and Symfony
Server side rendering with React and Symfony
Ignacio Martín2.8K views
Server Side Rendering of JavaScript in PHP von Ignacio Martín
Server Side Rendering of JavaScript in PHPServer Side Rendering of JavaScript in PHP
Server Side Rendering of JavaScript in PHP
Ignacio Martín2.1K views
Extending Redux in the Server Side von Ignacio Martín
Extending Redux in the Server SideExtending Redux in the Server Side
Extending Redux in the Server Side
Ignacio Martín1.2K views
React Native Workshop - React Alicante von Ignacio Martín
React Native Workshop - React AlicanteReact Native Workshop - React Alicante
React Native Workshop - React Alicante
Ignacio Martín1.7K views
Asegurando APIs en Symfony con JWT von Ignacio Martín
Asegurando APIs en Symfony con JWTAsegurando APIs en Symfony con JWT
Asegurando APIs en Symfony con JWT
Ignacio Martín4.3K views
Redux saga: managing your side effects. Also: generators in es6 von Ignacio Martín
Redux saga: managing your side effects. Also: generators in es6Redux saga: managing your side effects. Also: generators in es6
Redux saga: managing your side effects. Also: generators in es6
Ignacio Martín6.3K views
Integrating React.js with PHP projects von Ignacio Martín
Integrating React.js with PHP projectsIntegrating React.js with PHP projects
Integrating React.js with PHP projects
Ignacio Martín7K views
Integrando React.js en aplicaciones Symfony (deSymfony 2016) von Ignacio Martín
Integrando React.js en aplicaciones Symfony (deSymfony 2016)Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Ignacio Martín5.1K views
Adding Realtime to your Projects von Ignacio Martín
Adding Realtime to your ProjectsAdding Realtime to your Projects
Adding Realtime to your Projects
Ignacio Martín1.5K views

Último

AI Powered event-driven translation bot von
AI Powered event-driven translation botAI Powered event-driven translation bot
AI Powered event-driven translation botJimmy Dahlqvist
16 views31 Folien
We see everywhere that many people are talking about technology.docx von
We see everywhere that many people are talking about technology.docxWe see everywhere that many people are talking about technology.docx
We see everywhere that many people are talking about technology.docxssuserc5935b
6 views2 Folien
DU Series - Day 4.pptx von
DU Series - Day 4.pptxDU Series - Day 4.pptx
DU Series - Day 4.pptxUiPathCommunity
100 views28 Folien
Opportunities for Youth in IG - Alena Muravska RIPE NCC.pdf von
Opportunities for Youth in IG - Alena Muravska RIPE NCC.pdfOpportunities for Youth in IG - Alena Muravska RIPE NCC.pdf
Opportunities for Youth in IG - Alena Muravska RIPE NCC.pdfRIPE NCC
9 views12 Folien
zotabet.pdf von
zotabet.pdfzotabet.pdf
zotabet.pdfzotabetcasino
6 views1 Folie
PORTFOLIO 1 (Bret Michael Pepito).pdf von
PORTFOLIO 1 (Bret Michael Pepito).pdfPORTFOLIO 1 (Bret Michael Pepito).pdf
PORTFOLIO 1 (Bret Michael Pepito).pdfbrejess0410
7 views6 Folien

Último(20)

AI Powered event-driven translation bot von Jimmy Dahlqvist
AI Powered event-driven translation botAI Powered event-driven translation bot
AI Powered event-driven translation bot
Jimmy Dahlqvist16 views
We see everywhere that many people are talking about technology.docx von ssuserc5935b
We see everywhere that many people are talking about technology.docxWe see everywhere that many people are talking about technology.docx
We see everywhere that many people are talking about technology.docx
ssuserc5935b6 views
Opportunities for Youth in IG - Alena Muravska RIPE NCC.pdf von RIPE NCC
Opportunities for Youth in IG - Alena Muravska RIPE NCC.pdfOpportunities for Youth in IG - Alena Muravska RIPE NCC.pdf
Opportunities for Youth in IG - Alena Muravska RIPE NCC.pdf
RIPE NCC9 views
PORTFOLIO 1 (Bret Michael Pepito).pdf von brejess0410
PORTFOLIO 1 (Bret Michael Pepito).pdfPORTFOLIO 1 (Bret Michael Pepito).pdf
PORTFOLIO 1 (Bret Michael Pepito).pdf
brejess04107 views
IGF UA - Dialog with I_ organisations - Alena Muavska RIPE NCC.pdf von RIPE NCC
IGF UA - Dialog with I_ organisations - Alena Muavska RIPE NCC.pdfIGF UA - Dialog with I_ organisations - Alena Muavska RIPE NCC.pdf
IGF UA - Dialog with I_ organisations - Alena Muavska RIPE NCC.pdf
RIPE NCC15 views
UiPath Document Understanding_Day 3.pptx von UiPathCommunity
UiPath Document Understanding_Day 3.pptxUiPath Document Understanding_Day 3.pptx
UiPath Document Understanding_Day 3.pptx
UiPathCommunity101 views
Serverless cloud architecture patterns von Jimmy Dahlqvist
Serverless cloud architecture patternsServerless cloud architecture patterns
Serverless cloud architecture patterns
Jimmy Dahlqvist17 views
google forms survey (1).pptx von MollyBrown86
google forms survey (1).pptxgoogle forms survey (1).pptx
google forms survey (1).pptx
MollyBrown8614 views
IETF 118: Starlink Protocol Performance von APNIC
IETF 118: Starlink Protocol PerformanceIETF 118: Starlink Protocol Performance
IETF 118: Starlink Protocol Performance
APNIC186 views
Building trust in our information ecosystem: who do we trust in an emergency von Tina Purnat
Building trust in our information ecosystem: who do we trust in an emergencyBuilding trust in our information ecosystem: who do we trust in an emergency
Building trust in our information ecosystem: who do we trust in an emergency
Tina Purnat92 views
𝐒𝐨𝐥𝐚𝐫𝐖𝐢𝐧𝐝𝐬 𝐂𝐚𝐬𝐞 𝐒𝐭𝐮𝐝𝐲 von Infosec train
𝐒𝐨𝐥𝐚𝐫𝐖𝐢𝐧𝐝𝐬 𝐂𝐚𝐬𝐞 𝐒𝐭𝐮𝐝𝐲𝐒𝐨𝐥𝐚𝐫𝐖𝐢𝐧𝐝𝐬 𝐂𝐚𝐬𝐞 𝐒𝐭𝐮𝐝𝐲
𝐒𝐨𝐥𝐚𝐫𝐖𝐢𝐧𝐝𝐬 𝐂𝐚𝐬𝐞 𝐒𝐭𝐮𝐝𝐲
Infosec train9 views

Keeping the frontend under control with Symfony and Webpack

  • 1. Keeping the frontend under control with Symfony and Webpack Nacho Martín @nacmartin nacho@limenius.com Munich Symfony Meetup October’16
  • 2. I write code at Limenius We build tailor made projects using mainly Symfony and React.js So we have been figuring out how to organize better the frontend Nacho Martín nacho@limenius.com @nacmartin
  • 3. Why do we need this?
  • 4. Assetic? No module loading No bundle orientation Not a standard solution for frontenders Other tools simply have more manpower behind Written before the Great Frontend Revolution
  • 5. Building the Pyramids: 130K man years
  • 6. Writing JavaScript: 10 man days JavaScript
  • 7. Making JavaScript great: NaN man years JavaScript
  • 11. Tendency Task runners Bundlers Task runners + understanding of require(ments)
  • 12. Tendency Task runners Bundlers Task runners + understanding of require(ments)
  • 13. Package management in JS Server Side (node.js) Bower Client side (browser) Used to be
  • 14. Package management in JS Server Side (node.js) Bower Client side (browser) Used to be Now Everywhere
  • 15. Module loaders Server Side (node.js) Client side (browser) Used to be
  • 16. Module loaders Server Side (node.js) Client side (browser) Used to be Now Everywhere &ES6 Style
  • 17. Summarizing Package manager Module loader Module bundler
  • 18. Setup
  • 22. NPM setup $ npm init $ cat package.json { "name": "webpacksf", "version": "1.0.0", "description": "Webpack & Symfony example", "main": "client/js/index.js", "directories": { "test": "client/js/tests" }, "scripts": { "test": "echo "Error: no test specified" && exit 1" }, "author": "Nacho Martín", "license": "MIT" }
  • 23. Install Webpack $ npm install -g webpack $ npm install --save-dev webpack Or, to install it globally
  • 24. First example var greeter = require('./greeter.js') greeter('Nacho'); client/js/index.js
  • 25. First example var greeter = require('./greeter.js') greeter('Nacho'); client/js/index.js var greeter = function(name) { console.log('Hi '+name+'!'); } module.exports = greeter; client/js/greeter.js
  • 26. First example var greeter = require('./greeter.js') greeter('Nacho'); client/js/index.js var greeter = function(name) { console.log('Hi '+name+'!'); } module.exports = greeter; client/js/greeter.js
  • 27. Webpack without configuration $ webpack client/js/index.js web/assets/build/hello.js Hash: 4f4f05e78036f9dc67f3 Version: webpack 1.13.2 Time: 100ms Asset Size Chunks Chunk Names hi.js 1.59 kB 0 [emitted] main [0] ./client/js/index.js 57 bytes {0} [built] [1] ./client/js/greeter.js 66 bytes {0} [built]
  • 28. Webpack without configuration <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>{% block title %}Webpack & Symfony!{% endblock %}</title> {% block stylesheets %}{% endblock %} <link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}" /> </head> <body> {% block body %}{% endblock %} {% block javascripts %} <script src="{{ asset('assets/build/hello.js') }}"></script> {% endblock %} </body> </html> app/Resources/base.html.twig
  • 29. Webpack config module.exports = { entry: { hello: './client/js/index.js' }, output: { publicPath: '/assets/build/', path: './web/assets/build', filename: '[name].js' } }; webpack.config.js
  • 30. Webpack config module.exports = { entry: { hello: './client/js/index.js' }, output: { publicPath: '/assets/build/', path: './web/assets/build', filename: '[name].js' } }; webpack.config.js
  • 32. Now that we have modules, What about using modern JavaScript? (without caring about IE support)
  • 33. Now that we have modules, What about using modern JavaScript? (without caring about IE support)
  • 34. JavaScript ES2015 •Default Parameters •Template Literals •Arrow Functions •Promises •Block-Scoped Constructs Let and Const •Classes •Modules •…
  • 35. Why Babel matters import Greeter from './greeter.js'; let greeter = new Greeter('Hi'); greeter.greet('gentlemen'); class Greeter { constructor(salutation = 'Hello') { this.salutation = salutation; } greet(name = 'Nacho') { const greeting = `${this.salutation}, ${name}!`; console.log(greeting); } } export default Greeter; client/js/index.js client/js/greeter.js
  • 36. Install babel $ npm install --save-dev babel-core babel-loader babel-preset-es2015
  • 37. Install babel $ npm install --save-dev babel-core babel-loader babel-preset-es2015 module.exports = { entry: { hello: './client/js/index.js' }, output: { publicPath: '/assets/build/', path: './web/assets/build', filename: '[name].js' }, module: { loaders: [ { test: /.js$/, loader: 'babel-loader', exclude: /node_modules/ }, ] } }; webpack.config.js
  • 38. Install babel $ npm install --save-dev babel-core babel-loader babel-preset-es2015 module.exports = { entry: { hello: './client/js/index.js' }, output: { publicPath: '/assets/build/', path: './web/assets/build', filename: '[name].js' }, module: { loaders: [ { test: /.js$/, loader: 'babel-loader', exclude: /node_modules/ }, ] } }; webpack.config.js
  • 39. Install babel $ npm install --save-dev babel-core babel-loader babel-preset-es2015 module.exports = { entry: { hello: './client/js/index.js' }, output: { publicPath: '/assets/build/', path: './web/assets/build', filename: '[name].js' }, module: { loaders: [ { test: /.js$/, loader: 'babel-loader', exclude: /node_modules/ }, ] } }; webpack.config.js .babelrc { "presets": ["es2015"] }
  • 40. Install babel $ npm install --save-dev babel-core babel-loader babel-preset-es2015 module.exports = { entry: { hello: './client/js/index.js' }, output: { publicPath: '/assets/build/', path: './web/assets/build', filename: '[name].js' }, module: { loaders: [ { test: /.js$/, loader: 'babel-loader', exclude: /node_modules/ }, ] } }; webpack.config.js .babelrc { "presets": ["es2015"] }
  • 44. Loading styles: raw* loaders: [ //… { test: /.css$/i, loader: 'raw'}, ] exports.push([module.id, "body {n line-height: 1.5;n padding: 4em 1em;n}nnh2 {n margin-top: 1em;n padding-top: 1em;n}nnh1,nh2,nstrong {n color: #333;n}nna {n color: #e81c4f;n}nn", ""]); Embeds it into JavaScript, but… *(note: if you are reading the slides, don’t use this loader for css. Use css loader, that will be explained later)
  • 45. Chaining styles: style loaders: [ //… { test: /.css$/i, loader: ’style!raw'}, ]
  • 46. CSS loader header { background-image: url("../img/header.jpg"); } Problem
  • 47. CSS loader header { background-image: url("../img/header.jpg"); } url(image.png) => require("./image.png") url(~module/image.png) => require("module/image.png") We want Problem
  • 48. CSS loader header { background-image: url("../img/header.jpg"); } url(image.png) => require("./image.png") url(~module/image.png) => require("module/image.png") We want Problem loaders: [ //… { test: /.css$/i, loader: ’style!css'}, ] Solution
  • 49. File loaders { test: /.jpg$/, loader: 'file-loader' }, { test: /.png$/, loader: 'url-loader?limit=10000' }, Copies file as [hash].jpg, and returns the public url If file < 10Kb: embed it in data URL. If > 10Kb: use file-loader
  • 50. Using loaders When requiring a file In webpack.config.js, verbose { test: /.png$/, loader: "url-loader", query: { limit: "10000" } } require("url-loader?limit=10000!./file.png"); { test: /.png$/, loader: 'url-loader?limit=10000' }, In webpack.config.js, compact
  • 51. SASS { test: /.scss$/i, loader: 'style!css!sass'}, In webpack.config.js, compact $ npm install --save-dev sass-loader node-sass Also { test: /.scss$/i, loaders: [ 'style', 'css', 'sass' ] },
  • 52. Embedding CSS in JS is good in Single Page Apps What if I am not writing a Single Page App?
  • 53. ExtractTextPlugin var ExtractTextPlugin = require("extract-text-webpack-plugin"); const extractCSS = new ExtractTextPlugin('stylesheets/[name].css'); const config = { //… module: { loaders: [ { test: /.css$/i, loader: extractCSS.extract(['css'])}, //… ] }, plugins: [ extractCSS, //… ] }; {% block stylesheets %} <link href="{{asset('assets/build/stylesheets/hello.css')}}" rel="stylesheet"> {% endblock %} app/Resources/base.html.twig webpack.config.js
  • 54. ExtractTextPlugin var ExtractTextPlugin = require("extract-text-webpack-plugin"); const extractCSS = new ExtractTextPlugin('stylesheets/[name].css'); const config = { //… module: { loaders: [ { test: /.css$/i, loader: extractCSS.extract(['css'])}, //… ] }, plugins: [ extractCSS, //… ] }; {% block stylesheets %} <link href="{{asset('assets/build/stylesheets/hello.css')}}" rel="stylesheet"> {% endblock %} app/Resources/base.html.twig webpack.config.js { test: /.scss$/i, loader: extractCSS.extract(['css','sass'])}, Also
  • 56. Webpack-watch $ webpack --watch Simply watches for changes and recompiles the bundle
  • 57. Webpack-dev-server $ webpack-dev-server —inline http://localhost:8080/webpack-dev-server/ Starts a server. The browser opens a WebSocket connection with it and reloads automatically when something changes.
  • 58. Webpack-dev-server config Sf {% block javascripts %} <script src="{{ asset('assets/build/hello.js', 'webpack') }}"></script> {% endblock %} app/Resources/base.html.twig framework: assets: packages: webpack: base_urls: - "%assets_base_url%" app/config/config_dev.yml parameters: #… assets_base_url: 'http://localhost:8080' app/config/parameters.yml
  • 59. Webpack-dev-server config Sf {% block javascripts %} <script src="{{ asset('assets/build/hello.js', 'webpack') }}"></script> {% endblock %} app/Resources/base.html.twig framework: assets: packages: webpack: base_urls: - "%assets_base_url%" app/config/config_dev.yml framework: assets: packages: webpack: ~ app/config/config.yml parameters: #… assets_base_url: 'http://localhost:8080' app/config/parameters.yml
  • 60. Optional web-dev-server Kudos Ryan Weaver class AppKernel extends Kernel { public function registerContainerConfiguration(LoaderInterface $loader) { //… $loader->load(function($container) { if ($container->getParameter('use_webpack_dev_server')) { $container->loadFromExtension('framework', [ 'assets' => [ 'base_url' => 'http://localhost:8080' ] ]); } }); } }
  • 61. Hot module replacement output: { publicPath: 'http://localhost:8080/assets/build/', path: './web/assets/build', filename: '[name].js' }, Will try to replace the code without even page reload $ webpack-dev-server --hot --inline
  • 62. Hot module replacement output: { publicPath: 'http://localhost:8080/assets/build/', path: './web/assets/build', filename: '[name].js' }, Will try to replace the code without even page reload Needs full URL (so only in dev), or… $ webpack-dev-server --hot --inline
  • 63. Hot module replacement output: { publicPath: 'http://localhost:8080/assets/build/', path: './web/assets/build', filename: '[name].js' }, $ webpack-dev-server --hot --inline --output-public-path http://localhost:8080/assets/build/ Will try to replace the code without even page reload Needs full URL (so only in dev), or… $ webpack-dev-server --hot --inline
  • 64. SourceMaps const devBuild = process.env.NODE_ENV !== ‘production'; /… if (devBuild) { console.log('Webpack dev build'); config.devtool = 'eval-source-map'; } else {
  • 65. SourceMaps const devBuild = process.env.NODE_ENV !== ‘production'; /… if (devBuild) { console.log('Webpack dev build'); config.devtool = 'eval-source-map'; } else { eval source-map hidden-source-map inline-source-map eval-source-map cheap-source-map cheap-module-source-map Several options:
  • 66. Notifier $ npm install --save-dev webpack-notifier module.exports = { //… plugins: [ new WebpackNotifierPlugin(), ] }; webpack.config.js
  • 67. Notifier $ npm install --save-dev webpack-notifier module.exports = { //… plugins: [ new WebpackNotifierPlugin(), ] }; webpack.config.js
  • 68. Notifier $ npm install --save-dev webpack-notifier module.exports = { //… plugins: [ new WebpackNotifierPlugin(), ] }; webpack.config.js
  • 70. Optimization options var WebpackNotifierPlugin = require('webpack-notifier'); var webpack = require(‘webpack'); const devBuild = process.env.NODE_ENV !== 'production'; const config = { entry: { hello: './client/js/index.js' }, //… }; if (devBuild) { console.log('Webpack dev build'); config.devtool = 'eval-source-map'; } else { console.log('Webpack production build'); config.plugins.push( new webpack.optimize.DedupePlugin() ); config.plugins.push( new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) ); } module.exports = config;
  • 71. Optimization options var WebpackNotifierPlugin = require('webpack-notifier'); var webpack = require(‘webpack'); const devBuild = process.env.NODE_ENV !== 'production'; const config = { entry: { hello: './client/js/index.js' }, //… }; if (devBuild) { console.log('Webpack dev build'); config.devtool = 'eval-source-map'; } else { console.log('Webpack production build'); config.plugins.push( new webpack.optimize.DedupePlugin() ); config.plugins.push( new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) ); } module.exports = config; $ export NODE_ENV=production; webpack
  • 72. Optimization options var WebpackNotifierPlugin = require('webpack-notifier'); var webpack = require(‘webpack'); const devBuild = process.env.NODE_ENV !== 'production'; const config = { entry: { hello: './client/js/index.js' }, //… }; if (devBuild) { console.log('Webpack dev build'); config.devtool = 'eval-source-map'; } else { console.log('Webpack production build'); config.plugins.push( new webpack.optimize.DedupePlugin() ); config.plugins.push( new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) ); } module.exports = config; $ export NODE_ENV=production; webpack
  • 73. Bundle visualizer $ webpack --json > stats.json https://chrisbateman.github.io/webpack-visualizer/
  • 74. Bundle visualizer $ webpack --json > stats.json https://chrisbateman.github.io/webpack-visualizer/
  • 75. More than one bundle
  • 76. Separate entry points var config = { entry: { front: './assets/js/front.js', admin: './assets/js/admin.js', }, output: { publicPath: '/assets/build/', path: './web/assets/build/', filename: '[name].js' },
  • 77. Vendor bundles var config = { entry: { front: './assets/js/front.js', admin: './assets/js/admin.js', 'vendor-admin': [ 'lodash', 'moment', 'classnames', 'react', 'redux', ] }, plugins: [ extractCSS, new webpack.optimize.CommonsChunkPlugin({ name: 'vendor-admin', chunks: ['admin'], filename: 'vendor-admin.js', minChunks: Infinity }),
  • 78. Common Chunks var CommonsChunkPlugin = require(“webpack/lib/optimize/CommonsChunkPlugin”); module.exports = { entry: { page1: "./page1", page2: "./page2", }, output: { filename: "[name].chunk.js" }, plugins: [ new CommonsChunkPlugin("commons.chunk.js") ] } Produces page1.chunk.js, page2.chunk.js and commons.chunk.js
  • 79. On demand loading class Greeter { constructor(salutation = 'Hello') { this.salutation = salutation; } greet(name = 'Nacho', goodbye = true) { const greeting = `${this.salutation}, ${name}!`; console.log(greeting); if (goodbye) { require.ensure(['./goodbyer'], function(require) { var goodbyer = require('./goodbyer'); goodbyer(name); }); } } } export default Greeter; module.exports = function(name) { console.log('Goodbye '+name); } greeter.js goodbyer.js
  • 80. On demand loading class Greeter { constructor(salutation = 'Hello') { this.salutation = salutation; } greet(name = 'Nacho', goodbye = true) { const greeting = `${this.salutation}, ${name}!`; console.log(greeting); if (goodbye) { require.ensure(['./goodbyer'], function(require) { var goodbyer = require('./goodbyer'); goodbyer(name); }); } } } export default Greeter; module.exports = function(name) { console.log('Goodbye '+name); } greeter.js goodbyer.js
  • 81. Hashes output: { publicPath: '/assets/build/', path: './web/assets/build', filename: '[name].js', chunkFilename: "[id].[hash].bundle.js" },
  • 82. Chunks are very configurable https://webpack.github.io/docs/optimization.html
  • 84. Provide plugin plugins: [ new webpack.ProvidePlugin({ _: 'lodash', $: 'jquery', }), ] $("#item") _.find(users, { 'age': 1, 'active': true }); These just work without requiring them:
  • 85. Exposing jQuery { test: require.resolve('jquery'), loader: 'expose?$!expose?jQuery' }, Exposes $ and jQuery globally in the browser
  • 86. Dealing with a mess require('imports?define=>false&exports=>false!blueimp-file-upload/js/vendor/jquery.ui.widget.js'); require('imports?define=>false&exports=>false!blueimp-load-image/js/load-image-meta.js'); require('imports?define=>false&exports=>false!blueimp-file-upload/js/jquery.iframe-transport.js'); require('imports?define=>false&exports=>false!blueimp-file-upload/js/jquery.fileupload.js'); require('imports?define=>false&exports=>false!blueimp-file-upload/js/jquery.fileupload-process.js'); require('imports?define=>false&exports=>false!blueimp-file-upload/js/jquery.fileupload-image.js'); require('imports?define=>false&exports=>false!blueimp-file-upload/js/jquery.fileupload.js'); require('imports?define=>false&exports=>false!blueimp-file-upload/js/jquery.fileupload-validate.js'); require('imports?define=>false&exports=>false!blueimp-file-upload/js/jquery.fileupload-ui.js'); Broken packages that are famous have people that have figured out how to work with them
  • 87. CopyWebpackPlugin for messes new CopyWebpackPlugin([ { from: './client/messyvendors', to: './../mess' } ]), For vendors that are broken, can’t make work with Webpack but I still need them (during the transition)
  • 89. Summary: • What is Webpack • Basic setup • Loaders are the bricks of Webpack • Have nice tools in Dev environment • Optimize in Prod environment • Split your bundle as you need • Tips and tricks
  • 90. MADRID · NOV 27-28 · 2015 Thanks! @nacmartin nacho@limenius.com http://limenius.com