MODERN
WEB APPS

THE ESSENTIALS

WEB STACKS

L.A.M.P

M.E.A.N

TODAY'S WEB

G

E

R

M

A

N

W

E

B

LET'S START!

ASSEMBLE THE AVENGERS!

AND DO IT USING JAVASCRIPT

THE SERVER CAVE

AT FIRST, THERE WAS NOTHING.

> node

> var x = { a: 5 };
undefined

> console.log(x.a)
5
undefined

> x.b = function(){...}
[Function]

> x.c("Hello!")
TypeError: undefined is not a function
            

Created in 2009,
NodeJS was the first server side JavaScript engine that got widely adopted

Node is single threaded


var greets = ['World!', 'Kitty', ', Its Me'];

greets.forEach(function hello(greet) {
    console.log('Hello' + greet);
});
> node hello.js

Hello World!
Hello Kitty
Hello, It's Me

var fs = require('fs');

fs.readFile('myFile.txt', function(err, result){
    console.log(result);
});
> node fileReader.js

Lorem Ipsum Dolor
Sit Amet Hodor.
Hodor hodor hodor,
hodor.

fileReader.js

hello.js

JAVASCRIPT + I/O

It works, but not very scalable....

ONE FILE

VALLEY OF SHARING

var hulkStatus = require('./hulk.js');
var ironMan = require('./ironMan.js');

console.log('Hulk is ' + hulkStatus);
console.log(ironMan.name() + ' is Iron Man');
> node avengers.js

Hulk is MAD
Tony Stark is Iron Man
var radiation = 10000;
module.exports = 'MAD'
function getName() {
    return 'Tony Stark';
}
exports.name = getName;
exports.age = 48;

hulk.js

ironMan.js

avengers.js

Thinking in Modules

What is require?

Module Systems

CommonJS

  • Synchronous, on demand
  • Used by Node environment
  • Only one, mutable export
var moduleA = require('./fileA.js');
console.log(moduleA.name());

if(moduleA.size) {
    var moduleB = require('./fileB.js');
    console.log(moduleB);
}
exports.name = function(){...}
exports.size = 10;
var moduleC = require('./fileC.js')
module.exports = ...;

main.js

fileA.js

fileB.js

Module Systems

AMD

  • Asynchronous
  • Used mostly on web environment
  • Mutable objects
require(['./moduleA'], function(moduleA){
  console.log(moduleA.name());
  if(moduleA.size){
    require(['./moduleB'], function(moduleB){
      console.log(moduleB);
    })
  }
})
define('moduleA', [], function(){
    return {...}
});
define('moduleB', ['moduleC'], function(){
    return ...
});

main.js

fileA.js

fileB.js

Much better!

But still a lot of hard work....

Do I have to write everything myself?

VILLAGE OF COLLABORATION

In the JS community, developers

publish code as packages for other developers to use

PACKAGE

MANAGER

NPM IS A NODE UTILITY

> node -v
v8.5.0
> npm -v
v5.3.0

ALREADY BUNDLED IN NODE

BIGGEST CODE REPO IN THE WORLD

JSON BASED CONFIGURATION

NPMJS IS
NPM'S REGISTRY

BUT NPM CAN INSTALL PACKAGES FROM ANY SOURCE

SOUNDS GREAT!

THE ONLY CATCH IS

THAT IF YOU WANT TO USE PACKAGES...

YOU HAVE TO BE A PACKAGE YOURSELF!

An NPM package is just a folder with js files and a package.json

WHAT IS A "PACKAGE"?

{
  "name": "my-package",
  "version": "1.0.0",
  "author": "Liad",
  "scripts": {},
  "main": "avengers.js",
  "dependencies": {},
  "devDependencies": {}
}

avengers.js

hulk.js

ironman.js

package.json

{ }

PACKAGE ANATOMY

PACKAGE.JSON

LOCATED IN THE ROOT,
A JSON CONFIG FILE THAT DESCRIBES THE PACKAGE PROPERTIES

{
  "name": "my-package",
  "version": 1.0.0,
  "description": "A Cool Package",
  "author": "Liad Yosef",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/liady/my_package.git"
  },
  "keywords": [],
  "main": "index.js"
}

