Writing a frontend framework
purpose & fundamentals
Federico Pereiro
Start with why
Two heroes under stress
Two heroes under stress
1) Starts with energy
2) Faces challenges
as chosen
Two heroes under stress
1) Starts with dread
2) Faces challenges
as imposed
Tools make all the difference
Simple, not easy
Purpose:
write frontends feeling like Rocky, not like Homer
Moving onto how
How can I write a modern web app without a framework?
What is a modern web app anyway?
Web page
Web application
Web page
- Sending or receiving info requires a page refresh.
- The page is rendered once.
Web app
- Background communication with server.
- The page must be updated.
Web page
- Relies on connectivity.
- Fetches the whole thing.
Web app
- Offline ability.
- Fetches just the data.
A horrible solution
// server code
http.get ('/update', function (req, res) {
response.end ('<li>New item</li>');
})
// client code
ajax ('get', '/update', function (error, data) {
$ ('ul.list').append (data);
});
Better, but still bad
// server code
http.get ('/update', function (req, res) {
response.end (['New item']);
})
// client code
ajax ('get', '/update', function (error, data) {
JSON.parse (data).map (function (item) {
$ ('.table').append ('<li>' + item + '</li>');
});
});
Don't try this at home
// client code
function makeTag (tag, className, value, content) {
var o = '<' + tag;
o += ' class="' + className + '"';
o += ' value="' + value + '">';
o += content;
o += '</' + tag + '>';
return o;
}
Object literals!
<p>Hello!</p>
[‘p’, ‘Hello!’]
Object literals!
<p class=”nice”></p>
[‘p’, {class: ‘nice’}]
Object literals!
<div>
<p class=”nice”>Hello</p>
</div>
['div', [
[‘p’, {class: ‘nice’}, 'Hello']
]]
Object literals!
Conditional #1: cond ? [‘p’, ‘Hello!’] : []
Conditional #2: [‘p’, {class: cond ? ‘h2’ : ‘’}, ‘Hello!’]
Iteration:
[‘table’, ITEMS.map (function (item) {
return [‘tr’, [‘td’, item]];
})]
Object literals!
// client code
ajax ('get', '/update', function (error, data) {
var items = JSON.parse (data).map (function (item) {
return ['li', item];
});
$ ('ul.list').append (makeHTML (items));
});
I'm free!
Not so fast...
A simple application
Dependencies!
(Re)load:
- Affects everything.
- Must preserve user-provided amounts.
- Must preserve cart.
Filter:
- Affects counter of matching elements.
- Affects display of matching elements.
- Must preserve hidden elements.
Dependencies!
Add to cart:
- Relies on whether product is in cart already.
- Relies on amount entered in box.
- Affects cart item count, total & actual items.
Remove from cart:
- Affects cart item count, total & actual items.
Dependencies!
Submit cart:
- Get all cart data.
- Submit it.
- Clean up cart.
Imperative
reload (data) {
updateItems ();
updateCart ();
}
changeFilter () {
var filter = $ ('#filter').value;
updateItems ();
}
Imperative
addToCart (index) {
var q = $ ('#amount-' + index);
var p = ... // get matching product
updateCart ();
}
removeFromCart (index) {
var p = ... // get matching cart entry
updateCart ();
}
Imperative
updateItems () {
// Update counters
// Go through each of the DOM elements
// Hide/unhide it if required
// Update it if it has changed
// Remove it if it was deleted
// Add it if it is new
}
Imperative
updateCart () {
// Update counter & total
// Go through each of the DOM elements
// Hide/unhide it if required
// Update it if it has changed
// Remove it if it was deleted
// Add it if it is new
}
Every new feature must accomodate and be accomodated by the other features
O ( n )
2
Imperative with store
var Store = { items: [], cart: [], filter: '' }; ['input', {oninput: 'Store.filter = this.value; redraw ()'}];
Imperative with store
function redraw () {
// redraws entire view
}
Reactive views & store
view ('items', function (items) {
return view ('filter', function (filter) {
return items.map (function (item) {
if (item.match (filter)) return ['td', item];
});
});
});
Events & store
do ('set', 'filter', 'kitties');
do ('set', 'items', [
{title: 'kitties', price: 20},
{title: 'sunshine', price: 30}
]);
do ('add', 'items', {title: 'New!', price: 1000});
do ('rem', 'items', 0);
The big three
verb
path
arguments
Mind the path
do ('set', ['items', 0, title], 'kitties & sunshine');
Where's the verb?
view ('items', function (items) {
return view ('filter', function (filter) {
return items.map (function (item) {
if (item.match (filter)) return ['td', item];
});
});
});
Where's the verb?
add
set
rem
change
Implicitly...
view ('change', 'items', function (items) {
return view ('change', 'filter', function (filter) {
return items.map (function (item) {
if (item.match (filter)) return ['td', item];
});
});
});
Summary of fundamentals
- Object literals for templating
- Events with verb + path
- Global store updated by events
- View is a function of the store, triggered by an event
O ( n )
github.com/fpereiro/gotoB
Questions!
Questions?
Thank you!
fpereiro@gmail.com
Writing a frontend framework
By Fede Pereiro
Writing a frontend framework
Why and how I ended up writing a frontend framework
- 1,044