unlucioSenior Software Engineer - Namshi
Dive Master - PADI Remote Nerd - WEBdeBS
Back to the future:
Isomorphic javascript applications
An isomorphic javascript application:
What is an “isomorphic application”
Why did we need one
How did we build it @ Namshi:
Requirements
Choices
What did we actually do
Isomorphism
Isomorphism is a very general concept that appears in several areas of mathematics. The word derives from the Greek iso, meaning
"equal," and morphosis, meaning "to form" or "to shape.”
wolfram.com
http://mathworld.wolfram.com/Isomorphism.html
Isomorphic application
It’s a dynamic website capable of generating its html on both server and client side, virtually using the same
exact code in both environments
and a websiteis a bunch of nicely presented html
with an interactive layer on top
to be isomorphicwe need to stop at the html
take a note for future self!
Why do we need one?
Why do we need one?Robots: search engines don’t cope with SPAs
Why do we need one?Robots: search engines don’t cope with SPAs
Loading speed
Why do we need one?Robots: search engines don’t cope with SPAs
Loading speedJavascript doesn’t “fail safe”
@ Namshi…
We had to turn this…
in to this
See the difference?
SPA Page Isomorphic Page
SPA Page Isomorphic Page~190Kb js Target 100Kb js
SPA Page Isomorphic Page~190Kb js Target 100Kb js
~ 40p page speed at least 90p page speed
SPA Page Isomorphic Page~190Kb js Target 100Kb js
~ 40p page speed at least 90p page speed
Angular.js ?????????
?????????
?????????yup, we had to make a choice
We’re already using it on our SPAs
We’re already using it on our SPAsWell known dependency injection
We’re already using it on our SPAs
Well known dependency injection
Data bindings
We’re already using it on our SPAs
Well known dependency injection
Data bindings
Jade (and we like it)
It’s ok but…
Making it isomorphic needs
quite a bit of tooling around
New and fashionable…
New and fashionable… …we’re eager to try it :)
New and fashionable… …we’re eager to try it :)
The virtual DOM looks amazing
New and fashionable… …we’re eager to try it :)
The virtual DOM looks amazingFlux (once you get it) seems quite a good idea
New and fashionable… …we’re eager to try it :)
The virtual DOM looks amazingFlux (once you get it) seems quite a good idea
JSX…
New and fashionable… …we’re eager to try it :)
The virtual DOM looks amazingFlux (once you get it) seems quite a good idea
JSX…
… but components are a nice idea too!
New and fashionable… …we’re eager to try it :)
The virtual DOM looks amazingFlux (once you get it) seems quite a good idea
JSX…
… but components are a nice idea too!and The Internet says it can be isomorphic too!
Let’s try it! :D
Examples and suggested techniques seems been quite complex, with lots of structure and quite
opinionated
expectations slightly not met :’(
Yup, we’re back to square one…
Yup, we’re back to square one…
ok… let’s try getting what we like…
ok… let’s try getting what we like…
data bindings jade
flux components
flux capacitor
So, how does it work?
So, how does it work?Step 1
Understand the view
Data Content
TemplateInteraction
So, how does it work?Step 2
Strip the interaction layer
Data Content
TemplateInteraction
Remember our note: stop at the html.
So, how does it work?What do we get form the api:
Data Content
{ data: [], meta: {}, search: {} }
{ data: “”, meta: {}, search: {} }
So, how does it work?What do we get form the api:
{ data: “”, meta: {}, search: {} }
This guy is html!
So, how does it work?Choose a template engine
TemplateJade:
our current angular ones
Nunjucks: renders on client and server
let’s bring backwhat we liked about React
Flux
FluxOur data source: API
FluxOur data source: API
State: the URL
FluxOur data source: API
State: the URLAdd a little dispatcher to notify for data update
Show me the code!
Show me the code!Server entry point
var app = require('express')(); var tpl = require(‘../shared/tpl'); var url = require('url');
app.get('*', function(req, res) { var path = url.parse(req.url).pathname; api.getData(path).then(function(data) { var html = tpl.render('main.html', data); res.end(html); }); });
Show me the code!And it’s client counterpart
var page = require('page');
page('*', function (ctx) { var path = url.parse(ctx.path).pathname;
dispatcher.broadcast('URL_CHANGE', {url: path}); api.getData(path).then(function (data) { dispatcher.broadcast('DATA_UPDATE', data); }); });
page.start();
Components
ComponentsActivate on state update
ComponentsActivate on state update
Receive data
ComponentsActivate on state update
Receive dataTransform them if needed
ComponentsActivate on state update
Receive dataTransform them if needed
Delegate to templates for final html generation
Show me the code!
Show me the code!Title Component
var tpl = require('../tpl'); var vpo = require('vpo');
module.exports = function(data) { return tpl.render('title.html', { text: vpo.get(data, 'meta.seo.pageTitle') || vpo.get(data, 'title') || 'Namshi.com' }); };
Show me the code!Title Template
<title data-iso-component="title">{{ text }}</title>
Hey but what about my interaction?
Hey but what about my interaction?
well, according to legend: turns out that back in the 90s humans were able to use websites simply clicking on links and
without fancy UI wow-effects
Hey but what about my interaction?
Interaction We call them: widgets
they react to UI changes
they attach to iso-components
interact with data-nm-*
and use VUE.js
Show me the code!
Show me the code!var dispatcher = require(‘../../shared/dispatcher'); var menu = require('./menu'); var cart = require('../services/cart');
module.exports = function($dom) { $dom.on('click', '#site_menu .search_trigger', function(e) { dispatcher.broadcast(‘TOGGLE_SEARCH’); e.preventDefault(); });
this.bind($dom, {data: { menu: menu, cart: cart }}); };
Show me the code!<div id="site_header" data-iso-component="header" data-nm-class="navigation_open: menu.shiftContent"> <ul id="site_menu"> <li><a href="" class="search_trigger"> <i>{{ 'search' | translate }}</i></a> </li> <li><a href="/cart/" class="cart_overview"> <span data-nm-text="cart.itemsCount">0</span></a> </li> </ul> </div>
did we meet our target?
did we meet our target?Isomorphism: yes :)
did we meet our target?Isomorphism: yes :)
Size: 162kb (including templates)
did we meet our target?Isomorphism: yes :)
Size: 162kb (including templates)
Page speed…
Page speed…
node.js Browserify
Gulp Nunjucks Vanilla JS express.js
VUE.js Docker
Thank you for listening
Thank you for listening