+ All Categories
Home > Technology > Gitter marionette deck

Gitter marionette deck

Date post: 11-Aug-2015
Category:
Upload: mike-bartlett
View: 173 times
Download: 1 times
Share this document with a friend
51
Transcript
Page 1: Gitter marionette deck
Page 2: Gitter marionette deck

Who are these dudes?

suprememoocow

mydigitalself

Andrew Newdigate

Mike Bartlett

Page 3: Gitter marionette deck

Gitter is where developers come to talk

120 000 registered users

24 000 public communities

Active every minute of every day

502 releases in 1.5 years

Page 4: Gitter marionette deck

Zawinksi’s Law

Every program attempts to expand until it can read mail.

Those programs which cannot so expand are replaced by ones which can.

Page 5: Gitter marionette deck

Act 1

Performance

Page 6: Gitter marionette deck

Global Average

4.5Mbps

Akamai

Page 7: Gitter marionette deck

Two Great ToolsOSX Network Link Conditioner

Chrome DevTools Network Throttle

Page 8: Gitter marionette deck

Scene 1

Mistake: Assuming jQuery is fast enough

Page 9: Gitter marionette deck

Chuck Norris’ keyboard

Page 10: Gitter marionette deck

Finding performance problems

Don’t optimise too early

Focus on CollectionViews, CompositeViews

Improving the performance 1000% on a view that gets rendered once in the application isn’t going to make the slightest bit of difference.

Page 11: Gitter marionette deck

The Chrome DevTools Timeline is Awesome

Page 12: Gitter marionette deck

Example of using Timelines$.tooltip is really slow

Solution: change the tooltip behaviour to only initialise the tooltip on the first mouseover event fired on the element

1ms per tooltip to 0.005ms per tooltip

Page 13: Gitter marionette deck

Easy performance win: attachElContentOverride this method in your collection/composite child views

// Abbreviated version of the attachElContent MixIn we use on Gitter attachElContent: function(html) { if (typeof html === 'string') { this.el.innerHTML = html; return this; } this.$el.html(html); return this; }

Page 14: Gitter marionette deck

.innerHTML vs $.html

Page 15: Gitter marionette deck

Scene 2

Mistake: Not pre-rendering content

Page 16: Gitter marionette deck

Pre-rendering is good practicePage indexing / SEO advantages to doing it

Perceived speed of page load is much faster

Avoid multiple reflows as the application loads

Less jankiness

Page 17: Gitter marionette deck

Pre-rendering is messy

At the moment, done through a series of hacks:

• Server-side handlebars helpers

• Client-side Marionette extensions

Would be awesome to move this out into a semi-sane, open-source library (or build it into Marionette!)

Page 18: Gitter marionette deck

Fully pre-rendered Partially pre-rendered

Page 19: Gitter marionette deck

Isomorphic LayoutViewsIn LayoutView’s before:show event

• If the region is empty, initialise ChildView as per normal:

• If the region already contains content, mount the ChildView on the existing element: view.showChildView(regionName, new ChildView({ el: existingElement, template: false, ... options ... }));

view.showChildView(regionName, new ChildView({ ... options ... }));

Page 20: Gitter marionette deck

Isomorphic CollectionViews

childViewOptions: function (model) { if (!model.id) return; var el = this.$el.find('> [data-id="' + model.id + '"]')[0]; if (!el) return; return { el: el, template: false };},

<ul> <li data-id="1">One</li> <li data-id="2">Two</li> <li data-id="3">Three</li></ul>

collection.reset([ { id: “1”, name: “One” }, { id: “2”, name: “Two” }, { id: “3”, name: “Three” }]);

Page 21: Gitter marionette deck

Scene 3

Mistake: Too much Jason

Page 22: Gitter marionette deck

“640 people ought to be enough for any room”

Page 23: Gitter marionette deck

Thanks!

Page 24: Gitter marionette deck

People Roster Data

~300 characters

In a 5000 user room, that’s 1.4MB of JSON

Retina and non-retina avatar URLs

Unused fields, duplicate data, etc

{ "id": "5298e2d5ed5ab0b3bf04c980", "username": "suprememoocow", "displayName": "Andrew Newdigate", "url": "/suprememoocow", "avatarUrlSmall": "https://avatars1.githubusercontent.com/suprememoocow?v=3&s=60", "avatarUrlMedium": "https://avatars1.githubusercontent.com/suprememoocow?v=3&s=128", "gv": "3", "v": 30}

Page 25: Gitter marionette deck

How we represent them now

77 characters

In a 5000 user room, that’s still 375KB.

Limit the list to the first 20 people

{ "id": "5298e2d5ed5ab0b3bf04c980", "username": "suprememoocow", "gv": "3", "v": 30}

Page 26: Gitter marionette deck

Scene 4

Mistake: Using .on too much

Page 27: Gitter marionette deck

.on is a code smellUsing jquery events

Backbone events

Also, beware of long running setTimeouts

