Programming Single Page Applications
Craig Austgen, Brian Capouch, Nathan Samano
Saint Joseph's College
Follow along live:
http://slides.com/capouch/deck-1/live
What, Why, How
What:
Instead of a steady flow of page request/response interaction, there is a one-time transfer of a control program (and its data) from the server to the browser.
The program then takes over the browser window and directs all communication with both the enduser and the backend server.
That program is typically called "the shell"
What is a SPA?
- "Home page/site" loads as a single page
- Commonly JavaScript on both client and server sides
- Regions and views instead of pages
- It has its own genericized manifesto
A different way of thinking
- Browser is a virtual app container
- Apps are intended to be long-lived
- URL routing on both client and server
Why:
"A SPA isn't like an application; a SPA is an application"
-- Michael Mikowski
Advantages of SPA
- App-like responsiveness ("fluidity")
- Less network-latency delay after page load
- Only one full-page render
- Offline capabilities
- Takes advantage of HTML5 features
Challenges
- Good ones are intensive, expensive
- Nav buttons are problematic
- What's a page view?
- Analytics, SEO, etc.
- Memory and namespace management
Frameworks abound
How:
Our sample repo
- Architecture review
- Websockets interface
- SEO concerns
Basic SPA Components
- Executive (shell)
- Feature modules render views in regions
- Aware back-end
- Model (data)
https://github.com/capouch/spa.git
git clone https://github.com/capouch/spa.git
cd spa
npm install
node app.js
Notes on repo
- Simplicity first
- No data-saving ability
- Cartoonishly simple
- Demonstrates basic parts
What is an SPA?
- Single Page Load
- Long-lived
- Like an application
- Last bullet
<!-- Let's get started -->
<script>
// Calling the toplevel initModule starts up the SPA
$(function () { spa.initModule( $('#spa') ); });
</script>
</head>
<body>
<main id="spa"></main>
</body>
</html>
index.html
The URL
The SPA takes full ownership of routing requests within its namespace, i.e. it owns all of the URL past the hostname
The shell
The shell is a command and control interpreter, named to suggest its analogy to the well-known OS shell
SPA controls all routing
- Server-side is done by server package (e.g. Express)
- #Hash-based client routing is common
- HTML5 History API pushstate() is the ascendant
app.js
// Watch a file and let clients know if/when one changes
setWatch = function ( url_path, file_type ) {
console.log( 'setWatch called on ' + url_path );
//
if ( ! watchMap[ url_path ] ) {
console.log( 'setting watch on ' + url_path );
// Part of Node's native 'fs' package
fsHandle.watchFile(
url_path.slice(1),
function ( current, previous ) {
console.log( 'file accessed' );
// Send notice
if ( current.mtime !== previous.mtime ) {
console.log( 'file changed: ' + url_path);
io.sockets.emit( file_type, url_path );
}
}
);
watchMap[ url_path ] = true;
}
};
app.js
// Process all traffic looking for "magic names"
app.use( function ( request, response, next ) {
// Watch for changes to /js/data.js or /css/sockstyle.css file
if ( request.url.indexOf( '/js/data.js' ) >= 0 ) {
setWatch( request.url, 'script' );
}
else if ( request.url.indexOf( '/css/sockstyle.css' ) >= 0 ) {
setWatch( request.url, 'stylesheet' );
}
next();
app.use( express.static( __dirname + '/' ) );
});
data.js
var dummyPage = '<b>Name: </b>Javier Flores'
+ '<br><b> Address: </b>101 Calle Mayor'
+ '<br><b> Age: </b>52';
spa.socket.js
+ '<script id="sock_js" src="/js/data.js"></script>'
spa.socket.js initModule()
// Display the value of var "dummyPage" from the data.js file
$(function () {
jqueryMap.$socketIO.html( dummyPage );
});
// Set event handler to react to "script" message
io.connect(serverURL).on('script', function (path) {
$( '#sock_js' ).remove();
// Replace contents of element with freshly-loaded value
jqueryMap.$container.append(
'<script id="sock_js" src="'
+ path +
'"></script>'
);
// Redisplay HTML with new JavaScript
jqueryMap.$socketIO.html( dummyPage );
});
// Set event handler to react to "stylesheet" message
io.connect(serverURL).on('stylesheet', function (path) {
// Get rid of current style
$( '#sock_css' ).remove();
// Replace contents of stylesheet with file from websocket
// Note which container you append to here is crucial
jqueryMap.$container.append(
'<link id="sock_css" rel="stylesheet" href="'
+ path +
'"/>'
);
// Redisplay HTML with new styling
jqueryMap.$socketIO.html( dummyPage );
});
toSpanish.sh
#!/bin/bash
cp js/intl/data.js-es js/data.js
cp css/intl/sockstyle.css-es css/sockstyle.css
data.js-es
// Spanish "version" of our SPA view
var dummyPage = '<b>Nombre: </b>Javier Flores'
+ '<br><b> Dirección: </b>101 Calle Mayor'
+ '<br><b> Edad: </b>52<br><b> Estado: </b> D.F.';
SEO and SPAs
- Issue
- Follow SEO guidelines
- Our Solution
Traditional Pages
- Multiple HTML pages
- Static HTML
SPA
- Single HTML Page
- Small amount of HTML in Body
Solution
- Prerender.io
curl http://localhost:8000
Traditional crawlers' view
of index.html
curl http://localhost:8000/?_escaped_fragment_=
SPA->Desktop App
Full App w/Electron
OSCON 2015
By capouch
OSCON 2015
All about the SPA
- 1,554