Skypack + Webpack = 🎉
Best way to reduce bundle size is to not have third party dependencies inside source code.
Skypack
If you wonder what is skypack is, it is a open source CDN which delivers all the NPM packages up-converted to ES6 Modules.
This is huge move towards improving web, now we can use all major day to day npm packages directly in browsers, we never need npm install any more, that saves lots of space as well as time. Here is small example of how to consume packages
// index.js
import React from 'https://cdn.skypack.dev/react';
import {render} from 'https://cdn.skypack.dev/react-dom';
let element = React.createElement("h1", "Skypack is awesome!!!");
render(element, document.getElementById("root"));
All module we use from skypack are ES6 Modules, which means we don't need to use webpack
or any other transpiler/bundler
to convert common-js
module library into browser compatible js files.
If you noticed, we did not use JSX
in above example, we just used plain React api to create H1
element. If we need to use JSX
then we have to use some tools to parse JSX
and generate react element. There comes a need to use any transpiler/bundler.
Webpack
If you have't heard about webpack, to put it in simple term, Webpack is a bundler that provides ways to transpile and bundle the javascript, css, images, etc asserts so that it can be used in browsers.
Now webpack has added the support for emitting native ES6 Modules, with that our application code also can be transfer to browser as ES6 modules, which will give us runtime benefits as there is only very very less boilerplates / pollyfills.
Example
I played around with skypack
by revamping my personal project Readme Later which I am using to keep all the useful links. My bundle size has reduced drastically since all the dependency packages are now download directly in the browser. This is huge savings.
Here is the webpack config to emit the ES6 Modules of our source code:
// Webpack config to emit ES6 modules
module.exports = {
mode: "development",
devtool: "cheap-module-source-map",
entry: [
paths.appIndexJs,
],
experiments: {
// The support for ES6 Moduels is experimental
// so we need manually enable this feature
outputModule: true,
},
output: {
// Tell the webpack to emit our code as ES6 Modules
module: true,
},
// Tell Webpack to that we are targetting only browsers.
node: false,
resolve: {...},
module: {...},
plugins: [...],
};
If you observed well, we have consumed react
in out first example like this
import React from 'https://cdn.skypack.dev/react';
This is not cool right, we cannot copy and paste the url everywhere we consume React
. What if we wish to consume React
in the regular way like
import React from "react"
for that we need to change our webpack config little bit to automatically replace react
with https://cdn.skypack.dev/react
.
// webpack config
module.exports = {
entry: [...],
output: [...],
// Tell webpack to treat react as external dependency
// whose value is the url.
externals: {
"react": "https://cdn.skypack.dev/react",
},
// Tell webpack that all the externals dependencies are ES6 Modules
externalsType: "module",
}
It is not good to keep the dependencies config inside the build config, so let's move it to the place where all the dependencies are handled usually package.json
.
// package.json
{
"name": "Readme Later",
"dependencies": {...}
"externals": {
"development": {
"react": "https://cdn.skypack.dev/react@v17.0.1",
},
"production": {
// Pinned url optimized for production ans fast resolution
"react": "https://cdn.skypack.dev/pin/react@v17.0.1-yH0aYV1FOvoIPeKBbHxg/mode=imports,min/optimized/react.js",
}
},
}
// webpack.config
const packageJson = require("./package.json");
module.exports = {
...
externals: packageJson.externals[process.env.NODE_ENV],
externalsType: "module",
...
}
Here is the full example repository
Advantages
- Reduced bundle size since it only has the source code.
- Faster load time.
- Efficient caching of dependencies since dependencies won't change often.