Using RequireJS with CakePHP

Post on 17-May-2015

3.717 views 2 download

Tags:

description

CakeFest 2013 presentation

Transcript of Using RequireJS with CakePHP

USINGREQUIREJS

WITH CAKEPHPStephen Young @young_steveo

Sr. Software Engineer - Zumba Fitness

WHAT'S THE POINT?

The Javascript layer of a typical MVC app starts simple.

...like a jQuery Zen garden.

$(function(){ /* animate the blog title */ $('.title').slideDown();});

As the project matures, things get a little more tricky.$(function(){ /* animate the blog title */ $('.title').slideDown(350, function(){ $.get('/stats/count').done(function(data){ $('.statsCount') .html(data.count) .addClass('green'); }); });});

Still maintainable... -ish

...before long

/* mega_file.js */$(function(){ if (window.isBlogPage && typeof window.App !== 'undefined') { var app = new App(); /* animate the blog title */ $('.title').slideDown(350, function(){ $.get('/stats/count').done(function(data){ $('.statsCount').html(data.count).addClass('green'); $.getScript('/js/superAwesomePlugin', function(){ $.get('/posts/someData').done(function(data){ if(data.posts){ doSomeCoolStuff(data.posts); } }); }); }); });

} else if (window.isCategory) {

/* oh, god, please, stop! */

Don't forget the layout file...

<script src="js/box2d/common/b2Settings.js"></script><script src="js/box2d/common/math/b2Vec2.js"></script><script src="js/box2d/common/math/b2Mat22.js"></script><script src="js/box2d/common/math/b2Math.js"></script><script src="js/box2d/collision/b2AABB.js"></script><script src="js/box2d/collision/b2Bound.js"></script><script src="js/box2d/collision/b2BoundValues.js"></script><script src="js/box2d/collision/b2Pair.js"></script><script src="js/box2d/collision/b2PairCallback.js"></script><script src="js/box2d/collision/b2BufferedPair.js"></script><script src="js/box2d/collision/b2PairManager.js"></script><script src="js/box2d/collision/b2BroadPhase.js"></script><script src="js/box2d/collision/b2Collision.js"></script><script src="js/box2d/collision/Features.js"></script><script src="js/box2d/collision/b2ContactID.js"></script><script src="js/box2d/collision/b2ContactPoint.js"></script><script src="js/box2d/collision/b2Distance.js"></script>

THERE IS A BETTER WAY.

WHAT IS REQUIREJS?javascript module & file loader

AMD (asynchronous module definition)

optimization tool

http://requirejs.org

WHAT'S SO GREAT ABOUTAMD?

Encapsulation

Dependency Management

AMD EXAMPLEDefine your modules:

/* define(id?, dependencies?, factory); */define("SomeModule", ["dependency"], function(dependency) { var SomeModule = function(){}; SomeModule.prototype.init = function(){ dependency.doStuff(); }; /* etc. */ return SomeModule;});

AMD EXAMPLELoad your modules and use them:

/* require(dependencies, callback); */require(["ModuleA", "ModuleB"], function(ModuleA, ModuleB) { var module = new ModuleA(); ModuleA.init(); /* etc. */});

REQUIRE()OR

DEFINE() ?Use `define` to declare a module for use elsewhere.

Use `require` to pre-load dependencies and/or do somestuff.

WHAT IS CAKEPHP?(just kidding)

HELLO WORLD/* app/Views/Layouts/someLayout.ctp */echo $this->Html->script("require.js", [ "data-main" => "/js/bootstrap"]);

/* app/webroot/js/bootstrap.js */requirejs.config({ paths : { jquery : 'js/vendor/jquery.min' }});require(['js/libs/blogs']);

/* app/webroot/js/libs/blogs.js */require(['jquery'], function($){ $(function(){ /* etc. */ });});

IT IS THAT SIMPLE.IS IT THAT SIMPLE?IT NEVER IS, IS IT?

Even with RequireJS, things can get out of hand.

As with many great tools, RequireJS gives you plenty of"rope."

TIPSKeep your modules focused; remember the singleresponsibility principle.

Define a solid directory structure, and stick to it.(like your CakePHP /app directory)

Think "automagic" and document your conventions./js/README.md

GOING BEYOND"HELLO WORLD"

i.e. What if your project contains LOTS of javascript?

DISCLAIMER

This works for us, and we like it, but feel free to tweak it tomeet your needs.

image from gifsoup.com

IDENTIFY SECTIONS FORYOUR PROJECT

1. Break the project down into sections, and create amodule for each section (e.g. blog.js, shop.js, etc).

2. Identify any functionality that would be shared acrossmultiple sections, and put them into common modules.

CHANGE THE LAYOUTBEFORE:

/* (data-main does the heavy lifting) */echo $this->Html->script("require.js", [ "data-main" => "/js/bootstrap"]);

AFTER:/* no more data-main */echo $this->Html->script("require.js");

<script>

require(['/js/bootstrap.js'], function(){ require(['modules/<?php echo $javascriptModule; ?>']); });

</script>

MODIFY THEAPPCONTROLLER

public function beforeRender(){ $this->set(array( 'javascriptModule' => $this->javascriptModule ));} public function beforeFilter() { /* just a nice convention, you could use a default file too */ if (!$this->javascriptModule){ $moduleName = Inflector::underscore($this->name); $this->javascriptModule = $moduleName; }}

NEW BOOTSTRAP.JS FILErequirejs.config({ paths : { jquery : 'js/vendor/jquery', someLib : 'js/libs/someLib', anotherLib : 'js/libs/anotherLib' }, shim : { anotherLib : ['jquery', 'someLib'] }});

OPTIMIZATION

R.JSRequireJS ships with a great minification/concatenation

tool

It runs in Node.js, Java with Rhino, or a browser

It is configurable

Bonus: it can minify CSS files for you too (but I'm notgoing to cover that today)

CONCATENATEDEPENDENCIES

r.js will combine modules that are specified asdependencies in your module definitions

CONFIGURATION FILE{ "modules": [ { "name" : "bootstrap", "include" : [ "underscore", "backbone", "jquery" ] }, { "name" : "blogs", exclude: ['bootstrap'] }, { "name" : "shops", exclude: ['bootstrap'] }, { "name" : "profiles", exclude: ['bootstrap'] }, /* etc. */

THAT'S IT!QUESTIONS?

EXAMPLE REPOgithub.com/young-steveo/require_cake