The Problem
When their apps becomes bigger, many JSPM users experience slowness of their default development workflows. Typically users choose JSPM because they want to use the new ES6/7/typescript/whatever syntax and to get rid of build tools. And JSPM gives them what they want — in-browser ES6 transpilation for users’ ES6 modules by default and a variety of loader plugins to move their build tools into the browser. This works fine for small apps but when the app grows and gets more dependencies, the initial page load becomes very slow.
For one of my apps the initial page load with the default workflow could take ~40s.
The Causes
It’s possible to speed up things but unfortunately it requires some build tools. To improve the workflow, one has to identify the causes of the slowness. The most likely the causes are:
- In-browser transpilation because all files are re-transpiled on each page load. Not-only the transpilation of the JS files is slow, in-browser compilation of templates (if you have some templates) is slow too.
- The number of requests JSPM/SystemJS makes to load files makes the app slow in development because neither the browsers nor the most of development http servers handle the big number of requests well.
The Solution
There are several solutions for these bottlenecks:
- Move transpilation of app JS files and compilation of templates to a build tool. The tool must cache the result of compilation and re-compile only what was changed.
- Use JSPM bundles to build dependencies of the app.
The difference between improvements #1 and #2 is that #1 results in a transpiled/compiled copy of application files whereas #2 results in a single, possibly minified, file. So JSPM loads application js files and templates as usual but w/o using the transpiler or loaders plugins. Also the bundle can be rebuilt once in a while — when dependencies change. And app files needs to be rebuilt as they change.
- use a faster web server that supports modern HTTP protocols
For example, the commonly used
browser-sync
is quite slow compared to, say, nginx when serving many requests.
The Example
I have created a demo of my optimized development workflow here: https://github.com/OrKoN/jspm-ember-playground Although it uses the Ember framework I think it may be re-used with some modifications for other frameworks too.
It uses nginx
as a development server, so it needs to be installed first. Also it requires the realpath
command to be installed.
Most likely the demo works only on Linux-like systems.
To get it running once you installed nginx
and realpath
:
git clone https://github.com/OrKoN/jspm-ember-playground
cd jspm-ember-playground
npm install
gulp
sudo ./bin/start.sh
and open https://127.0.0.1:8080 for the development version or run:
gulp build
and open https://127.0.0.1:8081 for the production version of the app. Most likely the browser will warn you of an unverified ssl certificate but just continue.
Important notes about the structure of the app
Apart from the dependencies and overrides jspm has the following config:
"directories": {
"baseURL": "build"
},
"configFile": "jspm.config.js"
To avoid confusion the config file is renamed to jspm.config.js
and the baseURL
is set to the build
folder because nginx will serve files from the build
.
The build
folder is the root of the compiled development version of the app.
The other folders are:
app
- the source folder of the applicationbin
- the folder containingstart.sh
andstop.sh
to start/stop the nginxbuild
- as said previously, the root of the web server that contains the built development version of the appconf
- nginx configrationlogs
- nginx logspids
- nginx pidvcl
- config for the css framework that we use
Important files are the following:
gulpfile.js
- where all gulp tasks are definedjspm.config.js
- the config for jspm/systemjs. It’s symlinked to thebuild/jspm.config.js
by a gulp taskmain-bundle.js
- the bundled app dependencies. It’s symlinked to thebuild/main-bundle.js
by a gulp task
Important gulp tasks:
gulp
- the default task that rebuilds the development version of the app completelygulp build
- builds the production version of the app and places it to thedist
foldergulp watch
- watches the app folder for changes and re-compiles stuff that changed. In the demo,gulp watch
actually rebuilds everything but it’s easy to rebuild only what changed using gulp-changed or other tool
This is how the app looks like:
This is a development version (which means your can edit app files, hit the refresh button and see the update) and it takes 164ms to start. Of course, this app has few dependencies and it’s quite simple itself. But from my experience this setup scales good for bigger apps too.
Thanks for reading. Share your JSPM setups. I am eager to learn from others!