building large scale javascript application
Post on 27-Aug-2014
1.083 Views
Preview:
DESCRIPTION
TRANSCRIPT
Building Large ScaleJavascript Application
It's a little different than traditional JS
Who are the target audience?What is Javascript?
I use JS to do form validation
Javascript is the interactive tool that I use for user intraction
I write the full app in JS
What do we actually mean by Large Scale JavascriptApplication?
Large in size?
Large in complexity?
Large in developer count?
Why do we care?The ability of browsers have increased
The load on the client, rather than on the server
High level of interaction
What is the secret of building BIG things?
Aircrafts are amazing architecture!
How they made this crazy huge thing?
The secret to building large apps is never build largeapps. Break up your applications into small pieces.
Then, assemble those testable, bite-sized pieces intoyour big– Justin Meyer
Okay, Let's startAs we generally start
We have a good main.js
function somethingGood() { // Good code}
// few other functions ...
$( document ).ready(function() { $('#aButton').onClick(function(e) { // Do something });
// few event bindings ...});
But at the end, it become bad!
function somethingGood() { // ## Bad code, full of mess ##}
// 39 strange functions with 40% repetation
$( document ).ready(function() { $('#aButton').onClick(function(e) { // Do something ## became 60 lines! ## });
// ## 33 bindings ## , some event may bound multiple times});
Let's break them down
Files++
Filesize--
Lots of files...
Let’s Organise them
By feature By Layer
By feature By Layer
Lets call them Modules
Okay, but...
What is a Module?
My Yahoo!
Modules made it!
Modules are (preferably) small, focused, purposefulportion of code that -
Modules ... portion of code that -
Provides a specific feature
Modules ... portion of code that -
Contains business logic
Modules ... portion of code that -
Is decoupled (from other modules)
Modules ... portion of code that -
Is reusable (context matters)
Modules ... portion of code that -
Is testable
A simple module looks like -
var MyModule = ( function( window, $ ) { function aPrivateMethod() { $('catchSomething').load('http://...'); }
function myPublicMethod() { privateMethod(); alert( 'my other method' ); }
// explicitly return public methods when this object is instantiated return { someMethod : myPublicMethod };} )( window, jQuery );
Module rules
Module Rule #1
Never access the DOM outside the module
Module Rule #2
Don't Create Global variables
Module Rule #3
Don’t Access Non-Native, Global objects
Module Rule #4
Inject Dependencies
Module Communationwith other modules
Mediator PatternMediators are used when the communication between modules may be complex, but is still
well defined.
Mediators and Modules
Mediator publish/subscribe example
// example subscriber functionvar Subscriber = function ExampleSubscriber( myVariable ) { console.log( myVariable );};
// example usagesvar myMediator = new Mediator();myMediator.subscribe( 'some event', Subscriber );myMediator.publish( 'some event', 'foo bar' ); // console logs "foo bar"
Well defined Interfacing
Dependencies
Dependency on other code/Globals
Dependency on other files
Dependency on third party libraries
$('#my‐button').click(function() { $.get('https://api.github.com', function(data) { $('#res').html(data.emojis_url); });});
Closure cannot be reused
Closure cannot be tested
$.get and $('#res') using global object
var downloadEmojis = function() { $.get('https://api.github.com', function(data) { $('#res').html(data.emojis_url); });};
$('#my‐button').click(downloadEmojis);
Difficult to test
$.get and $('#res') using global object
var downloadEmojis = function(ajax, $el) { ajax.get('https://api.github.com', function(data) { $el.html(data.emojis_url); });};
$('#my‐button').click(function() { downloadEmojis($, $('#res'));});
Now we can controle dependencies :)
Dependency on other files/Modules
Scripts behind this presentation
<script src="bower_components/bespoke.js/dist/bespoke.min.js"></script><script src="bower_components/bespoke‐keys/dist/bespoke‐keys.min.js"></script><script src="bower_components/bespoke‐touch/dist/bespoke‐touch.min.js"></script><script src="bower_components/bespoke‐bullets/dist/bespoke‐bullets.min.js"></script><script src="bower_components/bespoke‐scale/dist/bespoke‐scale.min.js"></script><script src="bower_components/bespoke‐hash/dist/bespoke‐hash.min.js"></script><script src="bower_components/bespoke‐progress/dist/bespoke‐progress.min.js"></script><script src="bower_components/bespoke‐state/dist/bespoke‐state.min.js"></script><script src="bower_components/prism/prism.js"></script><script src="bower_components/prism/components/prism‐php.min.js"></script><script src="scripts/main.js"></script>
How can we make it better?
AMD
COMMON.JS
Let's see a little bit more about AMDUsing require.js
Using require.js
Include Require JS
<script data‐main="scripts/main" src="scripts/require.js"></script>
Using require.js
Configure Paths
require.config({ baseUrl: 'js/lib', paths: { jquery: 'jquery‐1.9.0' parse : 'parse‐1.2.18.min', underscore: 'underscore', backbone: 'backbone', marionette: 'backbone.marionette' }});
Using require.js
Define modules
define([ 'app', 'marionette'], function(app, Marionette){
return ExampleModule = app.module("Example", function(Example) { this.startWithParent = false;
this.addInitializer(function(){ console.log('Module:Example => initialized'); this.router = new Router({ controller: Controller }); }); });});
Asynchronous loading
On demand script loading
if(teamProfile) { // Load and show team profile require(['views/TeamProfileView'], function(TeamProfileView) { var teamInfo = { model : app.reqres.request('core:team', shortName) } app.main.show(new TeamProfileView(teamInfo)); });}
Dependency on third party libraries
http://bower.io/
list dependencies in bower.json
// bower.json{ "dependencies": { "angular": "~1.0.7", "angular‐resource": "~1.0.7", "jquery": "1.9.1"} }
MV* Frameworks
Seperation between Data and DOMM → Data | V → DOM | * → has many variations
MVCController → Mediates inputs and manipulates the model
MVPPresenter → all presentation logic is pushed to the presenter
MVVMViewModel → exposes model so it can be easily managed and consumed
How to select the right one?Why they are this huge in nymbers? :(
Same todo app built around 70 times with different frameworks and approaches
Let's see how BackboneJS did it
Todo Model
(function () { app.Todo = Backbone.Model.extend({ // and ensure that each todo created has title and completed keys. defaults: { title: '', completed: false },
toggle: function () { this.save({ completed: !this.get('completed') }); } });})();
Create a Todo
var todo = new app.Todo({ title: "Do something good!"});
What about list of Todos?
var Todos = Backbone.Collection.extend({ model: app.Todo,
// Filter down the list of all todo items that are finished. completed: function () { return this.filter(function (todo) { return todo.get('completed'); }); },
// Many other functions related to list ... });
Collections
Create a Todo list
var todoList = new Todos( {title: 'Do something good'}, {title: 'Another Task'}, {title: 'This task is Done', completed: true},);
Who will create them actually?
collection.fetch(); // Pulls list of items from servercollection.create(); // Create new item in list
// Sync a modelmodel.fetch(); // Fetch an item from servermodel.save(); // Save changes to modelmodel.destroy(); // Delete a model
Template
<script type="text/template" id="item‐template"> <div class="view"> <input class="toggle" type="checkbox" <%= completed ? 'checked' : '' %>> <label><%= title %></label> <button class="destroy"></button> </div> <input class="edit" value="<%‐ title %>"></script>
The DOM to render models or collection
Views
app.TodoView = Backbone.View.extend({ tagName: 'li', template: _.template($('#item‐template').html()),
initialize: function () { this.listenTo(this.model, 'change', this.render); this.listenTo(this.model, 'visible', this.toggleVisible); },
render: function () { this.$el.html(this.template(this.model.toJSON())); return this; }});
Takes models, Render them, listning to events
Views cont.
events: { 'click .toggle': 'toggleCompleted', 'dblclick label': 'edit'},
toggleCompleted: function () { this.model.toggle();},
edit: function () { this.$el.addClass('editing'); this.$input.focus();},
Handling to DOM events and manipulate model
Routers
var TodoRouter = Backbone.Router.extend({ routes: { '*filter': 'setFilter', 'url/pattern': 'handlerFunction' 'url/pattern/:param': 'handlerFunction' },
setFilter: function (param) { // Trigger a collection filter event app.todos.trigger('filter'); } });
Handles the routing with url changes
Start listning to url changes
app.TodoRouter = new TodoRouter();Backbone.history.start();
Url change? reload?... NO
http://the/app/url.com#a‐routehttp://the/app/url.com#another‐routehttp://the/app/url.com#a‐route/withParam/23
No more todayIt was a lot of things... ain't it?
Wait... Do I really need aaall of these?Well, depends on your apps requirement
Resource
http://superherojs.com/
About us
[ { "name": "Mohammad Emran Hasan", "mail": "phpfour@gmail.com", "blog": "http://emranhasan.com" }, { "name": "Anis Uddin Ahmad", "mail": "anisniit@gmail.com", "blog": "http://ajaxray.com" }]
Questions?
top related