We will reuse the animation sample, from the local OpenLayers installation, to try out both ways of building code:
animation.html and animation.js files from the examples folder into animation-exports.html and animation-exports.js.config/ol-animation-exports.json inspired by the config/examples-all.json with the following content:{
"exports": ["ol.Map",
"ol.Map#*",
"ol.View",
"ol.animation.*",
"ol.control.*",
"ol.layer.Tile",
"ol.proj.*",
"ol.source.OSM"
],
"src": ["src/**/*.js"],
"compile": {
"externs": [
"externs/bingmaps.js",
"externs/bootstrap.js",
"externs/closure-compiler.js",
"externs/example.js",
"externs/geojson.js",
"externs/jquery-1.7.js",
"externs/oli.js",
"externs/olx.js",
"externs/proj4js.js",
"externs/tilejson.js",
"externs/topojson.js",
"externs/vbarray.js"
],
"define": [
"goog.dom.ASSUME_STANDARDS_MODE=true",
"goog.DEBUG=false"
],
"jscomp_error": [
"accessControls",
"ambiguousFunctionDecl",
"checkEventfulObjectDisposal",
"checkRegExp",
"checkStructDictInheritance",
"checkTypes",
"checkVars",
"const",
"constantProperty",
"deprecated",
"duplicateMessage",
"es3",
"externsValidation",
"fileoverviewTags",
"globalThis",
"internetExplorerChecks",
"invalidCasts",
"misplacedTypeAnnotation",
"missingGetCssName",
"missingProperties",
"missingProvide",
"missingRequire",
"missingReturn",
"newCheckTypes",
"nonStandardJsDocs",
"suspiciousCode",
"strictModuleDepCheck",
"typeInvalidation",
"undefinedNames",
"undefinedVars",
"unknownDefines",
"uselessCode",
"visibility"
],
"extra_annotation_name": [
"api", "observable"
],
"jscomp_off": [
"es5Strict"
],
"compilation_level": "ADVANCED",
"output_wrapper": "// OpenLayers 3. See http://ol3.js.org/\n(function(){%output%})();",
"use_types_for_optimization": true
}
}animation-exports.js file, remove the goog.require statements.animation-exports.html, change the script src attribute from loader.js?id=animation to ../build/ol-animation-exports.js and add a new script reference with <script src="animation-exports.js"></script>.ol-animation-exports.js file from the root ol3 folder using the following command:
node tasks/build.js config/ol-animation-exports.json build/ol-animation-exports.js
./build.py serve or build.cmd serve and open your browser at http://localhost:3000/examples/animation-exports.html.animation.html and animation.js from the examples folder into animation-combined.html and animation-combined.js.<script src="jquery.min.js"></script>
<script src="../build/ol-animation-combined.js"></script>config/ol-animation-exports.json into config/ol-animation-combined.json.[] and add at the end of the array, contained in the src value, the .js files declaration to get a result like the following:"src": [
"src/**/*.js",
"examples/animation-combined.js"
]examples/animation-combined.js file the code from resources/example-behaviour.js.externs/example.js, from the config/ol-animation-combined.json file.
node tasks/build.js config/ol-animation-combined.json build/ol-animation-combined.js
http://localhost:3000/examples/animation-exports.htmlUntil now, to facilitate learning, we used code for samples within the HTML. For compressing code, firstly, you need to have all your JavaScript code in a separate .js file.
In the first case, we used exports in the ADVANCED mode. At the functional level, exporting means that you want to stop obfuscating variables, for example, renaming variables and properties to shorter ones. The advantage of this, is that you can use code from outside the library, in a third-party JavaScript file. It can help you, for example, in designing a subset of the OpenLayers 3 library for specific use cases or making your own library based on the OpenLayers 3 and a custom code augmenting the default.
At the code level, to know what we want to export, we reuse the content from goog.require statements in the example. These statements are deduced from the code namespaces and constructors . If you limit yourself to these statements to declare exports, when compiling and executing code, you will get some errors stating undefined in the browser debugger console. Mostly, it means you protect the constructor but you didn't choose some functions to protect. To protect from variable renaming, you will need to use the * character. It's what we have done with ol.Map#* or ol.proj.*.
In the ol.Map#* case, we said that we wanted to protect all functions from ol.Map.prototype to be renamed by using the # character. In the ol.proj.* use case, we just protected the all ol.transform namespace.
For externs, we will not go in to the details of how they work, but you just have to understand that it's a way to protect code from other libraries to be renamed by Closure Compiler by declaring their variables' and functions' signatures.
If we review the other parameters in the first JSON file, the most important ones are:
src: This helps define where your compiler has to search for code when managing dependencies. Those dependencies are declared by declarations in code.compilation_level: This can be set to ADVANCED, WHITESPACE_ONLY, or SIMPLE. We chose to look at the ADVANCED case, because once we have understood how to work with the advanced option, the others can be worked on easily.use_types_for_optimization. In fact, it highlights the importance of comments also known as annotations. We will start by quoting the official Closure Compiler documentation:"The Closure Compiler can use data type information about JavaScript variables to provide enhanced optimization and warnings. JavaScript, however, has no way to declare types. Because JavaScript has no syntax for declaring the type of a variable, you must use comments in the code to specify the data type."
From this, we can deduce that you can use Closure Compiler without always using comments but comments can help you catch errors using variables type checking based on comments (based on a standard called JSDoc).
It's also useful, because when Closure compiler uses the ADVANCED mode, the compiler renames variables to decrease the build size and annotations act as hints for the tool. Moreover, the commenting code is anyways a good practice to maintain code: do not hesitate to use them. Navigate to the official Closure Compiler documentation (https://developers.google.com/closure/compiler/docs/js-for-compiler) to see the exact grammar.
Another good tip related to annotations is the fact that adding an annotation @api (specific to OpenLayers) is the way to export a function when you use OpenLayers 3 default build system.
For instance, you can add @api to ol.Ellipsoid and ol.Ellipsoid.prototype.vincentyDistance in the file src/ol/ellipsoid/ellipsoid.js (for reference, see src/ol/map.js line 160). Then, launch node tasks/build.js config/ol.json build/ol.js and reuse the generated ol.js instead of the usual one. You will see that you can use, in your application code, the following code:
var ellipsoid_wgs84 = new ol.Ellipsoid(6378137, 1/298.257223563); var distance_ol3_vincenty = ellipsoid_wgs84.vincentyDistance([5, 34], [12, 56])
We will not inspect all the other parameters, because it's mostly not required to understand their meanings. If you would like to read further about them, we recommend that you go to the API and to the readme file about tasks that document most parameters at https://github.com/openlayers/ol3/blob/v3.0.0/tasks/readme.md.
If we dive into the case where we build everything together, the essential part is the inclusion of the build/ol-animation-combined.js file in the src array parameter.
As we removed the script tag calling resources/example-behaviour.js from animation-combined.html, we had to combine resources/example-behaviour.js with examples/animation-combined.js files. It's because adding a file into src array is not enough. You need goog.require into the file to be combined with the main OpenLayers 3 library code.
With the inclusion of resources/example-behaviour.js, the other important part was to remove the externs/example.js line, in order to unprotect the exampleNS namespace, so that we can rename it.
You should also note that in both reviewed case, compression can be achieved because of already mentioned goog.require and goog.provide. At the code level, they allow you to create a dependencies tree to gather each required function and variable within OpenLayers core library code source files.
If you inspect the size of the resulting files, you will see that although we have more content in the ol-animation-combined.js file than ol-animation-exports.js, the first one weighs 190Ko (65Ko manually gzip compressed), whereas the second size is 212Ko (71Ko manually gzip compressed).
The conclusion is that compiling everything together is the best solution to gain size in your code.
Now, try to reuse this knowledge in your own OpenLayers samples build.
Now that you have all the basic knowledge about OpenLayers toolkit to compress code, try and apply it your own project.
To improve your skills, you can perform the same task we did in the previous Time for action section, but using a different example. You can use the JSON files from the build/examples because they may help you to solve building dependencies. Be careful, if you rely only on those files, it will not work the expected way if you use the export method or you change JavaScript calls in samples HTML files.
To try it out.
You have to change the paths in samples, and then test them by interacting with your samples; it may work when they are loading, but they will fail later when clicking and panning.
Remember we told you that without externs, you can't use external libraries in the ADVANCED mode? We recommend you comment out or remove externs, to see what will happen if we don't use them. In particular, try to remove jQuery externs, and try to build the examples from Time for action.
What do you see? If you don't have any ideas, you can see that externs' keywords refers to some JavaScript files. If you need, for example, to use a library such as Underscore (another JavaScript Library to use more functional programming), just copy the externs, add your path to the JSON file, say, "../externs/underscore.js", and after this, you are ready to use your library in the Closure Compiler ADVANCED mode.
To find out about externs, go to this Closure Compiler web page: https://github.com/google/closure-compiler/wiki/Externs-For-Common-Libraries
If you require another less common library, find it using a search engine or by generating it with this tool: http://blog.dotnetwise.com/2009/11/closure-compiler-externs-extractor.html
In the worst case, you will be stuck and will only be able to use the SIMPLE mode or to use the exports method, building a minimum Openlayers core library.
Q1. When using the OpenLayers 3 toolkit, your code is not working in the ADVANCED compilation mode, although it's perfectly fine in the SIMPLE mode, what can be the cause (multiple choices accepted)?
goog.requiregoog.inheritsgoog functions in your custom codeNow, we have seen mainly an underlying way to compress the JavaScript code; we will introduce you to syntax and styles in JavaScript code. When you start to write more than ten lines of code, it starts to become an obligation!