Vue.js

 Service workers going Realtime

#infoshare / Blake Newman / @blakenewman

Text

slides.com/blakenewman

Agenda:

Background and features of Vue.js

 

Reactivity Systems

 

Templates, are they bad?

 

Service Workers in Realtime

So, Vue.

Where did vue come from?

DOB: ~ July 2013

Birthname: Seed

Father: Evan You 

@youyuxi

> 25 team members

> 12 languages

~ 50 repos/plugins

> 160 backers

> 20 companies

> $10,000 monthly

Plus much more from alternative avenues

Financially viable OSS

> 110k  in organisation

http://githubstats.lip.is/

14th most starred organisation

http://githubstats.lip.is/

11th most starred repository

Vue.js (core)

http://githubstats.lip.is/

> 1.6 billion npm downloads

The
JavaScript Framework

Progressive

Like JQuery?


    <body>
      <div id="app">
        {{ message }}
      </div>
    
      <script src="https://unpkg.com/vue"></script>
      <script>
        new Vue({
          el: '#app',
          data: {
            message: 'Hello Vue!'
          }
        });
      </script>
    </body>

(ish)

Reactivity

Single File Components

Asynchronous Components

Dynamic Components

Hot Reloading

vue-router

Vuex

Virtual DOM

SSR

Devtools

Ava.js

Jest

Nuxt.js

Streaming

Pre-processors

JSX

Caching

Render Functions

Weex

Scoped CSS

CSS Modules

Beautiful Documenation

Transition Effects

Template Engine

Slots

Scoped Slots

Functional Components

TypeScript

Critical CSS

Provide & Inject

PWA

Single File Component

   
    <template>
      <p class="red">
        {{ message }}
      </p>
    </template>
    
    <script>
      module.exports = {
        data: function () {
          return {
            message: 'Hello world!'
          };
        }
      };
    </script>

    <style>
      .red {
        colour: red;
      }
    </style>

Pre-Processing

   
    <template lang="pug">
      p.red {{ message }}
    </template>
    
    <script lang="ts">
      import Vue from 'vue'
      import Component from 'vue-class-component'

      @Component
      export default class MyComponent extends Vue {
         message: string = 'Hello world!'
      }
    </script>

    <style lang="styl">
      .red
        colour red
    </style>

Scoped CSS

   
    <template>
      <p class="red"></p>
    </template>
    
    <style scoped>
      .red {
        colour: red;
      }
    </style>
    
    <p class="red" data-v-f3f3eg9></p>

    <style>
      .red[data-v-f3f3eg9] {
        color: red;
      }
    </style>

CSS Modules

   
    <template>
      <p :class="$style.red"></p>
    </template>
    
    <style module>
      .red {
        colour: red;
      }
    </style>
    
    <div class="_1VyoJ-uZOjlOxP7jWUy19_0"></div>

    <style>
      ._1VyoJ-uZOjlOxP7jWUy19_0 {
        color: red;
      }
    </style>

Hot reloading

Scaffolding Templates

https://github.com/vuejs-templates

CLI

https://github.com/vuejs/vue-cli

Reactivity

Pull Reactivity system

Push Reactivity system

Pull

Explicit pull signal from developer

 

Manual optimisation hints from developer

 

Simplified with immutable data

Push

Memory overhead, proportional to dataset size

 

Improved by skipping observation of immutable data

 

Improved by reducing computation granularity

Frameworks

Silver bullet

 

Angular 2 onPush with Observables
 

React + MobX


Vue 2 + Immutable data

Templates

 

Render Functions (JSX)

   
    <template>
      <p>{{ message }}</p>
    </template>
    
    function render() {
      var _vm = this
      var _h = _vm.$createElement
      var _c = _vm._self._c || _h
      return _c('p', [
       _vm._v(_vm._s(_vm.message))
      ])
    }

Representation


    function render() {
      return (
        <div>{ this.message }</div>
      )
    }

    {
      class: {
        foo: true,
        bar: false
      },
    
      style: {
        color: 'red',
        fontSize: '14px'
      },
    
      attrs: {
        id: 'foo'
      },
    
      props: {
        myProp: 'bar'
      },
    
      on: {
        click: this.clickHandler
      },
      
      // ...
    }

