When an Ember app boots up, it primarily waits for the DOM to be ready before it can boot your application. Though this is a safe check needed in order to have your custom event handlers attach correctly to the DOM, it may not be always necessary. In Ember all event handling is attached to the rootElement
by default.
Waiting for the DOM ready may not be always necessary and would be a disadvantage in certain scenarios. For example, the app's index.html could also contain the API data needed by that route in a pre-fetched fashion. The index.html could be served in chunks (ie as the data is available we would be sending it to the client). When the client app boots in the browser, it can use this pre-fetched data to serve the route. When serving the base page in chunks (using Transfer-Encoding protocol), the browser fires the DOM ready only after the last chunk is sent down. This could cause the DOM ready to be delayed and the Ember app cannot do anything until then.
We would like the Ember app to boot up safely without waiting for DOM ready in order to parallelize the application creation phase. This helps us serve the initial page load of an Ember app much quicker. The application can fetch the models till the DOM ready event is fired.
During application creation, Ember builds the registry where it injects the view as well. With parallelizing the boot phase, we can inject the template into the view soon without waiting for DOM ready as well. The default event handling created by Ember would also work in this scenario since they all are attached to the rootElement
.
We believe, creating the registry and then immediately booting without waiting for DOM to be ready saves on an average 40-60 ms of ember creation phase and the views are rendered much faster. The numbers would be even more if DOM ready is fired very late. We carried out some experiments by sending the index.html in parts.
In order to test if the pre boot really helps, we created a basic Ember application that fetches data and uses the model fetched to serve the route (view). To pre-boot the application, we overwrote some of the Ember's internal API during the application creation. The app.js which is responsible for exporting your application was changed to as follows:
import Ember from 'ember';
import loadInitializers from 'ember/load-initializers';
import Resolver from 'ember/resolver';
Ember.MODEL_FACTORY_INJECTIONS = true;
let App = Ember.Application.extend({
isBooted: false,
init() {
// build the registry and other init tasks of Ember.Application
this._super(...arguments);
// boot the application immediately
this.boot();
},
boot() {
if (!this.isBooted) {
// boot the application only once
this.isBooted = true;
this._super(...arguments);
}
},
modulePrefix: config.modulePrefix,
podModulePrefix: config.podModulePrefix,
Resolver: Resolver
});
loadInitializers(App, config.modulePrefix);
export default App;
The above code snippet causes the Ember application to boot immediately without waiting for DOM ready. Moreover, we need to guard against not booting the application twice (once by booting immediately in init() and other when DOM is ready). Therefore, the above does not boot the application when the DOM becomes ready.
The above code snippet allows us to render components and views faster as the model for them is available without waiting for the DOM to be ready. The event handlers by Ember work as well since the listeners are attached to the rootElement.
We have seen this to give us better rendering times when the index.html is served in chunks and the last chunk comes in considerably late.
While the above works neatly, there could be cases where this could cause application's custom event handling to be broken. If the Ember application adds event listeners to document.body
(assuming the body element to be available), the event listener will not attached. We need to guard such listeners and attach the listeners when the DOM is ready.
Updated to latest ember style.