Skip to content

Instantly share code, notes, and snippets.

@davglass
Created June 30, 2011 15:37
Show Gist options
  • Save davglass/1056494 to your computer and use it in GitHub Desktop.
Save davglass/1056494 to your computer and use it in GitHub Desktop.
Loader Article

YUI and Loader changes for 3.4.0

In 3.4.0 we started the process of shifting some of Loader's logic around, to not only make it more performant, but to make it more robust and easier to use in other places (like on the server). We will be rolling out more changes in future revisions, but I wanted to take some time and explain what was changed, why it was changed and how it may impact developers. For the majority of use-cases, developers will notice nothing different, except that things are a little faster and their requirement downloads are a little smaller.

Seed File

The first thing I want to address is the YUI seed file. In previous versions of YUI, our seed file was very tiny and did not contain Loader or any of its meta-data. We've found that in the 90% use-case this was not as performant as we had hoped. The normal user includes the seed file then requests their modules, which in turn means that the seed needs to first fetch Loader, then calculate all of its dependencies, then fetch them all. We now feel that this extra http request is the wrong thing to do, so the new standard seed file contains Loader and its meta-data. Yes, this will make the initial request a little larger, but it will make the loading of modules that much faster since all of its meta-data requirements are now already on the page.

If you wish to use it the old way, you can just include the yui-base seed file instead. It contains everything that is needed to make YUI run in stand-alone mode plus it contains the ability to fetch Loader on demand. If you require even finer-grained dependencies, we have created a yui-core seed file that is exactly what the old yui-base seed was.

/build/yui/yui-min.js //YUI Seed + Loader
/build/yui-base/yui-base-min.js //Old YUI Seed with Loader fetch support
/build/yui-core/yui-core-min.js //Old yui-base without Loader fetch support

The other reasoning for this change is our roadmap for making YUI run in as many places as possible. The old seed file plus Loader in a single combo server request is all well and good if you have a combo server available in your application. But what about on the server? Or in an offline app on a mobile device? These places need to minimize file access while still getting the information they need.

Rollups

The next thing that we changed was removing rollups from the system and defaulting allowRollup to false in the Loader config. What does this mean to you? Well, hopefully nothing at all. Before I explain the impact of the change, let me explain the reasoning behind it. The primary reason is, again, performance, along with payload delivery. Take this example:

Module A: requires event-a, event-b Module B: requires event-c, event-d

When you request both, the rollup logic prior to 3.4.0 used to determine that you should get the event rollup. Which actually meant that you were getting:

event.a, event.b, event.c, event.d, event.e, event.f, event.g, event.h

You ended up with more on your page than you actually needed. By turning off the rollup support, YUI will now ask for only what you actually requested and nothing more. In most cases, you will not notice this. Module developers, may run into a situation where things that worked in the past may not work now. The reason for this is that they actually worked by accident before. Let me use a real world example: Dial.

In 3.3.0, Dial required this:

requires: [
    'widget',
    'dd-drag',
    'substitute',
    'event-mouseenter', 
    'transition',
    'intl'
]

For the most part, Dial worked in 3.4.0, however keyboard support did not work. After doing some simple investigating, it turned out that the rollup support was actually requesting the entire Event rollup (which includes event-move and event-key). Without the rollup logic pulling in all of event, 3.4.0 Dial no longer had all of its requirements. Making Dial's requirements more specific and defining all of its actual dependencies properly makes it work as expected.

requires: [
    'widget',
    'dd-drag',
    'substitute',
    'event-mouseenter',
    'event-move',
    'event-key',
    'transition',
    'intl'
]

For module developers, it is a best practice to make sure that your module requires exactly what it needs to function. Do not assume that an upstream module requirement is there. It's always better to make sure that you ask for what you need.

This also means that module requirements are more well defined. For example, datatype-date has Intl support built in. In previous versions you would access the Intl like this:

Y.Intl.getAvailableLangs('datatype-date');

But since this module doesn't actually have a language (the datatype-date-format module does), this will fail. It needs to be more specific and actually ask for languages for the correct module:

Y.Intl.getAvailableLangs('datatype-date-format');

Build File Explosion and Submodule Removal

After making this change, the next change we made was exploding the build directory and removing submodules from the core system. Submodule logic was not removed, only our meta-data structure was changed. This will provide backward compatibility for current installations.

Submodules in the core system caused a couple of issues that we needed to resolve. The first reason was performance. Each time Loader needed to calculate dependencies, it needed to walk the submodule/plugin structure of each module. Doing this thousands of times was hurting our performance during the Loader calculate routine. By removing support for submodules in the core system we saved tens of thousands of function calls and iterations.