MANDATORY FIELDS

NAME AND VERSION MUST BE FILLED

{
  "name": "my-package",
  "version": 1.0.0,
  "description": "A Cool Package",
  "author": "Liad Yosef",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/liady/my_package.git"
  },
  "keywords": [],
  "main": "index.js"
}

AUTO INIT

NPM INIT WILL AUTOMATICALLY GENERATE A CONFIG 

PACKAGE ANATOMY

will copy a package from NPM
to a folder called node_modules

$ npm install {package}

PACKAGE ANATOMY

will copy a package from NPM
to a folder called node_modules

node_modules
avengers.js
lodash
marvel
lodash
marvel
my-package
hulk.js
avengers.js
hulk.js
leftpad
.
.
.
$ npm install {package}

MY CODE

NPM

PACKAGE ANATOMY

NODE_MODULES

var moduleA = require('./moduleA');
var moduleB = require('./subFolder/moduleB');
var lodash = require('lodash');
var express = require('express');
var moduleA = require('./moduleA');
var moduleB = require('./subFolder/moduleB');
var lodash = require('node_modules/lodash');
var express = require('node_modules/express');

By default, Node automatically resolves package names to the node_modules folder

main.js

PACKAGE ANATOMY

A package can define other packages as dependencies, to be installed with it

node_modules
lodash
marvel
marvel
leftpad
my-package
express
galaxy
leftpad
express
galaxy

PACKAGE ANATOMY

This can get out of hand real quick.
Run npm ls to see which package brings
which dependencies

PACKAGE ANATOMY

Will save the package as a dependency

Will save the package as a development dependency - it's required for build or tests, but not for our runtime.

$ npm install {package} --save
$ npm install {package} --save-dev
{
  "name": "avengers",
  "version": 1.0.0,
  "description": "Better than DC",
  "author": "Liad Yosef",
  "license": "MIT",
  "dependencies": {
    "lodash": "^3.2.1",
    "marvel": "^4.4.1"
  },
  "devDependencies": {
    "webpack": "3.2.4"
  }
}
{
  "name": "avengers",
  "version": 1.0.0,
  "description": "Better than DC",
  "author": "Liad Yosef",
  "license": "MIT"
}
{
  "name": "avengers",
  "version": 1.0.0,
  "description": "Better than DC",
  "author": "Liad Yosef",
  "license": "MIT",
  "dependencies": {
    "lodash": "^3.2.1",
    "marvel": "^4.4.1"
  }
}
$ npm install

Installs the packages from package.json

SEMVER

SEMANTIC VERSIONING

SEMANTIC VERSIONING

1

3

1

.

.

1

3

1

Major

breaking changes

Minor

new features, not breaking

Patch

bugfixes, not breaking

v

SEMANTIC VERSIONING

Start with 1.0.0

Fixed a bug? 1.0.1

Added a feature? 1.1.0

Backwards incompatible? 2.0.0

SEMANTIC VERSIONING

3.9.x

Update only with bug fixes

3.x.x

Update with bug fixes, API additions, any non-breaking changes

"lodash": "~3.9.0"
"lodash": "^3.9.0"

Tilde ~

Caret ^

VERSION PROBLEMS

Getting the correct version that doesn't break the code can be... problematic.

NPM maintaines a packge-lock.json file, that holds the EXACT versions of the installed packages

VERSION RESOLUTION

