Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building Modern, Modular JavaScript Applications

Erik Grijzen
February 06, 2016

Building Modern, Modular JavaScript Applications

Presented @ Netcentric Summit 2016
Barceló convention center
Seville, Spain

More and more logic is moving from the server to the client side. The complexity of front-end applications is increasing rapidly and the end users are expecting highly interactive user interfaces.

Developing with a modular approach helps you to build more complex, scalable and robust applications. Existing modular solutions are very fragmented and are now being replaced by the new standards of ECMAScript 2015. We have new ways of writing our JavaScript code and this affects our current workflows.

Erik Grijzen

February 06, 2016
Tweet

More Decks by Erik Grijzen

Other Decks in Technology

Transcript

  1. Building Modern,
    Modular JavaScript
    Applications
    Netcentric Summit 2016

    View Slide

  2. Erik Grijzen
    Senior Frontend Developer @ Netcentric
    @ErikGrijzen
    www.erikgrijzen.com

    View Slide

  3. What is a module?

    View Slide

  4. Single purpose
    Do one thing and do one thing well - Unix philosophy

    View Slide

  5. It can live on it’s own.
    Independent

    View Slide

  6. Self contained
    Encapsulated private implementation.

    View Slide

  7. Public interface
    Exposes functionality for others to consume.

    View Slide

  8. Interchangeable
    Plug and play.

    View Slide

  9. Why do we need to write
    modular code?

    View Slide

  10. The rise of Web Apps
    Increasing frontend code complexity.

    View Slide

  11. Better reusability
    Don’t reinvent the wheel.

    View Slide

  12. Copying code
    Project A Project B
    js

    View Slide

  13. Better maintainability
    Updating a small, self-contained and independent
    module is much easier.

    View Slide

  14. Better testability
    Testing a small, self-contained and independent module
    is much easier.

    View Slide

  15. The global namespace
    Global namespace pollution and naming conflicts.

    View Slide

  16. Improved dependency management
    More explicit control over your dependencies.

    View Slide

  17. Script tag vomit

    View Slide

  18. Updating external libraries

    View Slide

  19. Included third party
    libraries into source control

    View Slide

  20. Optimized deployment
    An easy way to deploy your code.

    View Slide

  21. Libraries where you don’t
    use all its functionality

    View Slide

  22. Manual custom builds

    View Slide

  23. Modules allow us to build
    more complex, large-scale
    and robust applications.

    View Slide

  24. The evolution of
    JavaScript modules

    View Slide

  25. Grouping code
    var myApp = window.myApp || {};
    myApp.myModule = {
    init: function() {
    ...
    }
    };
    myApp.myModule.init();

    View Slide

  26. (function(){
    // Private scope
    })();
    Encapsulation

    View Slide

  27. (function($) {
    // Global import
    })(jQuery);
    Encapsulation

    View Slide

  28. var Counter = (function () {
    var counter = 0;
    function increment() {
    counter++;
    }
    return {
    increment: increment,
    };
    })();
    Counter.increment();
    Exposing a public interface

    View Slide

  29. var Counter = (function () {
    var counter = 0;
    function increment() {
    counter++;
    }
    return {
    increment: increment,
    };
    })();
    Counter.increment();
    Exposing a public interface
    Private implementation

    View Slide

  30. var Counter = (function () {
    var counter = 0;
    function increment() {
    counter++;
    }
    return {
    increment: increment,
    };
    })();
    Counter.increment();
    Exposing a public interface
    Public interface
    Private implementation

    View Slide

  31. // counter.js
    var counter = 0;
    function increment(){
    counter++;
    }
    module.exports = {
    increment: increment
    }
    CommonJS

    View Slide

  32. // counter.js
    var counter = 0;
    function increment(){
    counter++;
    }
    module.exports = {
    increment: increment
    }
    CommonJS
    // main.js
    var counter = require("./counter.js");
    counter.increment();

    View Slide

  33. CommonJS
    ● Standard for NodeJS
    ● Synchronous
    ● Build step required
    for browser support

    View Slide

  34. // counter.js
    define('counter', function() {
    var counter = 0;
    function increment() {
    counter++;
    }
    return {
    increment: increment
    };
    });
    Asynchronous Module Definition (AMD)

    View Slide

  35. // counter.js
    define('counter', function() {
    var counter = 0;
    function increment() {
    counter++;
    }
    return {
    increment: increment
    };
    });
    Asynchronous Module Definition (AMD)
    // main.js
    define('main', ['counter'],
    function(counter) {
    counter.increment();
    });

    View Slide

  36. AMD
    ● Mostly for browsers
    ● Asynchronous
    ● No build step
    ● Lazy loading

    View Slide

  37. Module system
    fragmentation.

    View Slide

  38. Supporting all module
    formats.

    View Slide

  39. (function (root, factory) {
    if (typeof define === 'function' && define.amd) {
    define([], factory);
    } else if (typeof module === 'object' && module.exports) {
    module.exports = factory();
    } else {
    root.returnExports = factory();
    }
    }(this, function () {
    var counter = 0;
    return {
    increment: function() {
    counter++;
    };
    }
    }));
    Universal Module Definition (UMD)

    View Slide

  40. (function (root, factory) {
    if (typeof define === 'function' && define.amd) {
    define([], factory);
    } else if (typeof module === 'object' && module.exports) {
    module.exports = factory();
    } else {
    root.returnExports = factory();
    }
    }(this, function () {
    var counter = 0;
    return {
    increment: function() {
    counter++;
    };
    }
    }));
    Universal Module Definition (UMD)
    AMD

    View Slide

  41. (function (root, factory) {
    if (typeof define === 'function' && define.amd) {
    define([], factory);
    } else if (typeof module === 'object' && module.exports) {
    module.exports = factory();
    } else {
    root.returnExports = factory();
    }
    }(this, function () {
    var counter = 0;
    return {
    increment: function() {
    counter++;
    };
    }
    }));
    Universal Module Definition (UMD)
    AMD
    CJS

    View Slide

  42. (function (root, factory) {
    if (typeof define === 'function' && define.amd) {
    define([], factory);
    } else if (typeof module === 'object' && module.exports) {
    module.exports = factory();
    } else {
    root.returnExports = factory();
    }
    }(this, function () {
    var counter = 0;
    return {
    increment: function() {
    counter++;
    };
    }
    }));
    Universal Module Definition (UMD)
    AMD
    CJS
    Global

    View Slide

  43. (function (root, factory) {
    if (typeof define === 'function' && define.amd) {
    define([], factory);
    } else if (typeof module === 'object' && module.exports) {
    module.exports = factory();
    } else {
    root.returnExports = factory();
    }
    }(this, function () {
    var counter = 0;
    return {
    increment: function() {
    counter++;
    };
    }
    }));
    Universal Module Definition (UMD)
    AMD
    CJS
    Global
    Module

    View Slide

  44. There must be a better way!

    View Slide

  45. A new way of writing
    JavaScript code.

    View Slide

  46. ES2015 Modules

    View Slide

  47. Modules are stored in files.
    JS

    View Slide

  48. Code is executed once it’s loaded.

    View Slide

  49. All code stays local to the module.

    View Slide

  50. Modules can import functionality.

    View Slide

  51. Modules can export functionality.

    View Slide

  52. One module per file.

    View Slide

  53. One file per module.

    View Slide

  54. Modules are singletons.

    View Slide

  55. A brief overview of the
    ES2015 module syntax

    View Slide

  56. Import & Export

    View Slide

  57. // counter.js
    var counter = 0;
    function increment() {
    return counter++;
    }
    function reset() {
    counter = 0;
    }
    Multiple named exports

    View Slide

  58. // counter.js
    var counter = 0;
    export function increment() {
    return counter++;
    }
    export function reset() {
    counter = 0;
    }
    Multiple named exports

    View Slide

  59. // counter.js
    var counter = 0;
    export function increment() {
    return counter++;
    }
    export function reset() {
    counter = 0;
    }
    Named import
    // app.js
    import { increment } from 'counter';
    increment(); // 1

    View Slide

  60. // counter.js
    var counter = 0;
    export function increment() {
    return counter++;
    }
    export function reset() {
    counter = 0;
    }
    Namespace import
    // app.js
    import * as counter from 'counter';
    counter.increment(); // 1
    counter.reset(); // 0

    View Slide

  61. // func.js
    export default function() {
    ···
    }
    Single default export

    View Slide

  62. // func.js
    export default function() {
    ···
    }
    Default import
    // app.js
    import myFunc from 'func';
    myFunc();

    View Slide

  63. Benefits over
    CJS and AMD?
    ● Cleaner syntax
    ● Sync and async
    ● Multiple exports
    ● Cyclic dependencies
    ● Static module structure

    View Slide

  64. Static module structure

    View Slide

  65. // Error
    if (someBooleanValue) {
    import myModule from 'module1';
    } else {
    import myModule from 'module2';
    }
    Static imports & exports

    View Slide

  66. Benefits of a
    static module
    structure?
    ● Dead code elimination
    ● Variable checking
    ● Type checking

    View Slide

  67. Module syntax IDE support

    View Slide

  68. Browser support:

    View Slide

  69. What now?!?

    View Slide

  70. Instead of waiting on
    browser adoption...

    View Slide

  71. Transpiler allow us to use
    the future syntax today!

    View Slide

  72. What are transpilers?

    View Slide

  73. ES2015 ES5
    compile

    View Slide

  74. ES2015 ES5
    compile
    CommonJS

    View Slide

  75. ES2015 ES5
    compile
    AMD

    View Slide

  76. ES2015 ES5
    compile
    Other

    View Slide

  77. View Slide

  78. Module Loader API

    View Slide

  79. Module Loader API
    (not part of ES2015)

    View Slide

  80. System.import('counter')
    .then(function(counter) {
    counter.increment();
    })
    .catch(function() {
    ···
    });
    Loader API

    View Slide

  81. JS
    JS
    JS
    JS
    JS
    JS
    JS
    Module Loader JS

    View Slide

  82. In the future HTTP/2 will
    be able to load all modules
    asynchronously in
    production.

    View Slide

  83. Until then, we have to
    bundle our modules into
    one single file.

    View Slide

  84. Module bundling.

    View Slide

  85. View Slide

  86. Modern module bundlers
    have a transpiler built-in.

    View Slide

  87. How does bundling work?

    View Slide

  88. Traversing the dependency tree.
    main.js bundle.js
    JS
    JS
    JS
    JS
    JS
    JS
    JS

    View Slide

  89. Mixing different module systems.
    main.js bundle.js
    AMD
    CJS
    ES2015
    JS
    JS
    JS
    JS

    View Slide

  90. Easy code splitting for lazy loading.
    bundle1.js
    AMD
    CJS
    ES2015
    bundle2.js
    bundle3.js
    JS
    JS
    JS
    JS

    View Slide

  91. Bundling common code together.
    bundle1.js
    AMD
    CJS
    ES2015
    bundle2.js
    bundle3.js
    common.js
    JS
    JS
    JS
    JS

    View Slide

  92. Tree-shaking

    View Slide

  93. Normal bundling behavior.
    counter.js
    increment() {

    }
    reset() {

    }

    View Slide

  94. Normal bundling behavior.
    import { increment }
    from ‘counter’;
    increment();
    counter.js main.js
    increment() {

    }
    reset() {

    }

    View Slide

  95. Normal bundling behavior.
    counter.js
    increment() {

    }
    reset() {

    }
    import { increment }
    from ‘counter’;
    increment();
    main.js bundle.js
    increment() {

    }
    reset() {

    }
    increment();

    View Slide

  96. Removing unused exports.
    counter.js
    increment() {

    }
    reset() {

    }
    import { increment }
    from ‘counter’;
    increment();
    main.js bundle.js
    increment() {

    }
    reset() {

    }
    increment();

    View Slide

  97. Removing unused exports.
    import { increment }
    from ‘counter’;
    increment();
    counter.js main.js bundle.js
    increment() {

    }
    reset() {

    }
    increment() {

    }
    increment();

    View Slide

  98. Modularization

    View Slide

  99. Third party modules.

    View Slide

  100. Package Managers

    View Slide

  101. View Slide

  102. NPM - The biggest
    package manager on earth!

    View Slide

  103. www.modulecounts.com

    View Slide

  104. What is a package?

    View Slide

  105. A folder containing a program.
    Package
    index.js

    View Slide

  106. This program is described by a
    package.json file.
    Package
    index.js
    package.json

    View Slide

  107. {
    "name": "test-package",
    "version": "1.0.0",
    "main": "index.js",
    "repository": {
    "type": "git",
    "url": "git+https://github.com/..."
    },
    "author": "Erik Grijzen"
    }
    package.json

    View Slide

  108. Ideal workflow:
    Two step approach

    View Slide

  109. 1). Installing a package.
    > npm install angular --save
    Terminal

    View Slide

  110. {
    "name": "test-package",
    "version": "1.0.0",
    "main": "index.js",
    "dependencies": {
    "angular": "1.4.9",
    },
    "author": "Erik Grijzen"
    }
    package.json

    View Slide

  111. 2). Import a package.
    import angular from 'angular';
    index.js

    View Slide

  112. Package installation folder.
    Package
    /node_modules
    index.js
    package.json

    View Slide

  113. Excluding installed packages from
    source control.
    Package
    /node_modules
    index.js
    package.json
    .gitignore

    View Slide

  114. Semantic versioning.

    View Slide

  115. 1.0.0
    Published packages
    Not everyone is following this rule

    View Slide

  116. 1.0.1
    Patch release
    Bug fixes and patches.

    View Slide

  117. 1.2.0
    Minor release
    New features that don’t break existing features.

    View Slide

  118. 2.0.0
    Major release
    Changes that break backwards compatibility.

    View Slide

  119. Useful version ranges.

    View Slide

  120. ~1.0.0
    Installs: 1.0.8
    Automatic bugfix updates.

    View Slide

  121. ^1.0.0
    Installs: 1.4.9
    Automatic bugfix and feature updates.

    View Slide

  122. Don’t blindly trust all
    published packages.

    View Slide

  123. Modern component-based
    architecture

    View Slide

  124. Most modern frameworks
    support component-based
    development.

    View Slide

  125. Composing your application into
    smaller components.
    Component

    View Slide

  126. Modularized components.

    View Slide

  127. Component

    View Slide

  128. One entry point.
    JS

    View Slide

  129. Importing other assets.
    JS
    HTML CSS PNG
    JS

    View Slide

  130. Importing other assets.
    JS
    JADE SCSS PNG
    JS

    View Slide

  131. Component-based
    project structure

    View Slide

  132. Self-contained components.
    Component
    component.js
    component.css
    component.html
    component.test.js
    image.png

    View Slide

  133. import styles from './component.css';
    import template from './component.html';
    import image from './logo.png';
    var img = document.createElement('img');
    img.src = image;
    Importing other assets

    View Slide

  134. Inlining all static assets.
    Component
    Component
    Component
    Component
    bundle.js

    View Slide

  135. Create separate bundles.
    Component
    Component
    Component
    Component
    bundle.js
    bundle.css
    logo.png

    View Slide

  136. Creating common bundles.
    Component
    Component
    Component
    Component
    bundle.js
    bundle.css
    logo.png
    common.css

    View Slide

  137. Easily removing components.
    Component
    Component
    Component
    Component
    bundle.js
    bundle.css

    View Slide

  138. Easily removing components.
    Component
    Component
    Component
    bundle.js
    bundle.css

    View Slide

  139. Developer experience

    View Slide

  140. We are all familiar with
    browser syncing.

    View Slide

  141. What about
    module syncing?

    View Slide

  142. Hot module loaders

    View Slide

  143. Live editing experience.

    View Slide

  144. Frameworks and libraries
    are moving to ES2015.

    View Slide

  145. Focus on learning the new
    JavaScript standards.

    View Slide

  146. The future is bright, let’s
    start using ES2015 today!

    View Slide

  147. Thank you!
    Senior Frontend Developer @ Netcentric
    @ErikGrijzen
    www.erikgrijzen.com

    View Slide