compared with
Key
This line was removed.
This word was removed. This word was added.
This line was added.

Changes (11)

View Page History
{zone-data:revision}
0.1 - 13 January 2009: Initial Draft.
0.2 - 25 January 2009: More comprehensive overview; modified requirements; referenced laboratory prototype
{zone-data}

Creating custom builds is relatively trivial. However, at this time, it requires the developer to examine each page, aggregate the dojo.require and other statements, create a layer file, and create the build profile.

Zend_Dojo provides a common point of aggregation for all these areas, and could be repurposed trivially introspected to build a custom layer file and related build profile, which would leave creating the actual build as the only step necessary by the developer. This proposal aims to address this idea.

h3. How Dojo custom layers work
Rather than generate a large number of dojo.require statements for every page, a
common solution in Dojo is to create what is known as a layer script. A layer
script typically is namespace, and contains simply a number of dojo.require
statements:
{code:javascript}
dojo.provide("foo.main");

(function() {
dojo.require("dojo.parser");
dojo.require("dijit.layout.BorderContainer");
dojo.require("dijit.layout.ContentPane");
dojo.require("dijit.form.Button");
dojo.require("dijit.form.ValidationTextBox");
dojo.require("dijit.form.Form");
dojo.require("dijit.Dialog");
dojo.require("dojox.dtl.Context");
})();
{code}

These scripts _may_ also contain JavaScript to run on every request, such as dojo.addOnLoad events, definition of dojo.data stores, etc. Such an approach might look like this:

{code:javascript}
dojo.provide("foo.main");

(function(){
dojo.require("dojo.parser");
dojo.require("dijit.layout.BorderContainer");
dojo.require("dijit.layout.ContentPane");
dojo.require("dijit.form.Button");
dojo.require("dijit.form.ValidationTextBox");
dojo.require("dijit.form.Form");
dojo.require("dijit.Dialog");
dojo.require("dojox.dtl.Context");

if (!foo.disableLayout) {
dojo.addOnLoad(function(){
foo.layout = new dijit.layout.BorderContainer();
});
}
})();
{code}

Within your view markup, then you would simply dojo.require this single layer script:

{code:html}
<script language="javascript">
dojo.require("foo.main");
</script>
{code}

This approach has the benefit of making it easier to maintain your view scripts between environments, and prevents the necessity of constantly updating application code.

Dojo itself leverages this paradigm in its build system. The build system can use a layer script as a dependency, and compile all JavaScript into a single file, pulling in all dependencies recursively. Additionally, it can strip out comments, whitespace, and perform optimizations on the code.

Build profiles are simply JavaScript, and define the various build options, layers, and dependencies. An example for the above might look like the following:

{code:javascript}
dependencies = {
action: "release",
version: "1.3.0-foo",
releaseName: "foo",
loader: "default",
cssOptimize: "comments",
optimize: "shrinksafe",
layerOptimize: "shrinksafe",
copyTests: false,
layers: [
{
name: "../foo/main.js",
layerDependencies: [],
dependencies: [
"foo.main",
]
}
],
prefixes: [
[ "dijit", "../dijit" ],
[ "dojox", "../dojox" ],
[ "foo", "../foo" ]
]
};
{code}

Several things to note about it. First, note that the layer has a dependency on
itself. This allows you to reference the same module in your dojo.require both
before and after the build. Second, note that even though dojo.parser was
referenced in a dojo.require statement, there is no "dojo" prefix defined; the
dojo directory will always be included in the build.

You then pass this profile to the build script, typically located in
util/buildscripts/ of the dojo distribution. You need to provide the location of
the profile script, and a release directory. As an example:

{code}
./build.sh profileFile=/abs/path/to/build/profile.js releaseDir=path/to/releases/
{code}

The benefits to using custom builds are tremendous:

* Decreased load times for end users. Without a build, each dojo.require statement will produce an XmlHttpRequest round trip with the server, leading to often dozens of calls; with a build script, these are practically eliminated. Additionally, due to the build stripping out white space and comments and doing code optimizations, the final file size is a fraction of the cumulative file size.
* Faster JavaScript responses. The build process performs a number of code optimizations, resulting in snappier response times for JS operations.
* Optionally, you can also request build operations on CSS associated with your custom Dojo namespace. This will evaluate all @import statements and pull the CSS from those files into a single file, and strip comments and whitespace. The result is a single, smaller CSS file -- again, leading to decreased load times for end users.

But where does Zend Framework fit into this?

h3. Proposed Zend Framework integration

The dojo() view helper aggregates all the information needed by a build layer: the various dojo.require statements, dojo.addOnLoad events (if desired), and Dojo-related initialization javascript (if desired).

We propose a class that can take the metadata contained in the dojo() view helper in order to generate both a layer script and a build profile. This will simplify the deployment story for developers using Zend Framework and Dojo.

This class will also allow aggregating the results of visiting several pages with differing Dojo requirements, allowing developers to create comprehensive build layers for their site.

{zone-data}

{zone-data:references}
* [Dojo Build System documentation|http://dojotoolkit.org/book/dojo-book-0-4/part-6-customizing-dojo-builds-better-performance/dojo-build-system] documentation|http://www.dojotoolkit.org/book/dojo-book-0-9/part-4-meta-dojo/package-system-and-custom-builds]
* [My blog entry on layers|http://weierophinney.net/matthew/archives/188-Proper-Layer-files-when-using-Dojo-with-Zend-Framework.html]
* [Prototype code|http://framework.zend.com/svn/framework/laboratory/Zend_Dojo]
{zone-data}

{zone-data:requirements}
* This component *MUST* add the ability to create Dojo custom layer files:
** Layer files locations *COULD* be auto-discovered based on the path to dojo.js and the layer name
** If a CDN is being used, layer files *MUST NOT* be created
** The component *MUST* allow specifying the exact filesystem location to the file; if so, it *MUST* also require specifying the location relative to dojo.js
* This component *MUST* provide the ability to generate Dojo custom layer files.
** The component *MUST* allow specifying the layer name
** The component *MUST* allow specifying whether or not to include dojo.addOnLoad events and/or Dojo-related JavaScript aggregated in the helper
* The component *MUST* allow creating the build profile
** The generated layer file *MUST* allow specifying the layer script path (relative to dojo.js)
** The generated layer file *MUST* determine the dependent namespaces from the dojo.require statements
** Automated build layers *WILL* be loaded via a dojo.require() statement
** Any dojo.require statements aggregated by the helper *MUST* be omitted when the dojo() view helper is rendered
* The helper should check to see if a layer file already exists; if so:
** The helper *MUST* parse the existing layer file, and break it into discrete tokens (dojo.require statements and layer-related javascript)
** The component *MUST* allow specifying arbitrary build options
** The component *MUST* provide default recommended build options
* This component *SHOULD* allow parsing an existing layer file into tokens
** The helper *MUST* component *SHOULD* check to see if new dojo.require statements and/or javascript have been registered; if so, it *MUST* update the layer file. create a composite file
** The helper *MUST* have a flag indicating whether or not live updating of the layer file should occur.
* When an automated build layer is specified, the component *SHOULD* allow specifying whether all JS directives should be included in the layer file, or only the dojo.require statements
* The component *SHOULD* allow creating the build profile
** The component *SHOULD* allow specifying where to write the build profile
{zone-data}