{
  "name": "avengers",
  "version": "1.0.0",
  "lockfileVersion": 1,
  "requires": true,
  "dependencies": {
    "ansi-regex": {
      "version": "2.1.1",
      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
    },
    "ansi-styles": {
      "version": "2.2.1",
      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
      "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
    },
    "asn1": {
      "version": "0.2.3",
      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
      "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y="
    },
    "assert-plus": {
      "version": "0.2.0",
      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz",
      "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ="
    },
    "async": {
      "version": "2.6.0",
      "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz",
      "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==",
      "requires": {
        "lodash": "4.17.4"
      }
    },

An NPM package can be

WHAT'S IN A JSON

A library

- or -

An app

The package.json serves them both

{
  "name": "chalk",
  "version": "2.3.0",
  "description": "Terminal string styling done right",
  "main": "./index.js",
  "scripts": {
    "build": "node ./build.js",
    "test": "mocha *.test.js"
  },
  "dependencies": {
    "lodash": "^4.17.4",
    "console": "^1.1.0"
  },
  "devDependencies": {
    "mocha": "^3.1.10"
  },
  "keywords": [ "Chalk", "Log", "Cool" ],
  "author": "sindresorhus",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/chalk/chalk/issues"
  },
  "homepage": "https://github.com/chalk/chalk#readme"
}

NPM IS ALSO A TASK RUNNER

$ npm run {scriptName}

WHAT'S IN A JSON

{
  "name": "chalk",
  "version": "2.3.0",
  "description": "Terminal string styling done right",
  "main": "./index.js",
  "scripts": {
    "build": "node ./build.js",
    "test": "mocha *.test.js"
  },
  "dependencies": {
    "lodash": "^4.17.4",
    "console": "^1.1.0"
  },
  "devDependencies": {
    "mocha": "^3.1.10"
  },
  "keywords": [ "Chalk", "Log", "Cool" ],
  "author": "sindresorhus",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/chalk/chalk/issues"
  },
  "homepage": "https://github.com/chalk/chalk#readme"
}

NPM IS ALSO A TASK RUNNER!

$ npm run {scriptName}

NPM SCRIPTS

FOR MORE INFO ON NPM VISIT THE DOCS:

THE 39 WIZARDS

TC39

ECMA TECHNICAL COMMITTEE 39

TC39

THE FORMAL COMMITTEE
THAT CREATES THE SPEC FOR JAVASCRIPT

TC39 STAGES

STAGE 0 - STRAWMAN

Free-form ideas, reviewed in TC39 meetings

STAGE 1 - PROPOSAL

Formally accepted proposal

STAGE 2 - DRAFT

Has description of syntax and semantics

STAGE 3 - CANDIDATE

Spec text complete, has at least 2 implementations

STAGE 0 - STRAWMAN

STAGE 1 - PROPOSAL

STAGE 2 - DRAFT

STAGE 3 - CANDIDATE

// auto-bind
::console.log = console.log.bind(console)
// partial application
const addOne = add(1, ?);
addOne(2); // 3
// decorators
@logged
class Element {...}
// private fields
class Element {
  #x = 5
  getNum() { return this.#x }
}

STAGE 4 - SPEC!

TC39 SPECS

A NEW SPEC IS RELEASED YEARLY,

WITH STAGE 4 APPROVED FEATURES

ES5

ES6

ES2015

ES2016

ES2017

ES2018

ES7

Object.create

JSON

Modules

Classes

Arrow Functions

Proxies

Generators

Exponent

Async / Await

Object.entries

?

ES8

ES6 MODULES

The best import and export since Pablo

ES6 MODULES

main.js

  • Static
  • Immutable references
  • No cyclic dependencies
  • Allows default and named imports
import { name, size } from './moduleA';
import moduleB from './moduleB';

console.log(name());
if(size){
    console.log(moduleB);
}
export const name = function(){}
export const size = ...
export default const x = 5;

fileA.js

fileB.js

ES6 MODULES

moduleB.js

import { name } from './moduleA';
function run() {
    console.log(name);
}
import moduleB from './moduleB';
export name = 'Deadpool';

moduleA.js

moduleB.js

var name = require('./moduleA').name;
function run() {
    console.log(name);
}
var moduleB = require('./moduleB');
exports.name = 'Deadpool';

moduleA.js

CommonJS

ES6

Module Systems

CommonJS

AMD

ES6

Synchronous

Cyclic deps.

Immutable

On demand

Reference based

Default/named

This is so cool!
Let's use ALL OF IT!

ES2015

STAGE-0

It takes time for EcmaScript features to be adopted by engines

Problem:

TOWER OF CONFUSION

NEW PROBLEM

Code written on one environment won't necessarily run on another!

SOLUTIONS

Decide on a common ground - which code can be published to NPM inside packages

Allow developers to use new ES features regardless of their target platform

BUT HOW???

STARTED IN 2014 AS A PET PROJECT,

BABEL WAS ORIGINALLY MADE TO CONVERT ES6 CODE TO ES5 CODE

SINCE THEN IT GOT 24,000 STARS ON GITHUB

