I managed to hit 5 articles this week š Iām already finding a lot of value re-reading all of the blurbs on Fridays. This week I focused on Webpack and some Async/Await fun.
#1: A Beginnerās Guide to Webpack 4 and Module Bundling - Part 1
Webpack: module bundler for JS but can be used for HTMl, CSS and images
- can give you more control over the number of HTTP requests your app is making
- can easily consume packages from nom
Can create an npm script for running webpack -> npm run develop
(dev mode) or npm run build
for production
- production is minified and file size is much smaller - optimizes for execution speed at runtime and output file size
- dev mode optimizes for build speed and debugging
ES Modules (split your program into many small, self-contained programs
- importing modules from npm donāt uses a relative path but all of your own imports do need a relative path
Tree Shaking
* in dev mode, webpack includes whole packages but in production mode all of the unused modules are removed from bundle
Loaders
* let you run preprocessors (eg. babel) on files as they are imported - allows you to bundle resources beyond js
* loaders can be chained to gather into a series of transforms
CSS in JS
import ā./style.scssā
* needs the style-loader
npm package which outputs the string tiny an embedded <style>
tag
* Makes it easier to bundle assets together (images, css, HTML)
* dead code elimination: when the JS component is no longer loaded, the CSS file will also not be loaded
* css modules: local CSS with unique classes to avoid css overwriting
* this allows us to decrease HTTP requests by bundling/splitting code cleverly
Images (eg package: file-loader
)
* store the source of images as strings inside js - preloads them and the browser doesnāt have to fetch them with separate requests later
import imageExample from ā./image-exampleā
img.src = imageExample
- this will include an image where the src attribute contains a dataURI of the image itself
- background images in CSS are also processed by
file-loader
#2: A Beginnerās Guide to Webpack 4 and Module Bundling - Part 2
Dependency Graph
- loaders build a tree of dependencies among your assets and compiles single static assets
Code Splitting
- allows you to split code into bundles that can be loaded on demand or in parallel (faster load time)
button.onclick = () => {
import(/* webpackChunkName: "chat" */ "./chat").then(chat => {
chat.init()
})
}
We can import the webpack chunk when we click the button (lazy loading)
Plugins
* loaders operate transform on single files, plugins operate across larger chunks of code
* help split code in clever ways and optimize for production
* webpack plugin "mode" lets us use dev and production specific plugins (eg: UglifyJsPlugin)
Production
* need a config file for dev, and prod (webpack.common.js, webpack.dev.js, webpack.prod.js - use common file in both dev and prod file)
Split CSS
* best practise is to split CSS from JS when bundling for production (use ExtractTextWebpackPlugin) and add the css file to your html as a link
* allows for parallel loading of JS and CSS so it will be faster
Generating HTML
* html-webpack-plugin updates our index.html file to reference new file paths
Development
* webpack-dev-server provides a simple web server with live reloading
* HotModuleReplacement swaps module at runtime without the refresh
HTTP/2
* webpack gives you control over how assets are fetched as well
* HTTP/2 allows multiple files to be delivered in a single request (so concatenation isnāt a silver bullet anymore)
* Could be more performant to cache several small files
#3: Code Splitting
Code splitting allows you to split code into various bundles to load them on demand or in parallel
3 Approaches:
- Entry Points - manually split code using
entry
configuration - in the webpack.config.js file, list the modules by their js files
module.exports = {
mode: ādevelopmentā,
entry: {
index: ā./src/index.jsā,
another: ā./src/another-module.jsā
}
Pitfalls:
* If there is duplicated modules between chunks, they will be included in both bundles
* Canāt be used to dynamically split code
- Prevent Duplication - use the SplitCHunks to deduce and split chunks
- extract common dependencies into a separate chunk
optimization: {
splitChunks: {
chunks: āall
}
}
-
other code splitting modules: mini-css-extract-plugin (split css out of main app), bundle-loader (split code and lazy load the resulting bundles), promise-loader (similar to bundle-loader but uses promises)
-
Dynamic Imports - Split code via inline function calls within modules
- use import() syntax (uses promises so need the promise polyfill for older browsers)
return import(/ webpackChunkName: "lodash" / 'lodash').then(_ - webpackChunkNAme in the component will cause our separate bundle to be named loads.bundle.js
- import() returns a promise so ti can be used with async functions
- use import() syntax (uses promises so need the promise polyfill for older browsers)
Prefetching/Preloading modules
Use inline directives when declaring imports allows webpack to output "Resource Hint" which tells the browser that for prefetch (probably needed for nav sometime in the future) and preload (resource might be deemed in the current nav)
import(/* webpackPrefetch: true */ 'LoginModal');
This creates: <link rel="prefetch" href="login-modal-chunk.js">
(browser will prefetch)
Prefetch: webpack will add the prefetch hint once the parent chunk has been loaded
Preload vs prefetch (browser support could be different!)
* preload chunk loads in parallel to parent and prefetch loads after parent
* preloaded has medium priority and is instantly downloaded, prefetch loads in browser idle time
* preloaded instantly requested by parent, prefetch is used anytime in the future
Bundle Analysis:
* useful to analyze code splits to check where modules end up (use like webpack-chart and webpack-visulizer or webpack-bundle-analyzer)
#4: Async Function
An async function operates asynchronously via the event loop - uses a promise to return its result
function doesTheWork() {
return new Promise(resolve => {
setTimeout(() => {
resolve(āresolvedā);
}, 2000);
});
}
async function asyncCall() {
var result = await doesTheWork();
console.log(result) // "resolved"
}
asyncCall()
Async functions can contain an await expression that pauses the execution of the async function and waits for the passed Promiseās resolution, and then resumes the async functionās execution
The await keyword is only valid inside async functions - you will get a SyntaxError otherwise
Options:
//sequential start - takes 2 + 1 seconds
async function() {
const slow = await resolveAfter2Seconds();
// if the value of the expression following the
//await operator is not a Promise, itās converted to a resolved Promise
const fast = await resolveAfter1Second();
}
//concurrent start - takes 2 seconds total - this can also be done with Promise.all (might be better if you have
//to wait for more than 2 promises
async function() {
const slow = resolveAfter2Seconds();
const fast = resolveAfter1Second();
console.log(await slow);
console.log(await fast); // doesnāt log until slow is done!
}
The async function is implicitly wrapped in Promise.resolve so you donāt have to put an await on the return statement in an async function
#5: Async/await
async: a function will always return a promise (js will automatically wrap the return statement in a promise
await: only works inside the async function - makes js wait until that promise is resolved, and then evaluates the expression
- the waiting doesnāt cost CPU resources because the engine can do other jobs in the meanwhile
- similar to promise.then but easier to read/write
// wait 3 seconds
await new Promise((resolve, reject) => setTimeout(resolve, 3000));
await accepts thenables (any function that
class methods can also be async:
class Waiter {
async wait() {
return await Promise.resolve(1);
}
}
new Waiter().wait().then(alert); //1
Error Handling:
- await Promise.reject(new Error("Whoops!"))
- await waits for the promise to resolve then throws the error
- Can use in a try/catch as per usual
- if not in a regular try/catch, the promise is rejected and we can do something like: f().catch(alert);