Using RequireJS with CakePHP
-
Upload
stephen-young -
Category
Technology
-
view
3.717 -
download
2
description
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