Making your scripts digestible for the browser
Maurizio Lupo (@sithmel)
Browser Javascript
Different scripts communicate using the
Browser Javascript
Javascript allows to
Browser Javascript
Browser Javascript
Polluting global scope
Bundle more modules together
Provide old browser compatibility
Bundling
First solution: wrap a group of scripts within a closure
(function () {
... script1 ...
... script2 ...
... script3 ...
...
}());
Bundling
AMD
define('react', function () {
...
return react;
});
define('jquery', function () {
...
return $;
});
define('myapp', ['jquery', 'react'], function ($, react) {
...
});
Can run natively in the browser (horribly slow)
To fix performance uses bundling step
Bundling
commonjs
--- react.js
...
module.exports = react;
--- file jquery.js
...
module.exports = jquery;
--- file app.js
var jquery = require('jquery');
var react = require('react');
...
Can't run natively in the browser
Rely on a bundling step
Cross compatible with node.js
Bundling
commonjs
var _get = require('lodash/get');
Please do not import a gorilla to get a banana
Bundling
ES2015 modules
--- react.js
...
export default react;
--- file jquery.js
...
export default jquery;
--- file app.js
import jquery from 'jquery';
import react from 'react';
...
Can't run natively in the browser
Rely on a bundling step
Can be transpiled to common.js
Its semantic allows "treeshaking" (with webpack 2 and rollup)
How it works: Browserify
A loader is inserted in each bundle
Every module is wrapped within a closure
If a module rely on any node.js feature, this is added: __dirname, process, buffer etc.
Compatible with commonjs: ES2015 must be converted by babel
How it works: Browserify
The module is happended to a object
The keys of the object are the name or path in the require
if you use the "fullPath" option the keys are absolute paths (very bad)
If you use the bundle-collapser module keys are converted in integers
How it works: Browserify
if you use the "debug" option, sourcemaps are appended to the file
How it works: Webpack 2
A loader is inserted in each bundle
Every module is wrapped within a closure
If a module rely on any node.js feature, this is added: __dirname, process, buffer etc.
Compatible with AMD, commonjs, ES2015
How it works: Webpack 2
Modules are happended to a object
The keys of the object is a number
Any non js require is treated accordingly to its loader
How it works: Webpack 2
Using ES2015 imports, unused modules are stripped (treeshaking)
But you have to configure babel to not transpile es2015 modules:
presets: [['es2015', {modules: false}], 'react']
How it works: Webpack 2
What about third party modules?
You have to add the module field to the package.json
{
"name": "my-package",
"version": "0.1.0",
"main": "dist/my-package.umd.js",
"module": "dist/my-package.es2015.js"
}
How it works: Rollup
No loader required
No closures, it renames conflicting variables
If a module rely on any node.js feature, this is added: __dirname, process, buffer etc. (work in progress)
A bit immature but:
Treeshaking is similar to webpack
It uses ES2015 natively and requires a plugin for commonjs
Babel
Transpilation
const a = 10;
To
From
var a = 10;
It only requires the appropriate preset
For ES use babel-preset-env
Babel
Polyfilling
It includes:
polyfills in the global namespace
Regenerator runtime: used in conjunction with transpiled generators
It throws an exception if imported more than once and it is massive!!!
Babel
Polyfilling
It is a plugin and includes:
polyfills in the private namespace (core-js)
Regenerator runtime: used in conjunction with transpiled generators
It doesn't include polyfills for existing built-ins: .includes .contains etc.
Babel
Polyfilling
It is a collection of polyfills, they can be cherry picked
for using generators
Babel
babel-preset-env has a plugin to automatically import individual corejs imports as required by target environment!
Always check what you are importing:
Be careful to import a module designed for the server:
Checklist