Virtual DOM Node

    
    function render() {
      return React.createElement(
        "div",
        null,
        this.items.map(function (item) {
          return React.createElement("p", null, item);
        }),
        React.createElement("p", null, this.message)
      )
    }

Normalisation


   function render() {
     return (
       <div>
         { this.items.map(item => <p>{ item }</p>) }
         <p>{ this.message }</p>
       </div>
     )
   }

    [node, [node, ...], node]
   
    <template>
      <div>
        <p v-for="item in items">{{ item }}</p>
        <p>{{ message }}</p>
      </div>
    </template>
    
    function render() {
      var _vm = this
      var _h = _vm.$createElement
      var _c = _vm._self._c || _h
      return _c('div', [_vm._l((_vm.items), function(item) {
        return _c('p', [_vm._v(_vm._s(item))])
      }), _c('p', [_vm._v(_vm._s(_vm.message))])], 2)
    }
   
    <template>
      <div>
        <span>Static content</span>
        <span>More static content</span>
      </div>
    </template>

Static nodes


    function render() {
      return (
        <div>
          <h2>Static content</h2>
          <p>More static content</p>
        </div>
      )
    }

    function render() {
      var _vm = this
      var _h = _vm.$createElement
      var _c = _vm._self._c || _h
      return _vm._m(0)
    }

    function render() {
      return React.createElement(
        "div",
        null,
        React.createElement("span", null, "Static content"),
        React.createElement("span", null, "More static content")
      )
    }

Further Optimisations

Render (JSX)

Templates

Declarative with JSX

Presentational

Logical

HTML + small DSL

Child normalization

Declarative

Power of JavaScript

No DSL?

Static node hoisting

Progressive code updates

Closer to compiler

Service Workers and going Realtime

Service Workers

Web Workers

Server

Service Worker

Cache

Client

WebSockets

WebSocket

Server

Client

Combined

Server

Service Worker

Client

WebSocket

WTF?!?

HELP

Client to server messaging system

Server

Service Worker

Cache

Client

WebSocket

Server

Service Worker

Cache

Client

WebSocket

    
    self.addEventListener('fetch', event => {
      const { request } = event;
      const url = new URL(request.url);
    
      // Ignore not GET request or is is different origin.
      if (request.method !== 'GET' || url.origin !== self.location.origin) return;
    
      event.respondWith(async () => {
        try {
          // If not online and cache available then use cache
          if (!navigator.onLine) {
            const cache = await caches.match(request);
            if (cache) return cache;
          }
    
          // Request message
          const response = await self.fetch(request);
          
          // If not 'ok' then do not cache
          if (!response.ok) return response;
    
          // Add response to cache
          const openCache = await caches.open(CACHE_NAME);
          await openCache.put(request, response.clone());
    
          return response;
        } catch (error) {
          // User is landing on a page.
          return request.mode === 'navigate' ? caches.match('./') : '';
        }
      });
    });

Server

Service Worker

Cache

Client

WebSocket

    
    const socketIO = require('socket.io');
    const express = require('express');
    
    const app = express();
    const io = socketIO(app);
    

    app.post('/api/user', () => {
      io.emit('USER_COUNT_UPDATED');
    });

    
    app.get('/api/user/count', (req, res) => {
      res.json({ count: 99283374 }).end();
    });

    

Server

Service Worker

Cache

Client

WebSocket

    
    import io from 'socket.io-client';
    
    export default store => {
      const socket = io.connect();
    
      socket.on('connect', () => store.dispatch('app/connect'));
      socket.on('disconnect', () => store.dispatch('app/disconnect'));
    
      socket.on('USER_COUNT_UPDATED', () => store.dispatch('user/getCount'));
    };

    // Load Socket layer asynchronously in separate chunk
    import('./socket').then(socket => socket.default(store));
    
    // Setup service worker
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('service-worker.js', { scope: './' });
    }

Server

Service Worker

Cache

Client

WebSocket

Twitter: @blakenewman

Github: @blake-newman

 

Feedback: goo.gl/1dc6i9

Slides: slides.com/blakenewman

Q&A

Made with Slides.com