JavaScript Dependencies, Modules & Browserify
Upcoming SlideShare
Loading in...5
×
 

JavaScript Dependencies, Modules & Browserify

on

  • 4,972 views

JavaScript dependencies & modules ...

JavaScript dependencies & modules

Introduction to Browserify and how to use CommonJS/Node.js modules in the browser. We'll start with a brief look at the current landscape of handling dependencies and modules.

Talk at sthlm.js 2014-01-07, http://www.meetup.com/sthlm-js/events/156429912/

Demos & code examples; https://github.com/johannilsson/sthlmjs-1401

Statistics

Views

Total Views
4,972
Views on SlideShare
4,958
Embed Views
14

Actions

Likes
12
Downloads
25
Comments
0

1 Embed 14

https://twitter.com 14

Accessibility

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

JavaScript Dependencies, Modules & Browserify JavaScript Dependencies, Modules & Browserify Presentation Transcript

  • Dependencies & Modules Wednesday, January 8, 14 To handle dependencies and modules in a simple way we need some proper tooling.
  • Wednesday, January 8, 14 Then talk a bit about Browserify.
  • Wednesday, January 8, 14 I’ve been writing JS for quite a while, then I was off doing mostly backend & Android for while. This is Theo, I felt like him here towards JS for a while I feel that the tools around for JS has increased and grown in complexity quite fast. It also feels like it is changing quite rapid. This is also what attracts me to JS, for me it feels like this is where a lot of creativity and innovation is happening. But picking the right tools is a/has been challenge.
  • Web Sites vs Web Apps What’s the difference? Wednesday, January 8, 14 I find this quite interesting. And is there a difference and why do we make a difference?
  • We’re all building sites that people visit, do something, and leave. Differentiating websites vs. web apps is no good to anyone. – Jack Franklin https://speakerdeck.com/jackfranklin/port80-practical-javascripting Wednesday, January 8, 14
  • Why do you want to make that distinction? What benefit do you gain by arbitrarily dividing the entire web into two classes? – Jeremy Keith http://adactio.com/journal/6246/ Wednesday, January 8, 14
  • A lot of people ignore new JavaScript tools, methods or approaches because those are just for “web apps.” – Jack Franklin https://speakerdeck.com/jackfranklin/port80-practical-javascripting Wednesday, January 8, 14 I’ve done this for too long.
  • Dev Environment • Module System: A way to programmatically load scripts when they’re needed. • Package Manager: Fetch scripts from online sources like GitHub, taking dependencies into account. • Build Tool: Combine scripts and other assets together into something usable by browsers. http://dailyjs.com/2013/01/28/components/ Wednesday, January 8, 14 And to handle modules and dependencies properly we need to use proper tools to cope with a modern development environment
  • Modules A way to programmatically load scripts when they’re needed Wednesday, January 8, 14 Let’s start with modules. What is a module? Keep it simple, it’s basically just a script that we can load whenever we need it.
  • <script <script <script <script <script <script <script <script <script <script type="text/javascript" type="text/javascript" type="text/javascript" type="text/javascript" type="text/javascript" type="text/javascript" type="text/javascript" type="text/javascript" type="text/javascript" type="text/javascript" src="/js/namespace.js"></script> src="/js/vendor/buffer-loader.js"></script> src="/js/vendor/requestanimframe.js"></script> src="/js/vendor/stats.min.js"></script> src="/js/vendor/minpubsub.js"></script> src="/js/app.js"></script> src="/js/utils.js"></script> src="/js/sound.js"></script> src="/js/shape.js"></script> src="/js/main.js"></script> Wednesday, January 8, 14 We can easily do this by separating our code into various scripts. It gets a bit troublesome to manage this though. For me this way hinders me from playing around and prototyping and testing things out while developing.
  • SRC_DIR = src BUILD_DIR = build JS_SERVER_FILES = $(SRC_DIR)/js/namespace.js ! $(SRC_DIR)/js/vendor/buffer-loader.js ! $(SRC_DIR)/js/vendor/requestanimframe.js ! $(SRC_DIR)/js/vendor/stats.min.js ! $(SRC_DIR)/js/vendor/minpubsub.js ! $(SRC_DIR)/js/app.js ! $(SRC_DIR)/js/utils.js ! $(SRC_DIR)/js/sound.js ! $(SRC_DIR)/js/shape.js ! $(SRC_DIR)/js/main.js JS_BUNDLE = $(BUILD_DIR)/app.js BUILD_BUNDLE = $(BUILD_DIR)/app-min.js .DEFAULT_GOAL := all all: bundle ! java -jar $(GCC_COMPRESSOR) $(GCC_OPTION) --js=$(JS_BUNDLE) -js_output_file=$(BUILD_BUNDLE) debug: bundle ! cat $(JS_FILES) >> $(JS_BUNDLE) Wednesday, January 8, 14 To assemble this for production we would need a build script. This is a Makefile from an old project of mine. Sure we can have grunt but it is still configuration clean: we need to add. ! rm -Rf $(BUILD_DIR)/* For me this isn’t really prototyping friendly.
  • <% if (production) { %> <script type="text/javascript" <% } else { %> <script type="text/javascript" <script type="text/javascript" <script type="text/javascript" <script type="text/javascript" <script type="text/javascript" <script type="text/javascript" <script type="text/javascript" <script type="text/javascript" <script type="text/javascript" <script type="text/javascript" <% } %> src="/js/app-min.js"></script> src="/js/namespace.js"></script> src="/js/vendor/buffer-loader.js"></script> src="/js/vendor/requestanimframe.js"></script> src="/js/vendor/stats.min.js"></script> src="/js/vendor/minpubsub.js"></script> src="/js/app.js"></script> src="/js/utils.js"></script> src="/js/sound.js"></script> src="/js/shape.js"></script> src="/js/main.js"></script> Wednesday, January 8, 14 And we add a conditional to check if we are in production. Personally I don’t like this since it means that we’re during development will most likely run a different version of the site than we will in production.
  • The Problem • • • • Code complexity grows as the site gets bigger Assembly gets harder Developer wants discrete JS files/modules Deployment wants optimized code in just one or a few HTTP calls http://requirejs.org/docs/why.html#1 Wednesday, January 8, 14 Why do we want modules. Require.JS outlines a few arguments on there website. We basically want it because we need better structure in our code.
  • Current Landscape • • • • • • Globals Globals + Namespace AMD (Asynchronous Module Definition) CommonJS UMD (Universal Module Definition) (ES6) Wednesday, January 8, 14 Alternatives for modules, not taking framework specific ones like Angular into account.
  • var isEven = function(i) { return i % 2 === 0; }; Wednesday, January 8, 14 I like small bits of code that can be separated and re-used. Here’s a quite simple re-usable bit of code but is short enough to fit a slide. Simple re-usable code
  • Globals Wednesday, January 8, 14
  • even.js var isEven = function(i) { return i % 2 === 0; }; Wednesday, January 8, 14
  • app.js console.log(1, console.log(2, console.log(3, console.log(4, Wednesday, January 8, 14 isEven(1)); isEven(2)); isEven(3)); isEven(4));
  • index.html <!doctype html> <script src="even.js"></script> <script src="app.js"></script> Wednesday, January 8, 14
  • Globals + Namespace Wednesday, January 8, 14
  • app-even.js var App = App || {}; App.isEven = function(i) { return i % 2 === 0; }; Wednesday, January 8, 14
  • app.js var App = App || {}; console.log(1, console.log(2, console.log(3, console.log(4, Wednesday, January 8, 14 App.isEven(1)); App.isEven(2)); App.isEven(3)); App.isEven(4));
  • index.html <!doctype html> <script src="app-even.js"></script> <script src="app.js"></script> Wednesday, January 8, 14
  • AMD Asynchronous Module Definition With Require.JS Wednesday, January 8, 14
  • even.js define([], function() { var isEven = function(i) { return i % 2 === 0; }; return isEven; }); Wednesday, January 8, 14
  • app.js require(['even'], function(even){ console.log(1, even(1)); console.log(2, even(2)); console.log(3, even(3)); console.log(4, even(4)); }); Wednesday, January 8, 14
  • index.html <!doctype html> <script data-main="js/app.js" src="js/require.js"></script> Wednesday, January 8, 14
  • //Allow for anonymous modules if (typeof name !== 'string') { //Adjust args appropriately callback = deps; deps = name; name = null; } //This module may not have dependencies if (!isArray(deps)) { callback = deps; deps = null; } require.js //If no name, and callback is a function, then figure out if it a //CommonJS thing with dependencies. if (!deps && isFunction(callback)) { deps = []; //Remove comments from the callback string, //look for require calls, and pull them into the dependencies, //but only if there are function args. if (callback.length) { callback .toString() .replace(commentRegExp, '') .replace(cjsRequireRegExp, function (match, dep) { deps.push(dep); }); //May be a CommonJS thing even without require calls, but still //could use exports, and module. Avoid doing exports and module //work though if it just needs require. //REQUIRES the function to expect the CommonJS variables in the //order listed below. deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps); } } //If in IE 6-8 and hit an anonymous define() call, do the interactive //work. if (useInteractive) { node = currentlyAddingScript || getInteractiveScript(); if (node) { if (!name) { name = node.getAttribute('data-requiremodule'); } context = contexts[node.getAttribute('data-requirecontext')]; } } //Always save off evaluating the def call until the script onload handler. //This allows multiple modules to be in a file without prematurely //tracing dependencies, and allows for anonymous module support, //where the module name is not known until the script onload event //occurs. If no context, use the global queue, and get it processed //in the onscript load callback. (context ? context.defQueue : globalDefQueue).push([name, deps, callback]); }; define.amd = { jQuery: true }; Wednesday, January 8, 14 /** * Executes the text. Normally just uses eval, but can be modified * to use a better, environment-specific call. Only used for transpiling * loader plugins, not for plain JS modules. * @param {String} text the text to execute/evaluate. */ req.exec = function (text) { /*jslint evil: true */ return eval(text); }; Quite a massive file that is required //Set load these files. Not sure exactly what goes on here, but it works. to up with config info. req(cfg); }(this));
  • Makefile / Build step build: ! node r.js -o name=app out=js/app-built.js baseUrl=./js build-even: ! node r.js -o name=even out=js/even-built.js baseUrl=./js Wednesday, January 8, 14 For production you might want to bundle your files into one bundle. You still depend on require.js though. And you will have one setup for development and another one for production.
  • CommonJS / Node.js Wednesday, January 8, 14
  • even.js var isEven = function(i) { return i % 2 === 0; }; module.exports = isEven; Wednesday, January 8, 14
  • app.js var isEven = require('./even'); console.log(1, console.log(2, console.log(3, console.log(4, Wednesday, January 8, 14 isEven(1)); isEven(2)); isEven(3)); isEven(4));
  • UMD Universal Module Definition Wednesday, January 8, 14
  • var shim = {}; if (typeof(exports) === 'undefined') { if(typeof define == 'function' && typeof define.amd == 'object' && define.amd) { // we are in amd. shim.exports = {}; define(function() { return shim.exports; }); } else { // we are in a browser, define its namespaces in global shim.exports = typeof(window) !== 'undefined' ? window : _global; } } else { // we are in commonjs, define its namespaces in exports shim.exports = exports; } even-lib.js (function(exports) { var isEven = function(i) { return i % 2 === 0; }; if (typeof(exports) !== 'undefined') { exports.isEven = isEven; } })(shim.exports); })(this); header.js Wednesday, January 8, 14 This approach has been quite popular for library developers to be able to meet end users requirements.
  • var shim = {}; if (typeof(exports) === 'undefined') { if(typeof define == 'function' && typeof define.amd == 'object' && define.amd) { // we are in amd. shim.exports = {}; define(function() { return shim.exports; }); } else { // we are in a browser, define its namespaces in global shim.exports = typeof(window) !== 'undefined' ? window : _global; } } else { // we are in commonjs, define its namespaces in exports shim.exports = exports; } even-lib.js header.js (function(exports) { even.js footer.js header.js var isEven = function(i) { return i % 2 === 0; }; if (typeof(exports) !== 'undefined') { exports.isEven = isEven; } })(shim.exports); })(this); Wednesday, January 8, 14 This approach has been quite popular for library developers to be able to meet end users requirements.
  • Makefile / Build step build: ! cat header.js even.js footer.js > even-lib.js Wednesday, January 8, 14
  • ES6 export function isEven() {} import { isEven } from 'even'; Wednesday, January 8, 14 It’s not here yet.
  • AMD & CommonJS Wednesday, January 8, 14 For me, there’s really only two options here. AMD or CommonJS. Both of these solves the issue of loading modules.
  • Wednesday, January 8, 14 Various alternatives around for common js, as far as I know there is only require js for AMD
  • CommonJS Wednesday, January 8, 14 Various alternatives around for common js, as far as I know there is only require js for AMD
  • CommonJS AMD Wednesday, January 8, 14 Various alternatives around for common js, as far as I know there is only require js for AMD
  • CommonJS AMD Wednesday, January 8, 14 Various alternatives around for common js, as far as I know there is only require js for AMD
  • What I want • • • • • Wednesday, January 8, 14 Simple Active community No complicated build environment Works with existing libraries, with none or minimal adaption Prototype friendly
  • require('modules') in the browser Wednesday, January 8, 14 http://browserify.org/, substack
  • Demo Wednesday, January 8, 14
  • Browserify • • • Wednesday, January 8, 14 require & npm modules in the browser Browser versions of core node modules (events, stream, path, url, ...) Extendable
  • How does it work? • • • • Wednesday, January 8, 14 Small wrapper for the require function Build process that keeps track of dependencies Bundles to one file No global leakage
  • ; (function e(t, n, r) { function s(o, u) { if (!n[o]) { if (!t[o]) { var a = typeof require == "function" && require; if (!u && a) return a(o, !0); if (i) return i(o, !0); throw new Error("Cannot find module '" + o + "'") } var f = n[o] = { exports: {} }; t[o][0].call(f.exports, function (e) { var n = t[o][1][e]; return s(n ? n : e) }, f, f.exports, e, t, n, r) } return n[o].exports } var i = typeof require == "function" && require; for (var o = 0; o < r.length; o++) s(r[o]); return s })({ 1: [ function (require, module, exports) { var isEven = function (i) { return i % 2 === 0; }; module.exports = isEven; }, {} ], 2: [ function (require, module, exports) { var isEven = require('./even'); console.log(isEven(2)); }, { "./even": 1 } ] }, {}, [2]); Wednesday, January 8, 14
  • voxel.js http://voxeljs.com/ Wednesday, January 8, 14
  • Dependencies Package Manager Fetch scripts, taking dependencies into account Wednesday, January 8, 14
  • Current Landscape • • • • • • Ender (http://ender.jit.su/) Volo (http://volojs.org/) Jam (http://jamjs.org/) Bower (http://bower.io/) 7 000+ Component (http://component.io/) npm (http://npmjs.org) 53 000+ Wednesday, January 8, 14 Quite a few alternatives around. Not likely that all of these will survive. Currently it seems like Bower is the one getting more and more attraction for front end developers
  • Transformations • • • • • AMD via deamdify Bower via debowerify Global via deglobalify ES6 via es6ify & more Wednesday, January 8, 14
  • { "name": "bower-demo", "version": "0.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo "Error: no test specified" && exit 1" }, "author": "Johan Nilsson", "license": "BSD-2-Clause", "devDependencies": { "debowerify": "~0.5.1" } } Wednesday, January 8, 14
  • { "name": "bower-demo", "main": "index.js", "version": "0.0.0", "authors": [ "Johan Nilsson <johan@markupartist.com>" ], "license": "BSD-2-Clause", "private": true, "dependencies": { "jquery": "~2.0.3" } } Wednesday, January 8, 14
  • var $ = require('jquery'); $('body').append('<p>bla bla</p>'); Wednesday, January 8, 14
  • browserify index.js -t debowerify > bundle.js Wednesday, January 8, 14
  • Additional Tools • • • • Wednesday, January 8, 14 Watchify – Watch mode for browserify builds Beefy – Local development server grunt-browserify gulp-browserify
  • Johan Nilsson johan@markupartist.com @johanni Wednesday, January 8, 14