In the last few weeks, the team has been involved in the great challenge of optimizing a PWA so it would be able to run smoothly on a low end Android device.
As part of the achieved results, the bundle size has been reduced by 70%, without removing any of the existing features.
One of the features the app has is a QR code reader. In that way, we found that the used library was a bit heavy for our app. So we decided to focus on that and started by inspecting the library code.
And then, it was time to act.
While inspecting QR scan library code, we found there were two exported components: Default and Legacy mode. The only difference between both were the input method of the QR code, but functionality was the same. So we decided to decouple them and remove unnecessary code.
It allowed us incrementing library maintainability and reducing total bundle size.
We found that the major part of the components was using classes and React lifecycle methods like componentDidMount or componentDidUpdate.
So, the next step was changing these classes to functional components and hooks. Letting the functional component with HTML markup and delegating all the logic on the hook. Therefore, we aim for the React target to be versions that support hooks.
This benefits us in two ways: decrementing components size and separating logic from the view.
Many dependencies were old, so we bet to update them.
The key was reducing the probability of bugs associated with these old libraries, and incrementing the maintainability of our library.
At this point, first we needed to understand what were made wrong in the library.
We found there was an excessive use of the setTimeout function without cleaning the generated ids. This produced that they remained callbacks queued post-processing.
Then, our next decision was moving from setTimeout to requestAnimationFrame. This gave us an advantage since the scan callback started to be executed in moments when the browser does not use the capacity of computing to render frames, producing an improvement on the camera stream fluency and avoiding main thread block.
The last but not least decision associated with performance was using web workers in QR processing.
Library didn’t have any way of giving information to the dev about what was happening, how it worked or scanned. So it was so difficult trying to find an issue while debugging.
This helped us to take this decision: adding a debugging property to the component to start logging useful data that could explain what was happening or failing.
We found that the library provides support for a Viewfinder to show a frame that indicates the distance and center while scanning the QR. This frame was developed with a div and inline-styles.
In order to give it extensibility, we decided to transform a component into an SVG that could be edited as needed. In addition, we are going to extend Viewfinder capabilities, allowing it to be displayed or not and receiving a custom component.
Finally, we published @blackbox-vision/react-qr-reader with all the mentioned optimizations.
All these changes resulted in reducing bundle size from 210.6 KB to 88.3 KB!
This implies a reduction of almost 60% of the weight of the library. This reduction in weight, results in a component that loads several times faster and whose average QR reading capacity is up to about 50 cm away at least.
For the next versions, we have in mind:
Thanks a lot for your time 😁 Contact us for any suggestion or comments!
If you are interested into library development, stay tuned! We will be publishing a blog post series soon 🎉!
Subscribe for latest updates
Sign Up for our newsletter and get notified when we publish new articles for free!