spa, isomorphic and back to the server: our journey with javascript @ jsday 2017 in verona (italy)

Post on 23-Jan-2018

470 Views

Category:

Technology

2 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Alex Nadalin - CTO @ namshi.comJsDay 2017 (Verona - Italy)

WARNING

Controversyahead

Take #1

Started with a SPA

...which pissed bots off

...which pissed clients off

...which pissed clients off

...which pissed clients off

Take #2

Isomorphic js

Both on the client & the server

universal js

Works only in movies

https://2015.jsday.it/talk/back-to-the-future-isomorphic-javascript-applications/

Clients are still angry

Sneak peak of a custom-madeuniversal js framework

Sneak peak of a custom-madeuniversal js framework

Sneak peak of a custom-madeuniversal js framework

Fonzie does not approve.

The do-over

Simplify our codebase

Better client-side performance

roi

Appealing looks

Best website?

It’s an app.

It’s an app.

Smooth transitions

It’s an app.

Smooth transitions

Great (perceived) performance

Can a websiteMatch that?

Spa closely bridges the gap

Spa closely bridges the gap

Additional layer of complexity

Generally no.

Spa closely bridges the gap

Additional layer of complexity

Do we need it?

service is king

The b* stack

The b* stack

The b* stack

The b* stack

The b* stack

server

client

Webpack 2

Webpack 2

Tree-shaking saved us15/20% of the gzipped

bundle size

Say “no” to jquery(as much as possible)

Abstraction === cost

Abstraction === cost let obj = { name: 'alex', age: 28, hair: 'enough', status: 'married', job: 'who really knows', }

_.pick(obj, ['name', 'age'])

{ name: obj.name, age: obj.age,}

Abstraction === cost let obj = { name: 'alex', age: 28, hair: 'enough', status: 'married', job: 'who really knows', }

_.pick(obj, ['name', 'age'])

{ name: obj.name, age: obj.age,}

Abstraction === cost let obj = { name: 'alex', age: 28, hair: 'enough', status: 'married', job: 'who really knows', }

_.pick(obj, ['name', 'age'])

{ name: obj.name, age: obj.age,}

Abstraction === cost let obj = { name: 'alex', age: 28, hair: 'enough', status: 'married', job: 'who really knows', }

_.pick(obj, ['name', 'age'])

{ name: obj.name, age: obj.age,}700k ops/s 75m ops/s

Abstraction === cost let obj = { name: 'alex', work: { name: 'Namshi' } }

_.get(obj, 'work.name', null)

Abstraction === cost let obj = { name: 'alex', work: { name: 'Namshi' } }

_.get(obj, 'work.name', null)

1.3m ops/s

Abstraction === cost let obj = { name: 'alex', work: { name: 'Namshi' } }

let work = null;

if (obj && obj.work && obj.work.name) { work = obj.work.name }

Abstraction === cost let obj = { name: 'alex', work: { name: 'Namshi' } }

let work = null;

if (obj && obj.work && obj.work.name) { work = obj.work.name }

70m ops/s

!Results may vary

let attributes = [‘price’,‘name’,‘description’,‘url’

]

………

function sanitize(products) {return products.map(p => {

return _.pick(p, attributes) })}

let attributes = [‘price’,‘name’,‘description’,‘url’

]

………

function sanitize(products) {return products.map(p => {

return _.pick(p, attributes) })}

let attributes = [‘price’,‘name’,‘description’,‘url’

]

………

function sanitize(products) {return products.map(p => {

return _.pick(p, attributes) })}

let attributes = [‘price’,‘name’,‘description’,‘url’

]

………

function sanitize(products) {return products.map(p => {

return _.pick(p, attributes) })} 5ms / req

let attributes = [‘price’,‘name’,‘description’,‘url’

]

………

function sanitize(products) {return products.map(p => {

return _.pick(p, attributes) })} 10% / req

const _ = require(‘lodash’)

const _ = require(‘lodash’)

const pick = require(‘lodash/pick’)

articles.filter(a => a.active).map(a => {a.title = titleCase(a.title)

return a})

articles.filter(a => a.active).map(a => {a.title = titleCase(a.title)

return a})

articles.filter(a => a.active).map(a => {a.title = titleCase(a.title)

return a})

articles.filter(a => a.active).map(a => {a.title = titleCase(a.title)

return a})

articles.filter(a => a.active).map(a => {a.title = titleCase(a.title)

return a})

700k ops/s

articles.reduce((acc, a) => {if (a.active) {a.title = titlecase(a.title)acc.push(a)

}

return acc})

articles.reduce((acc, a) => {if (a.active) {a.title = titlecase(a.title)acc.push(a)

}

return acc}) 4M ops/s

sprites?

No thanks,I do http/2

300kb 500kb 200kb

The old www

The old www

300kb 500kb

The old www

300kb 500kb 800kb / tot2 conns

The old www

sprites

sprites

1mb / tot1 conn

sprites

http/2

http/2

800kb / tot1 conn

http/2

react?

“No sauce please”

“No sauce please”

react-lite 25kb

preact 3kb

css animations

css animations

