Majid Hajian
Majid Hajian is a passionate software developer with years of developing and architecting complex web and mobile applications. He is passionate about web platform especially flutter, IoT, PWAs, and performance.
Majid Hajian
mhadaily
Reliable
Fast
Engaging
mhadaily
mhadaily
MaterialApp(
ThemeData(
name: "Majid Hajian",
location: "Oslo, Norway",
description: '''
Passionate Software engineer,
Community Leader, Author and international Speaker
''',
main: "Flutter/Dart, PWA, Performance",
homepage: "https://www.majidhajian.com",
socials: {
twitter: "https://www.twitter.com/mhadaily",
github: "https://www.github.com/mhadaily"
},
author: {
Pluralsight: "www.pluralsight.com/authors/majid-hajian",
Apress: "Progressive Web App with Angular, Book",
PacktPub: "PWA development, 7 hours video course",
Udemy: "PWA development, 7 hours video course",
}
founder: "Softiware As (www.Softiware.com)"
devDependencies: {
tea: "Ginger",
mac: "10.14+",
},
community: {
MobileEraConference: "Orginizer",
FlutterVikings: "Orginizer",
FlutterDartOslo: "Orginizer",
GDGOslo: "Co-Orginizer",
DevFestNorway: "Orginizer",
...more
})
);
Find me on the internet by
Cache API
XHR vs. Fetch API
let xhr = new XMLHttpRequest();
xhr.open('GET', 'http://domain/service');
// request state change event
xhr.onreadystatechange = function() {
// request completed?
if (xhr.readyState !== 4) return;
if (xhr.status === 200) {
// request successful - show response
console.log(xhr.responseText);
}
else {
// request error
}
};
// start request
xhr.send();
fetch(
'http://domain/service',
{ method: 'GET' }
)
.then( response => response.json() )
.then( json => console.log(json) )
.catch( error => console.error('error:', error) );
mhadaily
Cache API
Service Worker
mhadaily
self.addEventListener('fetch', (event) => {
console.log('[SW] Fetch ....');
const request = event.request;
event.respondWith()
}
Service worker
mhadaily
event.respondWith(
caches.match(request)
);
Service worker
mhadaily
event.respondWith(
fetch(event.request)
);
Service worker
mhadaily
event.respondWith(
caches.match(request).then( (res) => {
return res || fetch(request).then( (newRes) => {
caches.open(DYNAMIC_CACHE_VERSION)
.then( cache => cache.put(request, newRes) );
return newRes.clone();
});
})
);
Service worker
mhadaily
event.respondWith(
fetch(request)
.then((res) => {
caches.open(DYNAMIC_CACHE_VERSION)
.then(cache => cache.put(request, res));
return res.clone();
}) // Fallback to cache
.catch(err => caches.match(request))
);
Service worker
mhadaily
event.respondWith(
caches
.match(request).then((res) => {
const updatedResopnse = fetch(request)
.then((newRes) => {
cache.put(request, newRes.clone());
return newRes;
});
return res || updatedResopnse;
})
);
Service worker
mhadaily
const promiseRace = new Promise((resolve, reject) => {
let firstRejectionReceived = false;
const rejectOnce = () => {
if (firstRejectionReceived) {
reject('No response received.');
} else {
firstRejectionReceived = true;
}
};
fetch(request)
.then(res => res.ok ? resolve(res) : rejectOnce())
.catch(rejectOnce);
caches.match(request)
.then(res => res ? resolve(res) : rejectOnce())
.catch(rejectOnce);
});
event.respondWith(promiseRace);
Service worker
mhadaily
mhadaily
mhadaily
mhadaily
workbox.routing.registerRoute(
new RegExp('^https:\/\/my-api-server/post\/'),
workbox.strategies.networkOnly({
plugins: [bgSyncPlugin]
}),
'POST'
)
mhadaily
const bgSyncPlugin = new workbox.backgroundSync.Plugin('myQueueName', {
maxRetentionTime: 24 * 60 // Retry for max of 24 Hours
onSync: async((queue)=>{
let entry;
while ((entery = await queue.shiftRequest())){
try {
// Perform fetch request and perhaps show notificaiton to user
// you can also update cache
} catch(error) {
// remove request from the queue
}
}
});
});
mhadaily
const showNotification = () => {
self.registration.showNotification('Post Sent', {
body: 'You are back online and your post was successfully sent!',
icon: 'assets/icon/256.png',
badge: 'assets/icon/32png.png'
});
};
mhadaily
adaptiveLoading
mhadaily
2G
3G
4G
mhadaily
2G
3G
4G
mhadaily
workbox.routing.registerRoute(
new RegExp('/img/'),
workbox.strategies.cacheFirst({
cacheName: 'images',
plugins: [
adaptiveLoadingPlugin,
workbox.expiration.Plugin({
maxEntries: 50,
purgeOnQuotaError: true,
}),
],
}),
);
mhadaily
const adaptiveLoadingPlugin = {
requestWillFetch: async ({request}) => {
const urlParts = request.url.split('/');
let imageQuality;
switch (
navigator && navigator.connection
? navigator.connection.effectiveType
: ''
) {
//...
case '3g':
imageQuality = 'low_quality';
break;
//...
}
const newUrl = urlParts
.splice(urlParts.length - 1, 0, imageQuality)
.join('/')
.replace('.jpg', '.png');
const newRequest = new Request(newUrl.href, {headers: request.headers});
return newRequest;
},
};
mhadaily
PRECACHE
mhadaily
PRECACHE
shell_header.html
shell_footer.html
mhadaily
Dynamically changes
mhadaily
Fetch
Process
Fetch
Render
mhadaily
registerRoute(/\.html$/,
workbox.streams.strategy([
// Get from cache
() => cacheFirst.handle({
request: new Request("/shell_header.html"),
}),
// Get the body from the network
({request}) => networkFirst.handle({
request: `${request.url}?content=true`
});
// Get from cache
() => cacheFirst.handle({
request: new Request("/shell_footer.html"),
}),
]);
);
mhadaily
mhadaily
The web platform today is way more powerful than what we think!
let's embrace it!
Majid Hajian
mhadaily
Slides and link to source code
bit.ly/pwa-patterns
majid[at]softiware[dot]com
SVG icons credited to undraw.co
By Majid Hajian
Building progressive web apps has become the norm of web development these days! In fact, we are being introduced to more complex patterns and strategies to create a PWA. For example, When I built my book's website pwawithangular.com, I considered several caching strategies and patterns to deliver fully offline and high-performance web applications that can load under 2 seconds with 3G internet. In this talk, I will share how I could manage to build a fast and reliable PWA by using different caching strategies, advanced PWA architecture, and patterns.
Majid Hajian is a passionate software developer with years of developing and architecting complex web and mobile applications. He is passionate about web platform especially flutter, IoT, PWAs, and performance.