The magic of lazy loading images and splitting bundles ✨
the art of lazy loadinglazy loading@hdjirdeh
View Slide
lazydefer until the user needs it
loadingfetch + render
what?
images!
highesthighmediumlowlowhighhighestmediumlow
lowhighesthighmediumlowlowhighhighestmedium
highesthighmediumlowlowhighhighestmediummediumlow
> 40% of data fetched = images
scroll event listeners
class="lazy" data-src="kittens.png"
let image = document.querySelector("img.lazy");const lazyLoad = () "=> {setTimeout(() "=> {if (image.getBoundingClientRect().top "<= window.innerHeight "&&image.getBoundingClientRect().bottom ">= 0) {image.src = image.dataset.src;}}, 200);};document.addEventListener("scroll", lazyLoad);let image = document.querySelector("img.lazy");const lazyLoad = () "=> {setTimeout(() "=> {if (image.getBoundingClientRect().top "<= window.innerHeight "&&image.getBoundingClientRect().bottom ">= 0) {image.src = image.dataset.src;}}, 200);}document.addEventListener("scroll", lazyLoad);};
intersection observer
let image = document.querySelector("img.lazy");let lazyObserver = new IntersectionObserver(entry "=> {if (entry.isIntersecting) {const image = entry.target;image.src = lazyImage.dataset.src;lazyObserver.unobserve(image);}});lazyObserver.observe(image);let image = document.querySelector("img.lazy");let lazyObserver = new IntersectionObserver(entry "=> {if (entry.isIntersecting) {const image = entry.target;image.src = lazyImage.dataset.src;lazyObserver.unobserve(image);}});lazyObserver.observe(image);
bit.ly/inter-obs
native browser feature?
lazyload=“on”
what else?
code!
1 KB of Image1 KB of JavaScript≠
split, split, split ✂
import('lodash.sortby').then(module "=> module.default).then(doSomethingCool)import('lodash.sortby').then(module "=> module.default).then(doSomethingCool)dynamic import
what should I do?
audit
auditfix✂
Use a librarylozad.jslazysizesyall.js
Use IO + polyfillUse a libraryUse placeholders/load early
Use placeholders/load earlypinterestpinterest.com
Use placeholders/load earlylqip-loadergithub.com/zouhir/lqip-loader
Use placeholders/load earlyPrimitiveprimitive.lol
Use placeholders/load earlysqipgithub.com/technopagan/sqipSQIPLQIPImage
export const routes: Routes = [{path: 'main',component: 'MainComponent',},{path: 'details',loadChildren: 'details/details.module#DetailsModule'}];angular router
const DetailsComponent = () "=> import(‘./details.vue');export const router = new VueRouter({routes: [{ path: '/details', component: DetailsComponent },]})const DetailsComponent = () "=> import(‘./details.vue');export const router = new VueRouter({routes: [{ path: '/details', component: DetailsComponent },]})const DetailsComponent = () "=> import(‘./details.vue');export const router = new VueRouter({routes: [{ path: '/details', component: DetailsComponent },]})export const router = new VueRouter({routes: [{ path: '/details', component: DetailsComponent },]})vue router
import { Router } from 'preact-router';import Home from '"../routes/home';import Details from 'async!./details';export const App = () "=> (");import { Router } from 'preact-router';import Home from '"../routes/home';import Details from 'async!./details';export const App = () "=> (");preact cliimport { Router } from 'preact-router';import Home from '"../routes/home';import Details from 'async!./details';export const App = () "=> (");
const loadPage = (page) "=> (dispatch) "=> {switch(page) {case 'view1':import('"../my-view1.js').then((module) "=> {"// ""...});break;"//""...}dispatch(updatePage(page));};const loadPage = (page) "=> (dispatch) "=> {switch(page) {case 'view1':import('"../my-view1.js').then((module) "=> {"// ""...});break;"//""...}dispatch(updatePage(page));};const loadPage = (page) "=> (dispatch) "=> {switch(page) {case 'view1':import('"../my-view1.js').then((module) "=> {"// ""...});break;"//""...}dispatch(updatePage(page));};polymer pwa-starter-kit
import Loadable from 'react-loadable';const DetailsComponent = Loadable({loader: () "=> import('./details.component'),loading: () "=> Loading""...",});import Loadable from 'react-loadable';const DetailsComponent = Loadable({loader: () "=> import('./details.component'),loading: () "=> Loading""...",});import Loadable from 'react-loadable';const DetailsComponent = Loadable({loader: () "=> import('./details.component'),loading: () "=> Loading""...",});jamiebuilds / react-loadable
import loadable from 'loadable-components';const DetailsComponent =loadable(() "=> import('./details.component'), {LoadingComponent: () "=> Loading""..."});import loadable from 'loadable-components';const DetailsComponent =loadable(() "=> import('./details.component'), {LoadingComponent: () "=> Loading""..."});import loadable from 'loadable-components';const DetailsComponent =loadable(() "=> import('./details.component'), {LoadingComponent: () "=> Loading""..."});smooth-code / loadable-components
import LoadableVisibility from‘react-loadable-visibility/react-loadable'const DetailsComponent = LoadableVisibility({loader: () "=> import('./details.component'),loading: () "=> Loading""...",})import LoadableVisibility from‘react-loadable-visibility/react-loadable'const DetailsComponent = LoadableVisibility({loader: () "=> import('./details.component'),loading: () "=> Loading""...",})import LoadableVisibility from‘react-loadable-visibility/react-loadable'const DetailsComponent = LoadableVisibility({loader: () "=> import('./details.component'),loading: () "=> Loading""...",})stratiformltd / react-loadable-visibility
const DetailsComponent = Loadable({loader: () "=> import('./details.component'),loading: () "=> Loading""...",delay: 300,});jamiebuilds / react-loadableconst DetailsComponent = Loadable({loader: () "=> import('./details.component'),loading: () "=> Loading""...",delay: 300,});
suspense
bit.ly/lazy-metrometro (react native)
auditfix✂observe
webpack-bundle-analyzer
size-plugin
progressivetooling.com
responsive imageswith srcsetpreload criticalchunksconvert gifsto .mp4pre-cache assetsusing a serviceworkercompress assetsremove unusedpackagesmodule /nomodule
/DxUx
@hdjirdeh