preconnect

<html class="en"><head>

<link preconnect="https://a.namshicdn.com" crossorigin />

preconnect

<html class="en"><head>

<link preconnect="https://a.namshicdn.com" crossorigin />

preconnect

<html class="en"><head>

<link preconnect="https://a.namshicdn.com" crossorigin />Initiate

connections

ASAP

preload<link

rel="preload" href="https://mycdn.com/fonts.css" as="style" onload="

this.rel='stylesheet';this.className='font-loaded'

"/>

preload<link

rel="preload" href="https://mycdn.com/fonts.css" as="style" onload="

this.rel='stylesheet';this.className='font-loaded'

"/>

preload<link

rel="preload" href="https://mycdn.com/fonts.css" as="style" onload="

this.rel='stylesheet';this.className='font-loaded'

"/>

preload<link

rel="preload" href="https://mycdn.com/fonts.css" as="style" onload="

this.rel='stylesheet';this.className='font-loaded'

"/>

preload<link

rel="preload" href="https://mycdn.com/fonts.css" as="style" onload="

this.rel='stylesheet';this.className='font-loaded'

"/>

preload<link

rel="preload" href="https://mycdn.com/fonts.css" as="style" onload="

this.rel='stylesheet';this.className='font-loaded'

"/>

Async

Non-blocking

CSS

preload

prerender window.addEventListener('load', function(){ var preRenderLink = doc.createElement('link'); preRenderLink.rel='prerender'; preRenderLink.href= '{{ nextPage }}'; document.head.appendChild(preRenderLink); });

prerender window.addEventListener('load', function(){ var preRenderLink = doc.createElement('link'); preRenderLink.rel='prerender'; preRenderLink.href= '{{ nextPage }}'; document.head.appendChild(preRenderLink); });

prerender window.addEventListener('load', function(){ var preRenderLink = doc.createElement('link'); preRenderLink.rel='prerender'; preRenderLink.href= '{{ nextPage }}'; document.head.appendChild(preRenderLink); });

prerender window.addEventListener('load', function(){ var preRenderLink = doc.createElement('link'); preRenderLink.rel='prerender'; preRenderLink.href= '{{ nextPage }}'; document.head.appendChild(preRenderLink); });

prerender window.addEventListener('load', function(){ var preRenderLink = doc.createElement('link'); preRenderLink.rel='prerender'; preRenderLink.href= '{{ nextPage }}'; document.head.appendChild(preRenderLink); });

prerender

SPLIT ASSETS

SPLIT ASSETS

SPLIT ASSETS

index.html

detail.html

Less bandwidth, good cache rate

Less bandwidth, good cache rate

7 req~100kb

Less bandwidth, good cache rate

9 req~150kb

Less bandwidth, good cache rate

13 req~380kb

RESULTS ?

-60%AVG document content loaded time

1.93 vs 4.84

-14%Bounce rate

+40%AVG session duration

+30%Conversion rate

LookingForward

to...

Not available in the browserhttps://github.com/grpc/grpc/issues/8682

Under developmenthttps://firebase.google.com/docs/cloud-messaging/

Under developmenthttps://githubengineering.com/githubs-post-csp-journey/

Old, abandoned ideahttps://youtu.be/v0xRTEf-ytE?t=16m25s

Still far, far awayhttps://github.com/jakearchibald/navigation-transitions

“Hot” is overrated

NERD ADVICE

“Hot” is overratedhttps://jakearchibald.com/2016/caching-best-practices/

“As you can see, you can hack around poor caching in your service worker, but you're way better off fixing the root of the problem. Getting your caching right makes things easier in service worker land, but also benefits browsers that don't support service worker (Safari, IE/Edge), and lets you get the most out of your CDN.”

https://jakearchibald.com/2016/caching-best-practices/

NERD ADVICE

“Solve problems on the right layer”https://www.ampproject.org/learn/amp-design-principles/

NERD ADVICE

“Solve problems on the right layer”https://www.ampproject.org/learn/amp-design-principles/

NERD ADVICE

Servers can still be pretty darn fast.

NERD ADVICE

NERD ADVICE

Servers can still be pretty fast

NERD ADVICE

Servers can still be pretty fast

NERD ADVICE

Servers can still be pretty fast

NERD ADVICE

50% within <30ms

95% within <120ms

Take this any day

Measure $ wiselyhttp://250bpm.com/blog:86

NERD ADVICE

NERD ADVICEAct upon what moves the needlehttps://en.wikipedia.org/wiki/Pareto_principle

Alessandro Nadalin

Alessandro Nadalin

@_odino_

Alessandro Nadalin

@_odino_

Namshi

Alessandro Nadalin

@_odino_

Namshi

CTO

Alessandro Nadalin

@_odino_

Namshi

CTO

odino.org

Thanks!Alessandro Nadalin

@_odino_

Namshi

CTO

odino.org

joind.in/talk/9e16d

Alessandro Nadalin

@_odino_

Namshi

CTO

odino.org

we are hiring!tech.namshi.com/join-us

github.com/namshi

twitter.com/TechNamshi

tech.namshi.com

top related