 A TRUE JS REVOLUTION

No more writing code for the
lowest common denominator (i.e.     )

stage-0

es2015

jsx

modules

Write code however you want,
Compile it to run everywhere!

A MAGICAL TOOL
THAT CAN CONVERT CODE
TO ANOTHER CODE !

TRANSPILER

TAKES CODE IN ANY SOURCE LANGUAGE

AND CONVERTS IT TO A TARGET LANGUAGE

import { AVENGERS } from './Consts' ;

class Avenger {

  constructor (type, power) {
    this.type = type;
    this.power = power || 'Nothing';
    this.groups = {
      [AVENGERS]: true
    }
  }
  
  reportPower() {
    console.log(`I can do ${this.power} !`);
  }

}

const thor = new Avenger('God', 'A Thunder');
thor.reportPower();
var _Consts = require('./Consts');

var Avenger = function () {
  function Avenger(type, power) {
    var _groups;

    this.type = type;
    this.power = power || 'Nothing';
    this.groups = (_groups = {}, _groups[_Consts.AVENGERS] = true, _groups);
  }

  Avenger.prototype.reportPower = function reportPower() {
    console.log('I can do ' + this.power + ' !');
  };

  return Avenger;
}();

var thor = new Avenger('God', 'A Thunder');
thor.reportPower();
class Avenger {

  constructor (type, power) {
    this.type = type;
    this.power = power || 'Nothing';
    this.groups = {
      [AVENGERS]: true
    }
  }
  
  reportPower() {
    console.log(`I can do ${this.power} !`);
  }

}

const thor = new Avenger('God', 'A Thunder');
thor.reportPower();
var Avenger = function () {
  function Avenger(type, power) {
    var _groups;

    this.type = type;
    this.power = power || 'Nothing';
    this.groups = (_groups = {}, _groups[AVENGERS] = true, _groups);
  }

  Avenger.prototype.reportPower = function reportPower() {
    console.log('I can do ' + this.power + ' !');
  };

  return Avenger;
}();

var thor = new Avenger('God', 'A Thunder');
thor.reportPower();

Source

Target

class Avenger {

  constructor (type, power) {
    this.type = type;
    this.power = power || 'Nothing';
    this.groups = {
      [AVENGERS]: true
    }
  }
  
  reportPower() {
    console.log(`I can do ${this.power} !`);
  }

}

const thor = new Avenger('God', 'A Thunder');
thor.reportPower();
var Avenger = function () {
  function Avenger(type, power) {
    var _groups;

    this.type = type;
    this.power = power || 'Nothing';
    this.groups = (_groups = {}, _groups[AVENGERS] = true, _groups);
  }

  Avenger.prototype.reportPower = function reportPower() {
    console.log('I can do ' + this.power + ' !');
  };

  return Avenger;
}();

var thor = new Avenger('God', 'A Thunder');
thor.reportPower();

Source

Target

Abstract Syntax Tree

Transforms

var a = 3;
a + 5

Code

Abstract Syntax Tree

var bar = [1, 2, 3]
var foo = { bar: 1 }

Babel parses the code to an AST structure

const x = new Hero("Hulk", [1, 2, 3]);

The AST structure is traversable

Babel uses transforms
to manipulate the AST

Transforms are visitors that run on nodes, and can change the AST

[1, 2, 3]
utils.vector("1", "2", "3")

There are Babel plugins for every ES feature

es-arrow-functions

es-classes

es-shorthand-properties

es-spread

decorators

function-bind

react-jsx

react-display-name

async-generators

es-modules

For convenience, they are grouped into presets

...

...

...

...

...

ES2015

ES2017

REACT

STAGE-3

STAGE-0

USING

$ npm install --save-dev babel-cli
$ npm install --save-dev babel-preset-env ...
{
  "plugins": ["decorators"],
  "presets": [
    "react",
    "stage-0",
    // automatically select the plugins
    ["env", {
      "targets": {
        "browsers": ["last 2 versions", "safari >= 7"]
      }
    }]
  ]
}

1. Install Babel