this.ui.actionButton.on('click', function() { window.alert('Yo'); });

this.model.on('change', function() { window.alert('Yo'); });

Page 28: Gitter marionette deck

Obvious solutionUse modelEvents, collectionEvents and events modelEvents: { 'change': 'onChange'},events: { 'click @ui.badge': 'onBadgeClicked'},collectionEvents: { 'add reset sync reset': 'showHideHeader'},

Use listenTo for listening to Backbone.Eventsthis.listenTo(model, 'change', function() { })

Page 29: Gitter marionette deck

When you still need .onRemember to cleanup after yourself

onClick: function() { this.$someElement.on('mouseenter', ...); this.longRunningTimer = setTimeout(function() {}, 60000);},

onDestroy: function() { this.$someElement.off(); clearTimeout(this.longRunningTimer);},

Page 30: Gitter marionette deck

DevTools Heap SnapshotsTake periodic snapshots and use the comparison view to find new allocations

Page 31: Gitter marionette deck

Act 2

Software design mistakes

Page 32: Gitter marionette deck

Scene 1

Mistake: Coupling view components together

Page 33: Gitter marionette deck

MV* 101

This is how we’re taught to structure MV* applications at school.

Page 34: Gitter marionette deck

Sometimes it’s easier to ignore the advice

We need to tell another view to do something.

We’re in a rush, so we’ll just wire the dependency in and fix it later. var MyView = Mn.ItemView.extend({ ... onActionClicked: function() { this.options.anotherView.doSomething(); }, })var myView = new MyView({ anotherView: anotherView });

Page 35: Gitter marionette deck

Pretty soon we’ve got a tightly coupled mess

Page 36: Gitter marionette deck

This makes change hardJust try to:

• Move a view within the view hierarchy

• Remove a view in a certain environment (unauthenticated view, mobile, etc)

Let’s change things around a bit…

Page 37: Gitter marionette deck

Marionette solutions

Use a shared model and update the model

wreqr, or better yet, Backbone.Radio

Page 38: Gitter marionette deck

Imaginary Radio Behaviourvar MyView = Mn.ItemView.extend({ behaviors: { Radio: { name: 'ui', comply: { 'chat:focus': 'focusChat' … }, focusChat: function() { // .... }});

var AnotherView = Mn.ItemView.extend({ behaviors: { Radio: { name: 'ui' }, }, onActionClicked: function() { this.radio.command('chat:focus'); }});

*correct spelling

Page 39: Gitter marionette deck

Scene 2

Mistake: Messing with another view’s DOM

Page 40: Gitter marionette deck

Quick and dirty

A component needs to respond to an action and change another component’s DOM…

Easiest solution: just use jquery

onClick: function() { $('#action-button').hide(); }

Page 41: Gitter marionette deck

c/c++ pointer arithmetic

In c/c++, it’s possible to use pointer arithmetic to directly modify the contents of a location in memory.

I’m sure you will all agree: this is a VERY BAD IDEA!

bptr = (byte*) &data;bptr = bptr + 5;iptr = (int*) bptr;(*iptr) = 0xcafebabe;

Page 42: Gitter marionette deck

Now imagine…

Your DOM is a global memory shared by all the Javascript code running in your app

Each view in your app manages a distinct piece of the global memory

Mutating another view’s DOM is a bit like using pointer arithmetic to change it’s memory behind it’s back

Don’t do it!

Page 43: Gitter marionette deck

But why?

Refactoring becomes a nightmare

You’re creating hidden connections between views in your application.

Page 44: Gitter marionette deck

Scene 3

Mistake: Different module formats on the client and server

Page 45: Gitter marionette deck

Then

Client: AMD modules with RequireJS

Tests: run in a phantomjs

Server: commonjs modules with nodejs

Tests run in nodejs with mocha

Page 46: Gitter marionette deck

Now

Client: commonjs modules with webpack

Server: commonjs modules with nodejs

Shared code is kept in shared

Shared code can be tested quickly using the nodejs and mocha, without having to start a phantomjs browser

Page 47: Gitter marionette deck

require.ensure();

// In your backbone router.... markdown: function() { require.ensure(['views/markdown/markdownView'], function(require) { var MarkdownView = require('views/markdown/markdownView'); appView.dialogRegion.show(new MarkdownView({})); }); },

Page 48: Gitter marionette deck

Act 3

In closing

Page 49: Gitter marionette deck

Code DebtA lot of these problems as the result of technical debt. When we started building the project we chose Backbone, and only later did we switch to Marionette.

Initially, we treated Marionette as a neat extension of Backbone, for things like CollectionViews etc so the transition was gradual and left a lot of technical debt around.

Marionette 2 PR

Page 50: Gitter marionette deck

From a small prototype to a large application

A lot of the pain we’ve experienced has been down to the fact that we started off with a small application which has grown larger and larger.

Start as you mean to go on

Page 51: Gitter marionette deck

Recommended