Easy Web Development with WaveMaker
上QQ阅读APP看书,第一时间看更新

Going faster

Most often, any definition of a successful application includes being "fast enough". What is and is not "fast enough?" Well, it depends. What we can easily say is that a slow application is no fun to use. Users will complain or stop using the application outright if performance is too poor. Building a well-performing application does not "just happen"; it requires planning. Let's look at some of the key design factors of performance.

Using page containers effectively

As we have seen, the widgets file of a page defines all of the components of a page in the JSON format. The higher the number of widgets and components on a page, the more JSON code needs to be parsed before the page can be rendered. Therefore, the higher the number of components on a page, the longer it takes to load. Reducing page load time is one of the most significant performance knobs available to developers.

Page containers have a deferLoad property, as shown in the following screenshot:

When enabled, which is the default state, the page container does not automatically load any page. Instead, it remains empty until the page is needed or explicitly loaded. Deferring the loading of widgets after application initialization is a powerful technique for reducing application load times.

Let's look at the WaveyWeb application from the last chapter. In layer2 of tabLayers1, just below labelMouseOver, we have our first page container named pageContainer1. The page name property is set to PageRichText. Run the application.

After the application has loaded, open the Network tab of the Chrome Developer Tools and clear the previous requests using the Clear button. The Clear button is the circle with the angled line through it, as shown in the following screenshot:

Optionally, use the XHR filter button to filter out only XHR requests if you like. This makes service calls easier to see. Now click on the tab labeled Connect. Notice how PageRichText.a.js is fetched upon the showing of the tab layer containing the page container:

If you reload the application with the Network tab open, you'll see that PageRichText is not loaded with the application. We could force the page container to load a page before being shown, for example, by calling loadPage(). This is easy to demonstrate in the Developer Tools console by calling main.pageContainer1.loadPage("PageRichText") before the page loads, for example, by opening the layer containing the page container:

This instructs the page container to load PageRichText. Now, when we show layer2, PageRichText is already loaded and there is no fetch. Notice that once PageRichText is loaded, like main, it gets loaded in the memory and not re-fetched. Clicking through all three layers does not result in further loading of PageRichText. The browser already has PageRichText (and main) loaded and does not need to re-fetch these pages.

Using page containers with deferLoad is a good way to reduce the amount of work that the browser needs to do before the application is ready for the user. Using cut and paste, entire panels of widgets can be moved across pages easily. However, WaveMaker 6.5 Studio does not support multiple widget selection. You can select an entire panel, but not multiple individual widgets. You can move many widgets by copying a panel, but non-visual components and code must be processed individually. Furthermore, source code must be manually moved. The lesson here is that while you can break up a page into multiple pages afterwards, it is convenient to use multiple pages from the beginning.

Pages have no performance impact until they are loaded. Once loaded, however, they consume browser memory. Therefore, we need to consider how many pages the application shows at the same time.

How many page containers?

An effective application could utilize a single-page container. Consider a main page with headers, footers, menus, and so on, and a single-page container for the body. Such a layout could have two pages loaded at any time; main, and another page in the page container. Each navigation event changes the page loaded in the page container. This is an efficient design that keeps the memory footprint down. When a page is replaced by a new page, all references are removed enabling the replaced page's components to be garbage collected.

Another common pattern is to have a layer of page containers, such as we have done in the WaveyWeb application. This enables the application to have multiple pages loaded at the same time at the price of increased memory consumption. It also enables commonly used pages to remain loaded, generally improving the overall user experience.

Such a layout enables direct communication between pages, reducing the need for global variables. Components with application as their owner are available globally across all pages of the application and are called global variables as a result. These application-owned components are not unloaded until the application is destroyed. This means they are always in memory and always available. This can be a convenient means to share data across pages, but at the cost of consuming more memory.

Note

When directly sharing data across pages, it is important to verify that the page your code calls into exists. Check the value returned from the other page before using it, or otherwise prevent the user from getting into the situation outright. For example, the following if block checks that the variable data has a value before using it:

if(data && data.length > 0 ){
}

Once again, which is best depends on the applications' goals. For an application that is being run on older hardware with little memory and less-than-ideal browsers, every bit of memory usage is costly. Older versions of Internet Explorer are infamous for their performance degradation as memory utilization increases. For an application targeting such platforms, the single-page container with many small pages is a good choice. At the same time, having too many pages loaded simultaneously can be overbearing to all but the most powerful systems. For many, a balanced approach will be the best. Using a few pages loaded simultaneously often reduces the complexity needed to manage the state. This will generally consist of one or two common pages being always loaded with one or two page containers for worker pages to perform specific tasks.

Don't forget that it is possible to have page containers within page containers. Within a page loaded in a page container, such as on the main page, we can repeat the pattern of a single-page container or a layer of page containers. Be careful not to overload the browser by nesting too many pages when using this pattern.

Reducing module loading

Deferring the loading of content via page containers improves load times by reducing the number of components that need to be created and rendered before the application is ready for the user. However, even the simplest application requires time to load and parse the WaveMaker runtime and component classes. The libraries containing component classes must be loaded before the page can create instances of those components.

As we know, in the debug mode, each module file is loaded independently as needed. By contrast, in the gzip mode, these modules are combined into minified compressed files. These modules are divided into groups. These groupings have been carefully selected to provide good loading for most situations. With a little bit of knowledge and planning, we can use these groupings to gain additional loading advantages.

Let's add a rich-text editor to PageRichText of our WaveyWeb application and repeat our network observation. Rich-text editors are heavyweight components. When we go to the Connect layer, causing PageRichText to load, we see two additional network requests: a 51.8 KB gzip build file and a 3.0 KB NLS file. Because the page contains a rich-text editor, wm_richTextEditor.js is loaded immediately after PageRichText.a.js. In parsing the components of PageRichText, the page loader finds that the page needs the wm.RichText class. Most applications use editors, but only some applications use heavy rich-text editors. As such, the rich-text editor is in its own build file, thus saving applications that don't use the rich-text editor from ever having to load and parse that 51.8 KB file. The NLS file contains localizations for the labels such as cut, copy, and paste, and is specific to the user's locale. Therefore, the NLS files are not included in the component package.

There are 24 gzipped files in the lib\build\Gzipped folder. The names will give you a good idea about the modules they contain. The Wm_editors.js.gz file contains all editors except the basic text editor and the rich-text editor. It also contains the Dojo grid. A direct list of component classes and their module can also be seen by entering wm.componentList on the console. This returns an array of classes and the build modules they require. By being conscious of these loadings and organizing your pages accordingly, you can reduce or defer the amount of downloading needed to use the application.

It is possible to create your own custom build files. This can be tricky and is beyond the scope of this book. For users wishing to create their own minified, compressed files, the product documentation can be found at http://dev.wavemaker.com/wiki/bin/wmdoc_6.5/Performance#HTheJavascriptBuildSystem.