How to Resolve Bugs Caused by Conflicts Between Previous and Current Built Assets in SPA
Intro
Every time when you release a new web app, the bundler such as Webpack, Rollup generates a random hash filename for each build assets.
This allows the server to deliver new assets to the web app without conflicts and avoid asset conflicts with previous versions. However, this behavior can be problematic in SPA(Single page application) where the entire page does not refresh in the browser.
Imagine that you just released a new version of your SPA while users are using it. In a typical SPA, code splitting is applied. So, only after the user requests a new page your app will start to fetch new assets such as new scripts, CSS, or images. Unfortunately, your SPA will try to fetch old assets that have already disappeared from the server, and unless he refreshes the browser, he will just be staring at a white screen.
My company Luko's web app had an interface that accepted new input by moving on to the next step after the user completed input. And every time a new web app was released, there was a bug that prevented users from moving on to the next step.
- Luko Onboarding App -
How to solve this problem?
My colleague Piyush came up with the following idea to solve this problem.
- Build a new version of the app. New assets are created in the
/dist/assets
folder. - Copy the newly created assets and place them in a folder named
/.tmp
. - Get the previous version of assets and move them to the
/dist/assets
folder. Currently, the assets that were just built and the assets of the previous version coexist in the/dist/assets
folder. - Save the assets in new-assets somewhere and use them when building a new file.
The most important key point of this idea is that both previously built assets and current assets COEXIST in the /dist/assets
folder. This way, users can use the previous version of the app without issue until they reload the page.
We had to think about where to store the old and new assets, and we ended up creating a new git repository cached-assets
and storing them there.
The script below is the one we run after the app is built in CI/CD.
bash
#!/bin/bash
rm -rf cached-assets
mkdir cached-assets
# Pull old assets
cd cached-assets
git config --global user.email "your@email.com"
git config --global user.name "your name"
git init
git pull https://oauth2:${GITLAB_TOKEN}@gitlab.com/SOME/PATH/cached-assets.git BRANCH_NAME
cd ..
# Move new asset to temp
# dist/assets -> .tmp/
rm -rf .tmp
mkdir .tmp
cp -r dist/assets/* .tmp/
# Put old assets with current asset
mv cached-assets/* dist/assets/
# Force update cached-asset
cd cached-assets
rm -rf .git
git init
git checkout -b BRANCH_NAME
cd ..
mv .tmp/* cached-assets/
cd cached-assets
git add -A
git commit -m "update cached assets"
git push -f https://oauth2:${GITLAB_TOKEN}@gitlab.com/SOME/PATH/cached-assets.git BRANCH_NAME
## delete .tmp, cached-assets folder
cd ..
rm -rf .tmp
rm -rf cached-assets