Progressive Web App at Naukri.com

5.00 avg. rating (98% score) - 3 votes

Progressive Web App at Naukri.com

In today’s world where lives are internet driven, it is essential to provide faster and seamless web experience to our users.
Also, it is important to realize that installing and keeping every native app is not feasible for users due to storage limitations.
Following the same notion, Naukri.com decided to introduce Naukri Lite its own progressive web app.

What is a Progressive Web App?

A Progressive Web App is:

  • Progressive – Works for every user, regardless of browser choice because it’s built with progressive enhancement as a core tenet.
  • Responsive – Fits any form factor: desktop, mobile, tablet, or whatever is next.
  • Connectivity independent – Enhanced with service workers to work offline or on low-quality networks.
  • App-like – Feels like an app, because the app shell model separates the application functionality from application content.
  • Fresh – Always up-to-date thanks to the service worker update process.
  • Safe – Served via HTTPS to prevent snooping and to ensure content hasn’t been tampered with.
  • Discoverable – Is identifiable as an “application” thanks to W3C manifest and service worker registration scope, allowing search engines to find it.
  • Re-engageable – Makes re-engagement easy through features like push notifications.
  • Installable – Allows users to add apps they find most useful to their home screen without the hassle of an app store.
  • Linkable – Easily share the application via URL, does not require complex installation.

Why PWA?

The introduction of web app was very fruitful to us and here are the highlights of the impact.
   

How we made it?

It involved a well-focused team of enthusiastic and passionate engineers who dedicatedly worked on it for 4 months.  The first and foremost challenge was to decide the technology stack. We were sure that our efforts are focused around following major aspects:

  1. Scalability
  2. Data flow
  3. Faster load time
  4. Data persistence
  5. Routing
  6. Rendering
  7. Design Uniformity

Scalability

With more than a million user visiting naukri.com and features going live daily, considering the scalability was definitely the foremost important task for us. Some concepts which really made us scale were:

  • Building Interfaces over third party library
  • Component based development
  • Single state object of whole application
  • HTTP2 and lazy loading

Data flows

Frontend applications are different from backend applications and to handle data state in SPA(single page application) is not easy since as mentioned by Redux: “If a model can update another model, then a view can update a model, which updates another model, and this, in turn, might cause another view to update. At some point, you no longer understand what happens in your app as you have lost control over the when, why, and how of its state. When a system is opaque and non-deterministic, it’s hard to reproduce bugs or add new features.”

So to solve above mentioned complexity we adopted Redux, a Flux pattern implementation, in our project and reaped lots of benefits out it. Redux is also great when it comes to mixing asynchronicity and mutation.

Faster load time

In order to ensure that app users don’t feel the heat of slow network connections, we applied caching at various levels. We used sw-precache & sw-toolbox for defining our caching strategies. Precache worked well for app shell whereas dynamic cache helped us in caching files as the user used the app. Apart from file cache we also used data cache or data persistence.

Data persistence

Following are three major client side storage APIs sorted in order of preferences:

  1. IndexedDB
  2. WebSQL
  3. localStorage

But there are discrepancies in their supports in major browsers. So for a common API interface and to support all browser types, we used localforage(a LocalStorage-Like-Api with callbacks). After storing the data locally, following are some common requirements you have to handle in order to customize at storing and re-hydration phases:

  1. Blacklist or whitelist store keys to be persisted.
  2. Handle per reducer rehydration logic.
  3. Debouncing storage calls
  4. Purging

We used a library Redux persist to handle the above-mentioned requirements. We also ensured to normalize the states for an efficient space utilization. For more details read through Normalization. 

So far so good? Not really!

Along with data persistence, one must also ensure to migrate any state updation as per the requirement.For a better picture, read redux-persist-migrate Some other articles that can be useful to you are:

Routing

URLs are the backbone of the web. They allow users to share information freely across the web.For easy to configure routing we used Page.js. Page js is a small library that allows you to target express styled routes. Challenges

  • Render views based on routes.
  • Lazy loading dependencies based on routes.

Solution With React whenever you wish to render say component <View> after an async operation it’s better to do it like this. 

<AsyncTask>
    </View>
</AsyncTask>

React easily allows you to create a wrapper around other libraries. For lazy loading dependencies, we used http://requirejs.org/  but created our own wrapper components over it like below.

{
    const dependencies = {
        Component1 : "path to some Component1 file",
        Component2 : "path to some Component2 file
    };
    const renderFunction = ({Component1,Component2})=>{
        return <Component1>
                    <Component2 />
                </Component1>;
    }
    return <LazilyLoad module={dependencies}>{renderFunction}</LazilyLoad>;
}

Rendering

Whenever we talk about rendering, then the first thing that comes to mind is DOM with some sort of disappointment in terms of “layout calculation because of every time something change in DOM, browsers recalculate the CSS, redraw layout with repaint the entire web page” and this is the cause of the DOM slowness. So to solve this problem we adopted Virtual DOM mechanism to render layouts in our application and we also saw a significant gain in rendering. We are not going in depth of why virtual DOM is faster compared to Native DOM operation because there are numbers of article floating on the internet regarding the same.

Design Uniformity

We chose the Material design by materializecss library to build our web app, and hence to attain the flexibility of various components available in it along with the grid system it provides. Materializecss not only provides you bold design but also an ease of design modulations as per your requirement. It will provide you with sass files which only get compiled as per your usage, hence also saving on the load of CSS files.

Findings

  1. Use actual devices rather than simulator for checking app performance and feature support.
  2. Prefer React.PureComponents over React.Components for performance
  3. Avoid inlining object literals like below:<MyComponent style={{ paddingTop: 10 }} />
  4. Use React Perf to figure out wasted renders.

Build processes

  1. Babel for transpiling ES6 to ES5.
  2. Rollup to resolve ES6 modules. In love with its tree-shaking feature.

Future Road Map

The job isn’t done yet. Work is still in progress. Some of the stuff in our development pipeline is:

  1. Offline job applies
  2. Reducing FMT (First meaningful load time).
  3. Automation of processes.
  4. Open sourcing the components used in our web app to contribute back to our community.

Stay tuned to keep yourselves updated…

References :

https://developers.google.com/web/progressive-web-apps/
https://developers.google.com/web/fundamentals/getting-started/codelabs/your-first-pwapp/