  Install presets

2. Configure .babelrc

$ npm install --save-dev babel-plugin-decorators ...

  Install additional plugins

{
  "scripts": {
    "build": "babel src --out-dir dist"
  }
}

3. Add a script to package.json

$ npm run build

4. Run Babel!

Note that now with         

we have two code folders

source

dist

where our actual coding takes place

the code that can actually run

1. We can no longer code & refresh

2. We have to specify to npm where our ES5 code is

{
  "main": "dist/avengers.js"
}

PACKAGE IS READY!

JUNGLE OF THE WEBS

Then the evil web guys looked at all those Node wonders...

of

require

and

babel

and the

NPM

repository

amazing

...and they looked on their
crappy HTML full of <script>...

<html>
  <head>
    <script src="/editor/ed/src/common/ed._prototypeExtensions.js?version=2017-12-19T13_03_41"></script>
    <script src="/editor/ed/src/common/ed.common.js?version=2017-12-19T13_03_41"></script>
    <script src="/editor/ed/src/common/ed.convertUtils.js?version=2017-12-19T13_03_41"></script>
    <script src="/editor/ed/src/common/modules/ed.module.batchRequests.js?version=2017-12-19T13_03_41"></script>
    <script src="/editor/ed/src/common/modules/ed.module.events.js?version=2017-12-19T13_03_41"></script>
    <script src="/editor/ed/src/common/modules/ed.module.cache.js?version=2017-12-19T13_03_41"></script>
    <script src="/editor/ed/src/common/modules/ed.module.cookies.js?version=2017-12-19T13_03_41"></script>
    <script src="/editor/ed/src/common/modules/ed.module.lockables.js?version=2017-12-19T13_03_41"></script>
    <script src="/editor/ed/src/common/modules/ed.module.locks.js?version=2017-12-19T13_03_41"></script>
    <script src="/editor/ed/src/common/modules/ed.module.performance.js?version=2017-12-19T13_03_41"></script>
    <script src="/editor/ed/src/common/modules/ed.module.spotlight.js?version=2017-12-19T13_03_41"></script>
    <script src="/editor/ed/src/common/modules/ed.module.storage.js?version=2017-12-19T13_03_41"></script>
    <script src="/editor/ed/src/common/modules/ed.module.widgetEditor.js?version=2017-12-19T13_03_41"></script>
    <script src="/editor/ed/src/common/ed.dmAjaxUtils.js?version=2017-12-19T13_03_41"></script>
    <script src="/editor/ed/src/common/ed.dmx.js?version=2017-12-19T13_03_41"></script>
    <script src="/editor/ed/src/common/ed.editableUtils.js?version=2017-12-19T13_03_41" ></script>
    <script src="/editor/ed/src/common/ed.layoutNavigationUtils.js?version=2017-12-19T13_03_41"></script>
    <script src="/editor/ed/src/common/ed.measureUtils.js?version=2017-12-19T13_03_41"></script>
    <script src="/editor/ed/src/common/ed.stacktraceUtils.js?version=2017-12-19T13_03_41"></script>
    <script src="/editor/ed/src/common/ed.cssUtils.js?version=2017-12-19T13_03_41"></script>
    <script src="/editor/ed/src/common/ed.spinner.js?version=2017-12-19T13_03_41"></script>
    <script src="/editor/ed/src/common/ed.editorColors.js?version=2017-12-19T13_03_41"></script>
    <script src="/editor/ed/src/common/framework/ed.super.fw.js?version=2017-12-19T13_03_41"></script>

And said:

And the Node guys replied:

"Why can't we use all those good NPM stuff???"

"Are you sure? that will mean a loooot of network requests..."

And the Web guys cried because it was true.

IN ORDER TO SPLIT OUR CODE,

USE NPM MODULES,
AND RUN IN THE WEB...

WE HAVE TO BUNDLE THEM.

BUNDLE

STARTED IN 2012 AS A PET PROJECT,

WEBPACK WAS ORIGINALLY MADE TO BUNDLE FILES FOR USE ON THE WEB

SINCE THEN IT GOT 35,000 STARS ON GITHUB

TODAY, WEBPACK IS THE STANDARD IN WEB DEVELOPMENT FOR BUNDLING MODULES

import ironMan from './ironMan.js';
import hulk from './hulk.js';

console.log('Got Them');

avengers.js

HOW DOES IT WORK

var styles = require('./styles.scss');
if(Math.random()) {
    var _ = require('lodash');
    _.find('Tony Stark');
}

ironMan.js

HOW DOES IT WORK

import ironMan from './ironMan.js';
import hulk from './hulk.js';

console.log('Got Them');

avengers.js

var utils = require('./utils.js');
utils.smash();

hulk.js

.captain-america {
    display: none;
}

styles.scss

HOW DOES IT WORK

utils.js

var styles = require('./styles.scss');
if(Math.random()) {
    var _ = require('lodash');
    _.find('Tony Stark');
}

ironMan.js

var utils = require('./utils.js');
utils.smash();

hulk.js

import ironMan from './ironMan.js';
import hulk from './hulk.js';

console.log('Got Them');

avengers.js

exports.smash = function() {😤}
exports.sick = function() {🤢}
exports.angry = function() {😡}

lodash

.captain-america {
    display: none;
}

styles.scss

HOW DOES IT WORK

utils.js

var styles = require('./styles.scss');
if(Math.random()) {
    var _ = require('lodash');
    _.find('Tony Stark');
}

ironMan.js

var utils = require('./utils.js');
utils.smash();

hulk.js

import ironMan from './ironMan.js';
import hulk from './hulk.js';

console.log('Got Them');

avengers.js

exports.smash = function() {😤}
exports.sick = function() {🤢}
exports.angry = function() {😡}

lodash

.captain-america {
    display: none;
}

styles.scss

HOW DOES IT WORK

utils.js

var styles = require('./styles.scss');
if(Math.random()) {
    var _ = require('lodash');
    _.find('Tony Stark');
}

ironMan.js

var utils = require('./utils.js');
utils.smash();

hulk.js

import ironMan from './ironMan.js';
import hulk from './hulk.js';

console.log('Got Them');

avengers.js

exports.smash = function() {😤}
exports.sick = function() {🤢}
exports.angry = function() {😡}

lodash

bundle.js

.captain-america {
    display: none;
}

styles.scss

utils.js

var styles = require('./styles.scss');
if(Math.random()) {
    var _ = require('lodash');
    _.find('Tony Stark');
}

ironMan.js

var utils = require('./utils.js');
utils.smash();

hulk.js

import ironMan from './ironMan.js';
import hulk from './hulk.js';

console.log('Got Them');

avengers.js

exports.smash = function() {😤}
exports.sick = function() {🤢}
exports.angry = function() {😡}

lodash

/******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

var ironMan = __webpack_require__(1);
var hulk = __webpack_require__(2);

/***/ }),
/* 1 */
/***/ (function(module, exports) {

if(Math.random()) {
    var _ = {};
    _.find('Tony Stark');
}

/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {

var utils = __webpack_require__(3);
utils.smash();

/***/ }),
/* 3 */
/***/ (function(module, exports) {

exports.smash = function() {}
exports.sick = function() {}
exports.angry = function() {}

/***/ })
/******/ ]);

