Better PHP-Frontend Integration with Tungsten.js

Post on 16-Apr-2017

591 views 0 download

Transcript of Better PHP-Frontend Integration with Tungsten.js

Boston PHP, August 2015    

Better PHP­FrontendIntegration With

Tungsten.js

Let's get started!

Who Are We

Andrew Rota@andrewrota  

Matt DeGennaro@thedeeg

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

Divisions in Web Applications

Server Client

...But there are overlaps

Server ClientView Layer

What Comprisesthe View Layer

Constants

MarkupSome data stored somewhere

Client­SideInteraction is

Hard

The "jQuery"Approach

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

+­­

Maintenance Nightmare?

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

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

Maintenance Nightmare?

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

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

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');});

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'));});

Markup === Data

The "Backbone"Approach

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

+­­

DOM is Slow

Hundreds of propertiesHidden side effects

The "VirtualDOM" Approach

Described Markup

   

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>

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

Isn't this thesame as

Backbone?

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

How does thiswork?

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

Virtual DOM vs DOM

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}

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    }  }}

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

DOM­Free View Abstraction

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

Wayfair'sTransition

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

What we needed

PerformanceThe driving force

Stack CohesionFirst­class server­side rendering

Our Stack

Mustache TemplatesC++ on server, precompiled for client

ServerCustom PHP MVCFramework

ClientjQuery / AMDBackbone on Mobile

Looked at Common Frameworks...

   

..But nothing quite fit

Most common stack was:

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

ClientSame isomorphicJS framework

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

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

So What Did WeDo?

We wrote a new framework

Tungsten.js

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

Server agnostic

Attaches to server­rendered HTML and addsfunctionality

Application Logic

DOM Manipulation

Templates

ServerIntegration

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

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

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: {...}  }};

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

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

DEMO

Coming Soon

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

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

github.com/wayfair/tungstenjs