How To Use Webpack to Host Frontend Code via External URL
I am looking to use CloudFront to store JavaScript files rather than serving them using a proxy server or using my application code (because it is faster). In this note, I am looking at how to do that.
References
Related
What is Webpack?
Webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resource or asset.
- It bundles ES Modules, CommonJS, and AMD modules
- It can create a single bundle or multiple output chunks that are asynchronously loaded at runtime (to reduce initial loading times)
- Dependencies are resolved during compilation, reducing the runtime size
- Loaders can preprocess files while compiling, e.g. TypeScript to JavaScript, Handlebars to strings to compile function, images to Base64, etc.
- Highly modular plugin system to do whatever else your application requires.
Installation
$ npm install --save-dev webpack webpack-cli
Concepts
- Plugins
- Webpack has a rich plugin interface. Most of the features within webpack itself use this plugin interface. This makes webpack very flexible.
- Loaders
- Webpack enables the use of loaders to preprocess files. This allows you to bundle any static resource way beyond JavaScript. You can write your own loaders using Node.js.
- Performance
- Webpack uses async I/O and has multiple caching levels. This makes webpack fats and incredibly fast on incremental compilations.
- Module Formats
- Webpack supports many different formats out of the box. It performs clever static analysis on the AST of the code. It even has an evaluation engine to evaluate simple expressions. This means that you can support most simple existing libraries out of the box.
- Code Splitting
- Webpack allows you to split your codebase into multiple chunks. Chunks are loaded asynchronously at runtime. This reduces initial loading time.
- Optimizations
- Webpack can do many optimizations to reduce the output size of your JavaScript by deduplicating frequently used modules, minifying, and giving you full control over what is loaded initially and what is loaded at runtime through code splitting.
Behind the scenes, webpack "transpiles" code so that it can work in all browsers. Webpack supports a configuration file for projects. The webpack
cli command kicks off the transpilation process.
Notes
What I am Looking to Do
I want to serve JavaScript files through a CloudFront CDN in order to improve the the latency of requests that users experience when requesting JavaScript files.
PublicPath
The publicPath
configuration option can be quite useful in a variety of scenarios. It allows you to specify the base path for all the assets within your application. Every file emitted to your output.path
directory will be referenced from the output.publicPath
location. This includes child chunks and any other assets that are part of you dependency graph. In development, for example, we might have an assets/
folder that lives on the same level of our index page. But what if you wanted to host all these static assets on a CND in production?
import webpack from 'webpack';
// Try the environment variable, otherwise use root
const ASSET_PATH = process.env.ASSET_PATH || '/';
export default {
output: {
publicPath: ASSET_PATH,
},
plugins: [
// This makes it possible for us to safely use env vars on our code
new webpack.DefinePlugin({
'process.env.ASSET_PATH': JSON.stringify(ASSET_PATH),
}),
],
};
Another possible use case is to set the publicPath
on the fly. Webpack exposes a global variable called __webpack_public_path_
that allows you to do that. In your application's entry point, you can do this:
__webpack_public_path__ = process.env.ASSET_PATH;
That's all you need. Since we're already using the DefinePlugin
on our configuration, process.env.ASSET_PATH
will always be defined so we can safely do that.
There are chances that you don't know what the publicPath
will be in advance, and webpack can handle in automatically for you by determining the public path from variables like import.meta.url
, document.currentScript
, script.src
or self.location
. What you need is to set output.publicPath
to 'auto'
:
module.exports = {
output: {
publicPath: 'auto',
},
};
In cases where document.currentScrpt
is not supported like Internet Explorer), you have to include a polyfill like currentScript Polyfill
.
Progressive Web Applications
Progressive Web Applications (or PWAs) are web apps that deliver an experience similar to native applications. there are many things that can contribute to that. Of these, the most significant is the ability for an app to be able to function when offline. This is achieved through the use of a web technology called Service Workers.
Add the Workbox webpack plugin to give the application the ability to work offline.
$ npm install workbox-webpack-plugin --save-dev
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const WorkboxPlugin = require('workbox-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js',
print: './src/print.js',
},
plugins: [
new HtmlWebpackPlugin({
title: 'Output Management',
title: 'Progressive Web Application',
}),
new WorkboxPlugin.GenerateSW({
// these options encourage the ServiceWorkers to get in there fast
// and not allow any straggling "old" SWs to hang around
clientsClaim: true,
skipWaiting: true,
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
};
The webpack Workbox plugin results in two extra files being generated, service-worker.js
and the more verbose precache-manifest.b5ca1c555e832d6fbf9462efd29d27eb.js
. service-worker.js is the Service Worker file and the other file is a file that service-worker.js
requires so that it can run. Your own generated files will likely be different but you should have a service-worker.js
file there.
Registering Our Service Worker
import _ from 'lodash';
import printMe from './print.js';
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js').then(registration => {
console.log('SW registered: ', registration);
}).catch(registrationError => {
console.log('SW registration failed: ', registrationError);
});
});
}
Comments
You have to be logged in to add a comment
User Comments
There are currently no comments for this article.