bundle.js

WEBPACK SERIALIZES THE DEPS

/******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

var ironMan = __webpack_require__(1);
var hulk = __webpack_require__(2);

/***/ }),
/* 1 */
/***/ (function(module, exports) {

if(Math.random()) {
    var _ = {};
    _.find('Tony Stark');
}

/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {

var utils = __webpack_require__(3);
utils.smash();

/***/ }),
/* 3 */
/***/ (function(module, exports) {

exports.smash = function() {}
exports.sick = function() {}
exports.angry = function() {}

/***/ })
/******/ ]);

bundle.js

<html>
  <head>
    <script src="public/bundle.js"></script>
    ...
    </head>
</html>

THE FILE CAN BE USED IN <SCRIPT>

index.html

BASIC CONFIGURATION

module.exports = {
  entry: './avengers.js',
  output: {
    path: 'dist',
    filename: 'bundle.js'
  }
};

webpack.config.js

THAT'S IT.

FILE TYPES

Webpack should handle all types of dependencies, not just JS

import Icon from './Icon.jsx';
...
.scene {
  background: url("./all.png");
}
styles = require('./styles.scss');
...
return (
  <Button>{styles.name}</Button>
)

index.js

Icon.jsx

styles.scss

all.png

CSS, SCSS, LESS

