Better PHP-Frontend Integration with Tungsten.js
Post on 16-Apr-2017
591 Views
Preview:
Transcript
Boston PHP, August 2015
Better PHPFrontendIntegration 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 customerfacing 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
ClientSideInteraction 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 lowlevelperformance
+
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
Rerender 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
"Rerender" constantly, but inmemoryOnly 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
DOMFree View Abstraction
State + Template = ViewCan use same abstraction across platformsCreate markup for serverside 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 CohesionFirstclass serverside 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 firstclass serverside rendering
ClientSame isomorphicJS framework
So...
Required JS on the serverServerside 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 serverside 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 highperformance renderingDesigned to work with a portable template language
Server agnostic
Attaches to serverrendered 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 clientside
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 dataflowSerializable state for debugging
Server can render at any stateMultipage applications can use shared routes
DEMO
Coming Soon
Abstracting template from Mustache to allow moretemplate languagesComponentization to avoid allinone templatesBetter clientside data management
Fork us at Driven by feature requests, so let us know what you'dlike to see
github.com/wayfair/tungstenjs
top related