This article is part of a series of tutorials that show you how to develop your first application:
Goal
In this tutorial, we show you how to deploy the Hello World application. We use webpack to bundle the JavaScript and HTML files of our application.
Once you have set up the LuciadRIA license loading as explained in the Hello World tutorial, you can use LuciadRIA just as you would use any other third-party library.
Creating a webpack production bundle
In the package.json
file within our hello-world
project, we add an NPM script to create a production build:
{ "scripts": { "build": "webpack --mode production" } }
Now, we can run the build script:
npm run build
webpack
generates a production version of our application in the dist
folder.
The dist
folder now holds an index.html
and a main.js
file.
dist/main.js
is different from src/main.js
, though.
The dist/main.js
bundle is a lot larger because it has all JavaScript modules needed to run your application.
That includes imported modules from your application’s dependencies, like LuciadRIA.
When you run webpack
in production mode, it applies extra optimizations to the bundle, reducing the size of the bundle.
Deploying the application
To deploy the application, just copy the dist
folder to your favorite web server for static files, Apache or NGINX for example.
For demonstration purposes, we start a simple NodeJS server that serves the dist
directory:
npx serve ./dist -l 8000
Since we used the |
When we browse to http://localhost:8000, we see our Hello World application. We successfully deployed a production build of our application.
Using your deployment license in production
You must use your deployment license in production mode, but you probably still want to use your development license for development.
One of the ways to set this up is by defining a webpack module alias for the license file. In production mode, this alias resolves to your deployment license file. In development, it resolves to the development license file.
In the Hello World application, open the webpack.config.js
file, and find these settings:
const path = require('path'); module.exports = { devServer: { static: path.join(__dirname, 'dist') }, };
Let’s switch the export configuration so that we pass in the command line options through the argv
argument.
That allows us to check if we’re running in production mode or not, and set up our license alias accordingly.
const path = require('path'); module.exports = (env, argv) => { const isProduction = argv.mode === "production"; const riaDeploymentLicense = path.resolve(__dirname, "src/luciadria_deployment.txt"); const riaDevelopmentLicense = path.resolve(__dirname, "src/luciadria_development.txt"); return { devServer: { static: path.join(__dirname, 'dist') }, resolve: { alias: { "ria-license": isProduction ? riaDeploymentLicense : riaDevelopmentLicense } } } };
Next, update the license import in src/license-loader.js
:
import {setLicenseText} from "@luciad/ria/util/License.js"; import license from 'raw-loader!ria-license'; // alias for actual license, see webpack.config.js' resolve.alias setLicenseText(license);
Now we use the deployment license file in production mode. We still use the development license file during development.
Trading off build times and bundle sizes
In this section we take a look at how we can trade off build time against bundle size, and how our choice of rendering technology affects bundle size.
The configuration updates in this section are optional. The webpack configuration of the earlier sections will serve you just fine, because it gives you the most optimal bundle size, but it comes at the cost of longer build times. This section only gives you some insight in how to reduce build time and bundle sizes. |
Production builds can take some time to run.
This is especially true when you are using WebGLMap
, because in comparison it pulls in more code than the non-WebGL Map
.
Note that each LuciadRIA module is already optimized, so re-optimizing those modules will result in only a minor reduction
in bundle size.
To speed up our production build, we can set up webpack to skip all, or some, of the optimization of LuciadRIA modules.
Because webpack optimization only works at the level of chunks, we’ll put each LuciadRIA module in its own chunk, and tell webpack to skip the optimization of that chunk:
const path = require('path'); const TerserPlugin = require('terser-webpack-plugin'); module.exports = (env, argv) => { const isProduction = argv.mode === "production"; const riaDeploymentLicense = path.resolve(__dirname, "src/luciadria_deployment.txt"); const riaDevelopmentLicense = path.resolve(__dirname, "src/luciadria_development.txt"); return { devServer: { static: path.join(__dirname, 'dist') }, resolve: { alias: { "ria-license": isProduction ? riaDeploymentLicense : riaDevelopmentLicense } }, optimization: { splitChunks: { cacheGroups: { "ria-modules": { // split LuciadRIA into its own chunk test: /@luciad.(ria|ria-geometry|ria-milsym)/, name: 'ria-modules', enforce: true, chunks: 'all' } } }, minimizer: [ new TerserPlugin({ // disable optimization for the LuciadRIA chunk exclude: [/ria-modules/] }) ] }, } };
Now that webpack generates a separate ria-module.js
bundle, we need to include it in our index.html, before our main.js
bundle
<!doctype html> <html lang="en"> ... <body> <h1>Hello world</h1> <div id="map"/> <script src="ria-modules.js"></script> <script src="main.js"></script> </body> </html>
If you now run npm run build
, you’ll notice that it’s dramatically faster.
The total bundle size is slightly larger than if we had put everything into one big chunk, though.
Instead of doing no optimizations at all on the LuciadRIA chunk, we can choose to only mangle the bundle, and skip the more advanced compress optimizations.
This gives us a middle-ground solution between bundle size and production build time:
const path = require('path'); const TerserPlugin = require('terser-webpack-plugin'); module.exports = (env, argv) => { const isProduction = argv.mode === "production"; const riaDeploymentLicense = path.resolve(__dirname, "src/luciadria_deployment.txt"); const riaDevelopmentLicense = path.resolve(__dirname, "src/luciadria_development.txt"); return { devServer: { static: path.join(__dirname, 'dist') }, resolve: { alias: { "ria-license": isProduction ? riaDeploymentLicense : riaDevelopmentLicense } }, optimization: { minimizer: [ new TerserPlugin({ // run the default optimization on non-RIA chunks exclude: [/ria-modules/] }), new TerserPlugin({ // only mangle the RIA chunk include: [/ria-modules/], terserOptions: { mangle: true, compress: false } }) ], splitChunks: { cacheGroups: { "ria-modules": { test: /@luciad.(ria|ria-geometry|ria-milsym)/, name: 'ria-modules', enforce: true, chunks: 'all' } } } }, } };
We’ll let you choose how you trade off build time against bundle size. In the table below you can see an overview of some build timings and resulting bundle sizes, for the different options. This should give you an idea of how much effect these options have:
Map type | Optimizations | Build time | Bundle size |
---|---|---|---|
Map |
Everything in one chunk, all optimizations |
7 seconds |
1.01 MB |
Map |
Everything in one chunk, only mangle |
5 seconds |
1.22 MB |
Map |
Split off each LuciadRIA modules in a separate chunk, no optimization |
2 seconds |
1.67 MB |
Map |
Split off each LuciadRIA modules in a separate chunk, only mangle LuciadRIA |
5 seconds |
1.24 MB |
WebGLMap |
Everything in one chunk, all optimizations |
12 seconds |
8.35 MB |
WebGLMap |
Everything in one chunk, only mangle |
8 seconds |
8.55 MB |
WebGLMap |
Split off each LuciadRIA modules in a separate chunk, no optimization |
3 seconds |
9.39 MB |
WebGLMap |
Split off each LuciadRIA modules in a separate chunk, only mangle LuciadRIA |
8 seconds |
8.58 MB |