Lazy versus eager component loading
Lazy versus eager component loading
If you’re implementing a very simple search page using only a fraction of the Coveo JavaScript Search Framework components, you will legitimately expect this page to load faster than a more complex one.
Since the July 2017 release (v2.2900.23) of the Coveo JavaScript Search Framework, there are two different ways to load components in a search page: lazy and eager component loading.
While lazy component loading is conceptually more complex than eager component loading, using the former way in a simple implementation can result in linear performance gains during the search page loading process. These performance gains are especially noticeable on less powerful machines, such as mobile devices.
Eager component loading
Eager component loading sequentially loads the code of the entire framework before the initialization process of the search page, regardless of whether the page needs the totality of this code or not. Before the July 2017 release (v2.2900.23), this was the only way to load components in the Coveo JavaScript Search Framework.
In the best-case scenario (that is, when a search page uses every component of the framework), this straightforward approach performs as well as lazy component loading. However, eager component loading can never perform better than lazy component loading.
To use eager component loading in your search page, reference the CoveoJsSearch script (not the CoveoJsSearch.Lazy script).
You can reference the script from your own server:
<head>
[ ... ]
<script src="js/CoveoJsSearch.js"></script>
[ ... ]
</head>
You can also reference the script from the release of your choice (from version 1.2537 on) through the Coveo CDN (see JavaScript Search Framework CDN links):
<head>
[ ... ]
<script src="https://static.cloud.coveo.com/searchui/v1.2537/js/CoveoJsSearch.min.js"></script>
[ ... ]
</head>
Lazy component loading
Lazy component loading makes your search page download only the code of the components it actually requires to work.
Moreover, lazy loading downloads the code of any given component only once.
Consequently, having many instances of a component (such as Facet) in a search page has no effect on its initial load time.
Being asynchronous in nature, lazy component loading adds a layer of complexity to a search page. However, it can also result in linear performance increases for simple search pages.
Suppose you implement a very basic search page such as this one:
<body id="search" class="CoveoSearchInterface" data-design="new">
<div class="coveo-search-section">
<div class="CoveoSearchbox"></div>
</div>
<div class="coveo-main-section">
<div class="coveo-results-column">
<div class="CoveoResultList">
<script id="mySimpleResultTemplate" class="result-template" type="text/underscore">
<div class="CoveoResultLink"></div>
</script>
</div>
</div>
</div>
</body>
Eager component loading would download the entire Coveo JavaScript Search Framework code (60+ components) before initialization, even though the search page clearly has no need for most of this code.
Lazy component loading would first scan the page for Coveo components.
It would then only download the code which the SearchInterface, Searchbox, ResultList, and ResultLink components require.
This could result in substantial performance improvements, especially for users with slow Internet connections, or less powerful machines, such as mobile devices.
To use lazy component loading in your search page, reference the CoveoJsSearch.Lazy script (not the CoveoJsSearch script).
You can reference the script from your own server:
<head>
[ ... ]
<script src="js/CoveoJsSearch.Lazy.js"></script>
[ ... ]
</head>
You can also reference the script from the release of your choice (from version 2.2900 on) through the Coveo CDN (see JavaScript Search Framework CDN links):
<head>
[ ... ]
<script src="https://static.cloud.coveo.com/searchui/v2.2900/js/CoveoJsSearch.Lazy.min.js"></script>
[ ... ]
</head>
Interacting with lazy components
The eager component loading initialization process is synchronous.
This means that once the Coveo.init call is made, you can write code that interacts with your search page component instances right away, since all components are necessarily instantiated by then.
When you use lazy component loading in your search page, however, the initialization process is asynchronous.
The page starts by downloading the necessary component code "chunks" as separate threads during the init call.
These threads might very well still be ongoing by the time the init call is through.
Consequently, you must wait for a component promise to resolve before interacting with an instance of this component.
Consider the following markup:
<body id="search" class="CoveoSearchInterface" data-design="new">
<div class="coveo-search-section">
<div class="CoveoSearchbox"></div>
</div>
<div class="coveo-main-section">
<div class="coveo-facet-column">
<div class="CoveoFacet" data-id="yearFacet" data-title="Year" data-field="@year"></div>
</div>
<div class="coveo-results-column">
<div class="CoveoResultList">
<script id="mySimpleResultTemplate" class="result-template" type="text/underscore">
<div class="CoveoResultLink"></div>
</script>
</div>
</div>
</div>
</body>
Suppose you only want to collapse the Facet instance whose data-id is yearFacet after the DOM content of the page finishes loading.
If you’re using eager component loading in your search page, you could do this right after the init call:
document.addEventListener('DOMContentLoaded, function() {
[ ... ]
Coveo.init("#search");
// This works consistently with eager component loading.
Coveo.get(Coveo.$$(document).find(".CoveoFacet[data-id='yearFacet']")).collapse();
[ ... ]
)};
However, if you’re using lazy component loading in your search page, doing so will likely produce an error message such as:
Uncaught TypeError: Cannot read property 'collapse' of undefined
The explanation is simple: since the lazy loading initialization process is asynchronous, the Facet component might not yet be instantiated when you call its collapse method right after the init call.
Therefore, you have to wait for the component promise to resolve to make it work.
The Coveo.load top-level function allows you to conveniently do so:
document.addEventListener('DOMContentLoaded, function() {
[ ... ]
// This works consistently with lazy component loading.
Coveo.load("Facet").then(function(Facet) {
Coveo.get(Coveo.$$(document).find(".CoveoFacet[data-id='yearFacet']")).collapse();
});
[ ... ]
)};
Registering a lazy component
To work on a custom version of an existing component, make sure the lazy component loading process recognizes it.
When you extend an existing component, make sure the necessary code is available first. This means you have to wait for the "parent" component promise to resolve before you can register a "child" component implementation.
To do so, you should use the Coveo.LazyInitialization.registerLazyComponent method.
This method takes two arguments: the ID of the component you want to register, and a function that returns the component implementation when its prerequisite promise resolves.
Suppose you want to register a custom Searchbox as a lazy component.
You could do so in TypeScript:
export interface ICustomSearchboxOptions extends ISearchboxOptions {
title? : string;
size? : number;
isCollapsed? : boolean
};
export function lazyCustomSearchbox() {
// Wait for the 'Searchbox' promise to resolve, then return the 'CustomSearchbox' implementation.
return Coveo.load<IComponentDefinition>('Searchbox').then((Searchbox) => {
class CustomSearchbox extends Searchbox {
static ID = 'CustomSearchbox';
static options ICustomSearchboxOptions = _.extend({}, {
title : ComponentOptions.buildStringOption(),
size : ComponentOptions.buildNumberOption({ min : 0 }),
isCollapsed : ComponentOptions.buildBooleanOption({ defaultValue : false })
};
constructor(public element: HTMLElement, public options?: ICustomComponentOptions, bindings?: IComponentBindings) {
super(element, ComponentOptions.initComponentOptions(element, CustomSearchbox, options), bindings);
};
};
Coveo.Initialization.registerAutoCreateComponent(CustomSearchbox);
return CustomSearchbox;
});
};
// Register the 'CustomSearchbox' lazy component using the previously defined function.
Coveo.LazyInitialization.registerLazyComponent('CustomSearchbox', lazyCustomSearchbox);
Or, you could achieve the same result in JavaScript:
function lazyCustomSearchbox() {
// Wait for the 'Searchbox' promise to resolve, then return the 'CustomSearchbox' implementation.
return Coveo.load('Searchbox').then(function(Searchbox) {
var CustomSearchbox = (function(_super) {
__extends(CustomSearchbox, _super);
function CustomSearchbox(element, options, bindings) {
_super.call(this, element, ComponentOptions.initComponentOptions(element, CustomSearchbox, options), bindings);
this.element = element;
this.options = options;
this.bindings = bindings;
}
CustomSearchbox.ID = 'CustomSearchbox';
CustomSearchbox.options = _extend({}, {
title : ComponentOptions.buildStringOption(),
size : ComponentOptions.buildNumberOption({ min : 0 }),
isCollapsed : ComponentOptions.buildBooleanOption({ defaultValue : false })
}, Coveo.Searchbox.options);
return CustomSearchbox;
}(Searchbox));
Coveo.Initialization.registerAutoCreateComponent(CustomSearchbox);
return CustomSearchbox;
});
}
// Register the 'CustomSearchbox' lazy component using the previously defined function.
Coveo.LazyInitialization.registerLazyComponent('CustomSearchbox', lazyCustomSearchbox);
Fixing code chunks loading path issues
In some setups, the framework can’t automatically detect the path from which to load the code. When this issue occurs, the following error appears in the browser console:
Cannot load chunk for [ ...
] You may need to add the coveo-script class on the script tag that includes the Coveo framework.
As stated in the error message, add the CSS class coveo-script on the HTML script element that includes the Coveo framework.
<script class="coveo-script" src="js/CoveoJsSearch.Lazy.js"></script>
You must do this if the script tag that includes the Coveo script is added dynamically to your page and to support IE11. This error can also occur when using IE11 and when any script in the page is added dynamically.
|
|
Note
In older versions, you will get this error message instead:
As stated in the error message, you should use the Example
Suppose your search page has the following directory tree:
Depending on the type of path you want to use:
|