JSX, ELM, TS

ES6, ES7, ES?

PNG, SVG, TTF

FILE TYPES - LOADERS

Weback uses loaders to manipulate files
Files go through loaders to become valid js before getting into the bundle

.scss
.css
.js
.jsx
.js
.png
.js
.png
Icon.jsx
styles.scss
all.png
.js
.png
dist

FILE TYPES - LOADERS

babel-loader

converts es6 + jsx to js

sass-loader, style-loader

converts sass to css, embed as <style>

eslint-loader

run eslint on files

url-loader

move assets to dist

AND MANY MORE!

FILE TYPES - LOADERS

module.exports = {
  entry: './avengers.js',
  output: {
    path: 'public',
    filename: 'bundle.js'
  },
  module: {
    rules: [
      { test: /\.js$/, use: ['eslint-loader'] },
      { test: /\.jsx$/, use: ['babel-loader'] },
      { test: /\.png$/, use: [ 'url-loader'] },
      { test: /\.css$/, use: [ 'style-loader', 'css-loader'] }
    ]
  }
};

webpack.config.js

BUNDLE CONTROL

Weback will create a single bundle, unless told otherwise

Asset
Size
bundle.js
15.7MB

SPLIT POINTS

We can define split points in the code to split the bundle

// will be bundled in THIS CHUNK
import ironMan from './ironMan.js';

// will be bundled in THIS CHUNK
const spiderman = require('./spiderman.js');

// will create a NEW SPLIT POINT - new async chunk
const hulk = await import('./hulk.js');

SPLIT POINTS

We can define split points in the code to split the bundle

import { Apps } from 'consts';

export default function(appName) {
    switch(appName) {
    case Apps.DESIGN_EDITOR:
        return import('apps/designEditor?chunkName=designEditor');
    case Apps.CONTENT_EDITOR:
        return import('apps/contentEditor?chunkName=contentEditor');
    case Apps.MOBILE_CONTENT_EDITOR:
        return import('apps/mobileContentEditor?chunkName=mobileContentEditor');
    case Apps.SHELL:
        return import('apps/shell?chunkName=shell');
    default:
        return Promise.reject(`The app loader does not have a handler defined for app ${appName}`);
    }
}

CODE SPLITTING IN REAL FILE

Webpack also has a lot of plugins that run on the bundle itself

  • Minification

  • Coverage

  • Stats

  • Notifier

  • Clean Dist

  • And more...

module.exports = {
  entry: './avengers.js',
  output: {
    path: 'public',
    filename: 'bundle.js'
  },
  module: {
    rules: [
      { test: /\.js$/, use: ['eslint-loader'] },
      { test: /\.jsx$/, use: ['babel-loader'] },
      { test: /\.png$/, use: [ 'url-loader'] },
      { test: /\.css$/, use: [ 'style-loader', 'css-loader'] }
    ]
  },
  plugins: [new UglifyPlugin()],
  resolve: {
    alias: {
      "home": "./src/index.js"
    }
  }
};

webpack.config.js

$ npm install --save-dev webpack
$ npm install --save-dev babel-loader ...
{
  "scripts": {
    "build": "webpack",
    "watch": "webpack --watch"
  }
}

package.json

install

$ npm install --save-dev plugin-uglify ..

END OF THE ROAD?

USED JS IN THE SERVER

USED MODULES

USED NPM PACKAGES

USED MODERN SPEC

BUNDLED FOR WEB

WE ASSMEBLED THE AVENJS!!!

CLIENT

SERVER

WHY NOT BOTH??

RESOURCES

SEE YOU AT:

ADVANCED

ADVANCED

ADVANCED

QUESTIONS?

Modern Web Apps

By Liad Yosef

Modern Web Apps

A quick intro to modern web apps

  • 3,407