Loader was changed so that if a use property in a module's meta-data defined more modules, it will use those modules instead of trying to load the original module. So, if you requested "dd" Loader would inspect "dd"'s meta-data and see a use property that looks something like this:

"dd-ddm-base,dd-ddm,dd-ddm-drop,dd-drag,dd-proxy,dd-constrain,dd-drop,dd-scroll,dd-drop-plugin"

In the core YUI seed file, we are also including what we are calling virtual rollups or aliases. These module definitions are exactly the same as the meta-data in Loader. This way you can include all the files exported from our Dependency Configurator and still use these rollups without having Loader present on the page. In future releases, we will be refining this approach even more.

After making this change, we then preceeded to explode our build files. In previous releases, the submodules determined the modules file path. For example:

"dd": {
    "submodules": {
        "dd-drag": 
        // Module data
    }
}

In 3.3.0 when you built "dd", the file structure looked something like this:

/build/dd/dd-drag.js
/build/dd/dd-ddm.js
/build/dd/dd-drop.js

With the build system exploded in 3.4.0, "dd"'s build files now look like this:

/build/dd-drag/dd-drag.js
/build/dd-ddm/dd-ddm.js
/build/dd-drop/dd-drop.js

This allowed us to remove the "path" property from all of our module meta-data as well, saving file size and reducing the logic required to assemble the modules url paths.

If you are including a pre-configured combo URL, you must recalculate your URL when you upgrade

The downside to this change is that if you are including a combo URL of modules to "prep" your page you can not just change the version number and upgrade. You will need to revisit the Dependency Configurator and generate a new URL with new module structure.

The Future

I will be continuing to refine, refactor and maximize every aspect of our Loader and Seed strategy. These first steps were needed to aid in future changes that need to be made for not only our client-side strategy but also our server, command-line and mobile device strategies as well.

@ericf
Copy link

ericf commented Jun 30, 2011

Overall, I think this document describes the changes very well, and I like that you included the reasons and impact on developers. Here are my thoughts on the specific areas:

Seed File Changes

“We now feel that this extra http request is the wrong thing to do, so the new standard seed file contains Loader and its meta-data.”

I thought by default YLS/RLS was going to be used, therefore not requiring the module meta-data client-side? Is this not happening until 3.5.0? I do like the three seed files that are there now, as it gives more flexibility, but I think it will be important to promote the most common usage, and have the other options tucked-away under “Advanced Usage”.

Rollup Changes

“You ended up with more on your page than you actually needed. By turning off the rollup support, YUI will now ask for only what you actually requested and nothing more.”

I really like this change, we’ve turned off allowRollup for TipTheWeb a long time ago :) This also prevents weird bugs that crop up, where in some application code you’re using some code that’s only working because a Loader decided to include a roll-up module; then you change your dependencies and all of a sudden Loader doesn’t use the roll-up anymore, and your code breaks. <-- This has happened to me on a number of occasions.

“Without the rollup logic pulling in all of event, 3.4.0 Dial no longer had all of its requirements. Making Dial's requirements more specific and defining all of its actual dependencies properly makes it work as expected.”

Exactly! And I think module developers should be encouraged to make fine-grain choices about their dependencies. That said, Dependency Configurator is extremely important to make this process sane!

Build and Submodule Changes

“By removing support for submodules in the core system we saved tens of thousands of function calls and iterations.”

Awesome!

“In the core YUI seed file, we are also including what we are calling virtual rollups or aliases.”

I’m guessing there is a bunch of tests for this? It seems important for backwards compatibility for this stuff.

“This allowed us to remove the "path" property from all of our module meta-data as well, saving file size and reducing the logic required to assemble the modules url paths.”

Ah, this is a smart move! Yet another reason to not have physical roll-ups.

@davglass
Copy link
Author

@ericf - YLS/RLS was removed from this article because it's not a certainty yet. This was more about the changes that are in there and their impact on the developers. YLS support in general will require an article all on it's own ;)

On a side note tho, rls will use their own seed that contains the rls module and removes the loader infrastructure. This seed will have RLS/YLS on and configured to use the final public server by default. Once RLS/YLS is the default, we will likely make yui.js be the RLS seed, then create a yui-loader seed file to use the older loader based seed.

Does that make sense?

@davglass
Copy link
Author

@ericf As for the tests, Loader and the module system now (~3 months of work) has 1000+ tests to test all the various ways to load a module: local single file serving, combo file serving, loader-fetch, then combo, use star (with Loader on the server to provide the content) and rls against the dev RLS server.

@ericf
Copy link

ericf commented Jun 30, 2011

@davglass Yeah that makes sense. And I like the look of the changes that are happening now, for 3.4.0.

That's good to hear about the tests, and I'll be sure to give these changes thorough testing with our code-base for TipTheWeb.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment