better php-frontend integration with tungsten.js

56
Boston PHP, August 2015 Better PHPFrontend Integration With Tungsten.js

Upload: andrew-rota

Post on 16-Apr-2017

600 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Better PHP-Frontend Integration with Tungsten.js

Boston PHP, August 2015    

Better PHP­FrontendIntegration With

Tungsten.js

Page 2: Better PHP-Frontend Integration with Tungsten.js

Let's get started!

Page 3: Better PHP-Frontend Integration with Tungsten.js

Who Are We

Andrew Rota@andrewrota  

Matt DeGennaro@thedeeg

Page 4: Better PHP-Frontend Integration with Tungsten.js

Wayfair

Wayfair.com is an online destination for all thingshome

Selection of more than seven million home items from 7,000+suppliers

eCommerce company since 2002Several large websites running a shared codebasePHP backend for customer­facing pages

2,000,000+ lines of code for custom eCommerce platform2,000+ JavaScript modules

Page 5: Better PHP-Frontend Integration with Tungsten.js

Divisions in Web Applications

Server Client

Page 6: Better PHP-Frontend Integration with Tungsten.js

...But there are overlaps

Server ClientView Layer

Page 7: Better PHP-Frontend Integration with Tungsten.js

What Comprisesthe View Layer

Page 8: Better PHP-Frontend Integration with Tungsten.js

Constants

MarkupSome data stored somewhere

Page 9: Better PHP-Frontend Integration with Tungsten.js

Client­SideInteraction is

Hard

Page 10: Better PHP-Frontend Integration with Tungsten.js

The "jQuery"Approach

Page 11: Better PHP-Frontend Integration with Tungsten.js

Manual UI Management

JS is manually crafted to match the HTMLEvent listeners modify the DOM as neededUsually uses jQuery to smooth browser differences

Can be the fastest codeMaintenance nightmare for larger sitesDevelopers need to worry about low­levelperformance

+­­

Page 12: Better PHP-Frontend Integration with Tungsten.js

Maintenance Nightmare?

<div>  <div class="clickable"></div></div>

$('.clickable').on('click', function() {  $(this).parent().addClass('highlighted');});

Page 13: Better PHP-Frontend Integration with Tungsten.js

Maintenance Nightmare?

<div>  <div class="fancy‐border">    <div class="clickable"></div>  </div></div>

$('.clickable').on('click', function() {  $(this).parent().parent().addClass('highlighted');});

Page 14: Better PHP-Frontend Integration with Tungsten.js

Maintenance Nightmare?

<div class="highlightable">  <div class="fancy‐border">    <div class="clickable"></div>  </div></div>

<div class="highlightable">  <div class="clickable"></div></div>

$('.clickable').on('click', function() {  $(this).closest('.highlightable').addClass('highlighted');});

Page 15: Better PHP-Frontend Integration with Tungsten.js

Maintenance Nightmare?

<div class="highlightable" data‐highlight‐class="fancy‐highlighted"  <div class="fancy‐border">    <div class="clickable"></div>  </div></div>

<div class="highlightable" data‐highlight‐class="highlighted">  <div class="clickable"></div></div>

$('.clickable').on('click', function() {  var $highlightable = $(this).closest('.highlightable');  $highlightable.addClass($highlightable.data('highlightClass'));});

Page 16: Better PHP-Frontend Integration with Tungsten.js

Markup === Data

Page 17: Better PHP-Frontend Integration with Tungsten.js

The "Backbone"Approach

Page 18: Better PHP-Frontend Integration with Tungsten.js

Re­render constantly

Data is managed in JSTemplate renders to StringData + Template = innerHTML

Really simple to implementRepaint/relayout on each renderTouching DOM on every render

+­­

Page 19: Better PHP-Frontend Integration with Tungsten.js

DOM is Slow

Hundreds of propertiesHidden side effects

Page 20: Better PHP-Frontend Integration with Tungsten.js

The "VirtualDOM" Approach

Page 21: Better PHP-Frontend Integration with Tungsten.js

Described Markup

   

Page 22: Better PHP-Frontend Integration with Tungsten.js

Simplified Code

Turns imperative code into declarative code

<div class="{{#clicked}}fancy‐highlighted{{/clicked}}">  <div class="fancy‐border">    <div class="clickable"></div>  </div></div>

<div class="{{#clicked}}highlighted{{/clicked}}">  <div class="clickable"></div></div>

Page 23: Better PHP-Frontend Integration with Tungsten.js

Declarative Code

State + Template is a consistent outcomeSo by managing State rather than the page...

We can render the page from the server at any stateJS failures can fall back to forms so the server can update state

Markup is owned by one locationNo more "where did this class come from" discovery adventure time

Bugs can be reproduced by copying stateAnyone can copy model data and send to Devs for bug reports

Page 24: Better PHP-Frontend Integration with Tungsten.js

Isn't this thesame as

Backbone?

Page 25: Better PHP-Frontend Integration with Tungsten.js

Virtual DOM

"Re­render" constantly, but in­memoryOnly touch the DOM when necessary in a precisemanner (think scalpel vs sledgehammer)Dev's don't worry about DOM interaction

Page 26: Better PHP-Frontend Integration with Tungsten.js
Page 27: Better PHP-Frontend Integration with Tungsten.js

How does thiswork?

Page 28: Better PHP-Frontend Integration with Tungsten.js
Page 29: Better PHP-Frontend Integration with Tungsten.js

Vanilla DOM node creation Virtual DOM creation

Virtual DOM

document.createElement('div'); new VirtualNode('div');

  counterReset: ""  cssText: ""  cursor: ""  direction: ""  display: ""  dominantBaseline: ""  emptyCells: ""  enableBackground: ""  fill: ""  fillOpacity: ""  fillRule: ""

tagName: "DIV"properties: {}children: []count: 0

Page 30: Better PHP-Frontend Integration with Tungsten.js

Virtual DOM vs DOM

Page 31: Better PHP-Frontend Integration with Tungsten.js

Lifecycle of a Render ­ Diffing

{  "tagName": "INPUT",  "properties": {    "id": "toggle‐all",    "className": "js‐toggle‐all",    "type": "checkbox"  },  "children": [],  "count": 0}

{  "tagName": "INPUT",  "properties": {    "id": "toggle‐all",    "className": "js‐toggle‐all",    "type": "checkbox",    "checked": true  },  "children": [],  "count": 0}

Page 32: Better PHP-Frontend Integration with Tungsten.js

Lifecycle of a Render ­ Patching

Diff creates a "Patch" objectSmallest set of operations it could detect to updatethe DOM

{  "0": { // Index of the element to patch    "type": 4,  // type of patch operation, in this case 'update properties'    "patch": {  // Properties to update, in this case, update 'checked' to true      "checked": true    }  }}

Page 33: Better PHP-Frontend Integration with Tungsten.js

Lifecycle of a Render ­ Applying Changes

Iterate over Virtual Tree and attached DOM nodeVtree avoid iterating on unchanged DOMWhen changed node is found, apply changes

Page 34: Better PHP-Frontend Integration with Tungsten.js

DOM­Free View Abstraction

State + Template = ViewCan use same abstraction across platformsCreate markup for server­side renderingCreate native UI for app rendering

Page 35: Better PHP-Frontend Integration with Tungsten.js

Wayfair'sTransition

Page 36: Better PHP-Frontend Integration with Tungsten.js

Why were we looking for a new solution?

Our codebase had a hybrid of the jQuery andBackbone approachesDebugging was hardUnnecessary DOM selection / manipulationInteractive pages could become janky

Page 37: Better PHP-Frontend Integration with Tungsten.js

What we needed

PerformanceThe driving force

Stack CohesionFirst­class server­side rendering

Page 38: Better PHP-Frontend Integration with Tungsten.js

Our Stack

Mustache TemplatesC++ on server, precompiled for client

ServerCustom PHP MVCFramework

ClientjQuery / AMDBackbone on Mobile

Page 39: Better PHP-Frontend Integration with Tungsten.js

Looked at Common Frameworks...

   

Page 40: Better PHP-Frontend Integration with Tungsten.js

..But nothing quite fit

Most common stack was:

Servernode.js / io.jsIsomorphic JSframeworkNo first­class server­side rendering

ClientSame isomorphicJS framework

Page 41: Better PHP-Frontend Integration with Tungsten.js

So...

Required JS on the serverServer­side rendering was either

Much slower than our templatesFully unsupported

­  , 

Funny  story  about  server  rendering:  itwasn't actually designed that way“

Sebastian Markbåge React.js Conf

Page 42: Better PHP-Frontend Integration with Tungsten.js

Why is server­side rendering important?

Perceived time to loadSEOBrowser/User SupportLink previews for social mediaActual time to loadJS is not single point of failure

Page 43: Better PHP-Frontend Integration with Tungsten.js

So What Did WeDo?

Page 44: Better PHP-Frontend Integration with Tungsten.js

We wrote a new framework

Page 45: Better PHP-Frontend Integration with Tungsten.js
Page 46: Better PHP-Frontend Integration with Tungsten.js

Tungsten.js

JS framework with high­performance renderingDesigned to work with a portable template language

Server agnostic

Attaches to server­rendered HTML and addsfunctionality

Page 47: Better PHP-Frontend Integration with Tungsten.js

Application Logic

DOM Manipulation

Templates

Page 48: Better PHP-Frontend Integration with Tungsten.js

ServerIntegration

Page 49: Better PHP-Frontend Integration with Tungsten.js

Our Server Framework

Custom MVC framework using Mustache templatesfor View renderingPage load triggers Controller actionController uses load Models for dataModels call DAOs as necessaryData is passed to View layer

Page 50: Better PHP-Frontend Integration with Tungsten.js

Tungsten + PHP Integration ­ Server

View prepares template data and renders HTMLWraps HTML with a specific ID so we can attach client­side

Serializes data to JSON and adds data tonamespaced JS variableView has reference to the JS View and Model

Bootstraps view, model, and precompiled template into ourJS loader

Page 51: Better PHP-Frontend Integration with Tungsten.js

Tungsten + PHP Integration ­ Output

<div id="AppView1"><!‐‐ Markup from AppTemplate.mustache ‐‐></div>

<script src="AppView‐AppModel‐AppTemplate.js"></script>

var tungstenData = {  App1: {    view: "AppView",    model: "AppModel",    elem_id: "AppView1",    template: "AppTemplate",    data: {...}  }};

Page 52: Better PHP-Frontend Integration with Tungsten.js

Tungsten + PHP Integration ­ Client

JS factory function reads variable and constructs JSViews over the rendered HTMLSince data comes from the server, rendered HTMLwill matchDOM Events are bound, adding interaction

Page 53: Better PHP-Frontend Integration with Tungsten.js

Advantages

Zero DOM manipulation on page loadCentralized Data Store

Easier to reason about data­flowSerializable state for debugging

Server can render at any stateMulti­page applications can use shared routes

Page 54: Better PHP-Frontend Integration with Tungsten.js

DEMO

Page 55: Better PHP-Frontend Integration with Tungsten.js

Coming Soon

Abstracting template from Mustache to allow moretemplate languagesComponentization to avoid all­in­one templatesBetter client­side data management

Page 56: Better PHP-Frontend Integration with Tungsten.js

Fork us at Driven by feature requests, so let us know what you'dlike to see

github.com/wayfair/tungstenjs