JSPM

  • Created
  • Published
  • Downloads 4
  • Score
    100M100P100Q69178F
  • License MIT

Client-side JavaScript framework for Single Page Applications

Package Exports

  • slingjs

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (slingjs) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

Sling update history

Sling

Sling is a client-side JavaScript framework for building Single Page Applications (SPAs). Sling is lightweight, 3.0KB minified, and less than 1.3KB gzipped.

Sling creates and uses a virtual DOM to perform differential updates for fast rendering.

Sling has an automatic change detection mechanism which updates your components for you.

Sling is structured using ECMAScript modules so that Sling code is tree shakeable to ultimately reduce bundle sizes.

Goals

Next Billion Users (NBUs) Empower developers to create SPAs for the NBUs of the web. The NBUs tend to use more affordable and less powerful devices. These devices struggle to achieve a two second Time to Interactive (TTI) with larger component libraries and frameworks.

Practical Familiarity with other JavaScript component libraries. Components are instantiated objects which may be used to control their own state. Components have a simple markup language with a gradual learning curve.

Generalized API as unopinionated as possible. Developers choose the right design patterns for their SPAs—not the library.

Fast High performance. Sling aims to get your SPA to interactive as quickly as possible and aims to keep your SPA as responsive as possible by staying within small code production budgets. With Sling, it should be easier for your SPA to run at 60 frames per second for a native application experience.

Minimal Setup Simply import the Sling functions required by your SPA and Sling is ready to use. No configuration files and no hidden requirements.

Testing

Run npm run devServer after a npm install to start webpack-dev-server.

Then navigate to localhost:8080/todo.html.

Performance (Time)

The V8 JavaScript engine can compile and parse the entire minified distributed of Sling in 4 milliseconds. Sling Core 1.0 is compiled and parsed 10.5 times faster than a minimal Angular 9.0.0 project and 13.5 times faster than a normal Angular 9.0.0 project.

Version Compile Parse Total
Sling Core 1.0 (Routing included) 1ms 3ms 4ms
Sling Core + XHR 1.0 (Routing included) 2ms 4ms 6ms
Angular 9.0.0 Minimal (w/Routing) 27ms 15ms 42ms
Angular 9.0.0 (w/Routing) 30ms 24ms 54ms
Angular 9.0.0 (w/Routing and HttpClient) 30ms 26ms 56ms

Because Sling is so lightweight, it can render thousands more nodes than Angular can in the same time period. Both test applications were served using the http-server NPM package.

Version Number of nodes Time Nodes per ms
Sling Core 1.0 (Routing included) 10,000 27ms 370.3
Angular 9.0.0 Minimal (w/Routing) 10,000 294ms 34.0

Performance (Network)

Using simulated 3G network speeds, Sling Core 1.0 with routing and XHR loads 3.2 times faster than a minimal Angular 9.0.0 project and 3.62 times faster than an Angular 9.0.0 project with HttpClient.

Version Requests Async Time (3G network) Total
Sling Core 1.0 (Routing included) 1 2.06s 2.86KB
Sling Core + XHR 1.0 (Routing included) 1 2.06s 3.80KB
Angular 9.0.0 Minimal (w/Routing) 3 6.59s 226.1KB
Angular 9.0.0 (w/Routing) 3 7.18s 255.1KB
Angular 9.0.0 (w/Routing and HttpClient) 3 7.47s 270.1KB

Add Sling

To add Sling to your project, simply import the Sling function required by your application.

Below is an example of Sling import statements:

import { addRoute } from  './sling/core/sling-router';
import { setState, mount } from  './sling/core/sling';
import { setDetectionStrategy } from  './sling/core/sling-change';
import { Observable } from  '../../sling/reactive/sling-reactive';

Compatibility

Sling uses ES2015/ES6 syntax. Sling does not have any production dependencies.

Components

A component is a JavaScript class with a view() function that returns markup to render.

Components may be nested, but lifecycle hooks for nested components will not be automatically called. This is done for performance reasons and to stay within production code budgets.

Example component:

class HelloWorldComponent {
    constructor() {
    }

    view() {
        return markup('h1', {
            children: [
                textNode('Hello, world!')
            ]
        });
    }
}

Change Detection

Sling supports two change detection strategies: automatic and manual. The default mode is automatic.

Strategy Description
s.CHANGE_STRATEGY_AUTOMATIC Automatically update components after browser events and requests. This is the default setting.
s.CHANGE_STRATEGY_MANUAL Manually update components after browser events and requests.

Automatic change detection performs updates upon the following:

  • All browser events (click, mouseover, keyup, etc.)
  • setTimeout and setInterval
  • XMLHttpRequest and Fetch API requests

Automatic change detection does not perform updates upon the following:

  • Websocket events
  • IndexedDB callbacks

For versions of setTimeout and setInterval that do not trigger automatic change detection, use the following:

  • s.DETACHED_SET_TIMEOUT()
  • s.DETACHED_SET_INTERVAL()

For example:

s.DETACHED_SET_TIMEOUT(() => {
    console.log('Hello, world!');
}, 0);

Lifecycle Hooks

Components may specify up to three lifecycle hooks:

Lifecycle Hook Triggers Change Detection Timing
slOnInit() false Before the component is mounted to the DOM.
slOnDestroy() false Before the component is removed from the DOM.
slAfterInit() true After the component is mounted to the DOM.

Directives

Structural directives modify interactions with the DOM layout.

Directive Type Behavior
slUseExisting Structural Create the element or, if it exists, use the existing element.

Core API

initialize

__ void initialize ( )__

Explicitly initialize Sling.js.

setState

void setState ( newStateObj )

Set a new state object for SPA.

getState

object getState ( )

Get the state object for SPA.

markup

object markup ( tagString, { attrs: {}, children: [] } )

Returns markup object to render. May be mounted to DOM.

Example markup call:

markup('div', {
    attrs: {
        style:  "width:50%;margin:auto;padding:1rem;"
    },
    children: [
        ...Array.from(getState().getNotes(), (note) =>
            markup('div', {
                attrs: {
                    class:  'input-group mb-3 animEnter',
                    style:  'width:100%;'
                },
                children: [
                ]
            })
        )
    ]
});

textNode

string textNode( text )

Create a text node.

Example textNode call:

textNode('Click me!');

mount

element mount ( rootElementId, component, attachDetector = true )

Mounts component on element with ID rootElementId in DOM. Returns root element in DOM where component was added.

Mounted components replace the element with rootElementId to avoid an excessive DOM size. Mounted components must have the same root element ID as the element in the DOM they are attached to.

By default, the Sling change detector is attached for the mounted component. Setting attachDetector to false prevents the change detector from being attached to this component. There are two convenience constants for change detection which are as follows:

Constant Value
s.CHANGE_DETECTOR_DETACHED false
s.CHANGE_DETECTOR_ATTACHED true

update

void update ( rootElementId, component )

Updates the component mounted at element with ID rootElementId.

version

string version( )

Returns Sling version number represented as a string.

Example:

console.log(s.version); // '3.2.0'

Core Router API

initializeRouter

__ void initializeRouter ( )__

Explicitly initialize the router.

addRoute

void addRoute ( hashUrlRegEx, { root: elementId, routeObj: object })

Define a hash-based route that will replace element with ID elementId's content with the specified component on route action.

Below is a list of possible routeObj properties:

Property Description
root The id of the element to replace on route.
component The component to replace root.
authGuard A function that returns true if route action may be taken, otherwise false.
authFail Object with route property to route to on authGuard fail. Also may specify params.

Example route definition:

route('all', { component:  new  TodoListComponent(), root:  'divTodoList' });
route('completed', { component:  new  TodoListCompletedComponent(), root:  'divTodoList' });
route('user/:userId', { component: new UserProfileComponent(), root: 'divUserProfile' });

Example authGuard definition:

route('completed', { component:  new  TodoListCompletedComponent(), root:  'divTodoList', authGuard: function(proposedRoute) { console.log('This will prevent route to \'completed\'.'); return false; }, authFail: { route: 'all', params: { } } });

route

object route ( hashUrl, params = { }, attachDetector = true )

Navigate to the hash-based route according to a previously defined route. May specify route parameters as an object. Returns the component that was routed to.

By default, the Sling change detector is attached for the mounted component. Setting attachDetector to false prevents the change detector from being attached to this component.

Example route call:

s.route('user/5'); // Activates component at root for route 'user/:userId'

getRoute

void getRoute ( )

Get the current hash-based route.

getRouteSegments

string[] getRouteSegments ( )

Returns the current hash-based route's segments or an empty array if there are none.

Example:

console.log(getRouteSegments()); // [ 'user', '5' ]

Using Sling Reactive, route changes may be listened to by using a Sling Observable. Every time the route changes, the subscribed function below will be called.

let routeObservable = Observable(getRouteSegments());
routeObservable.subscribe(function(routeArr) {
    if (routeArr.length > 0) {
        this.primaryRoute = routeArr[0];
    }
    else {
        this.primaryRoute = '';
    }
}.bind(this));

getRouteParams

object getRouteParams ( )

Returns the current route's parameters as an object. Returns { } if there are none.

Core Change Detection API

initializeChangeDetector

__ void initializeChangeDetector ( )__

Explicitly initialize the change detector.

setDetectionStrategy

void setDetectionStrategy ( newDetectionStrategy )

Set the new change detection strategy.

detectChanges

void detectChanges ( )

Trigger automatic change detection immediately.

isDetectorAttached

boolean isDetectorAttached ( eleId )

Returns true if Sling change detector is attached for the given element ID eleId.

detachDetector

void detachDetector ( eleId )

Detach the Sling change detector for the given element ID eleId.

XHR API

slRequest

Promise slRequest ( url, methodString, optionsObject = { } )

Create a XML HTTP Request (XHR) for the specified URL using the specified method, such as GET. Returns a Promise.

Request Option Default Detail
contentType application/json Set Content-Type request header.
body '' Body of the request.
withCredentials false Send cookies to 3rd party domains.
timeout 0 0 is no timeout. Specified in milliseconds.
headers {} Key/value request headers to set.

On success, returns XMLHttpRequest which has data in response property like so:

XMLHttpRequest 
{
    onabort:  null
    onerror:  null
    onload:  null
    onloadend:  null
    onloadstart:  null
    onprogress:  null
    onreadystatechange:  ƒ ()
    ontimeout:  null
    readyState:  4
    response:  "[↵ {↵ "userId": 1,↵ "id": 1,↵ "title": ""
    ...
}

On request fail, returns an object in the following format:

{
    status: 404,
    statusText: ''
}

slRequestWithBody

Promise slRequestWithBody ( url, methodString, bodyObject = { } )

Create a XML HTTP Request (XHR) for the specified URL using the specified method, such as GET, with the specified body object. Returns a Promise.

On success, returns XMLHttpRequest which has data in response property like so:

XMLHttpRequest 
{
    onabort:  null
    onerror:  null
    onload:  null
    onloadend:  null
    onloadstart:  null
    onprogress:  null
    onreadystatechange:  ƒ ()
    ontimeout:  null
    readyState:  4
    response:  "[↵ {↵ "userId": 1,↵ "id": 1,↵ "title": ""
    ...
}

On request fail, returns an object in the following format:

{
    status: 404,
    statusText: ''
}

slGet

Promise slGet ( url, data = { } )

Create a GET XHR request with the specified data which returns a Promise.

On success, returns XMLHttpRequest which has data in response property like so:

XMLHttpRequest 
{
    onabort:  null
    onerror:  null
    onload:  null
    onloadend:  null
    onloadstart:  null
    onprogress:  null
    onreadystatechange:  ƒ ()
    ontimeout:  null
    readyState:  4
    response:  "[↵ {↵ "userId": 1,↵ "id": 1,↵ "title": ""
    ...
}

On request fail, returns an object in the following format:

{
    status: 404,
    statusText: ''
}

slPost

Promise slPost ( url, data = { } )

Create a POST XHR request with the specified data which returns a Promise.

On success, returns XMLHttpRequest which has data in response property like so:

XMLHttpRequest 
{
    onabort:  null
    onerror:  null
    onload:  null
    onloadend:  null
    onloadstart:  null
    onprogress:  null
    onreadystatechange:  ƒ ()
    ontimeout:  null
    readyState:  4
    response:  "[↵ {↵ "userId": 1,↵ "id": 1,↵ "title": ""
    ...
}

On request fail, returns an object in the following format:

{
    status: 404,
    statusText: ''
}

slPut

Promise slPut ( url, data = { } )

Create a PUT XHR request with the specified data which returns a Promise. On success, returns XMLHttpRequest which has data in response property like so:

XMLHttpRequest 
{
    onabort:  null
    onerror:  null
    onload:  null
    onloadend:  null
    onloadstart:  null
    onprogress:  null
    onreadystatechange:  ƒ ()
    ontimeout:  null
    readyState:  4
    response:  "[↵ {↵ "userId": 1,↵ "id": 1,↵ "title": ""
    ...
}

On request fail, returns an object in the following format:

{
    status: 404,
    statusText: ''
}

slPatch

Promise slPatch ( url, data = { } )

Create a PATCH XHR request with the specified data which returns a Promise.

On success, returns XMLHttpRequest which has data in response property like so:

XMLHttpRequest 
{
    onabort:  null
    onerror:  null
    onload:  null
    onloadend:  null
    onloadstart:  null
    onprogress:  null
    onreadystatechange:  ƒ ()
    ontimeout:  null
    readyState:  4
    response:  "[↵ {↵ "userId": 1,↵ "id": 1,↵ "title": ""
    ...
}

On request fail, returns an object in the following format:

{
    status: 404,
    statusText: ''
}

slDelete

Promise slDelete ( url, data = { } )

Create a DELETE XHR request with the specified data which returns a Promise.

On success, returns XMLHttpRequest which has data in response property like so:

XMLHttpRequest 
{
    onabort:  null
    onerror:  null
    onload:  null
    onloadend:  null
    onloadstart:  null
    onprogress:  null
    onreadystatechange:  ƒ ()
    ontimeout:  null
    readyState:  4
    response:  "[↵ {↵ "userId": 1,↵ "id": 1,↵ "title": ""
    ...
}

On request fail, returns an object in the following format:

{
    status: 404,
    statusText: ''
}

Reactive API

Stream

object Stream( )

Returns a Sling stream. A stream is a sequence of values over time and the associated operations which are automatically applied as those values change.

Example stream usage using Sling XHR API:

slGet('https://jsonplaceholder.typicode.com/posts').then(xhrResp => {
    let postArr = JSON.parse(xhrResp.response);
    let postStream = Stream().from(postArr).transform(function(arr) {
        return arr.filter(v => v.userId === 1);
    }).transform(function(arr) {
        return arr.filter(v => v.body.includes('quo'));
    });
});

Equivalent stream usage using preexisting stream object and Sling XHR API:

let postStream2 = Stream();
postStream2.transform(function(arr) {
    return arr.filter(v => v.userId === 1);
}).transform(function(arr) {
    return arr.filter(v => v.body.includes('quo'));
});

slGet('https://jsonplaceholder.typicode.com/posts').then(xhrResp => {
    let postArr = JSON.parse(xhrResp.response);
    postArr.forEach(post => {
        postStream2.push(post);
    });
});

Stream Functions

push

object push( value )

Push a value onto a stream. All transformers automatically called. Transformers are only applied on new data. Returns the stream.

transform

object transform ( function(arrayData) { } )

Add a new transformer to stream. Is automatically applied to all existing and new data. Returns the stream.

subscribe

object subscribe( function(arrayData) { } )

Add a function that is automatically called when the underlying stream data changes. Returns the stream.

clearSubscription

object clearSubscription( functionToClear )

Remove functionToClear from the list of subscribed functions. Returns the stream.

clearSubscriptions

object clearSubscriptions( )

Remove all subscribed functions. Returns the stream.

call

object call ( function(arrayData) { } )

Call a function which operates on the stream's data. Returns the stream.

getData

[ ] getData( )

Returns a copy of stream array data.

clearTransformers

object clearTransformers( )

Clears all transformers acting on the stream. Data will remain in state of last transformation. Returns the stream.

from

object from ( newArray )

Set stream data to newArray and apply all existing transformers. Returns the stream.

Observable

object observable( array )

Returns a Sling observable. An observable is an array which may be listened to.

Example observable usage:

let myArray = [1, 2, 3];
let myObservable = Observable(myArray);
myObservable.subscribe(function(arr) {
    console.log('New length: ' + arr.length);
});

myObservable.getData().push(4);
obs.getData()[myObservable.getData().length] = 5;

Observable Functions

subscribe

void subscribe ( listenerFunction )

Listener function will be automatically called whenever the underlying array data changes. Returns the observable.

clearSubscription

object clearSubscription( functionToClear )

Remove functionToClear from the list of subscribed functions. Returns the observable.

clearSubscriptions

object clearSubscriptions( )

Remove all subscribed functions. Returns the observable.

getData

[ ] getData( )

Get the underlying array data.

BehaviorSubject

object BehaviorSubject( value )

Returns a Sling behavior subject. A behavior subject is a value that emits changes to subscribers.

Example behavior subject usage:

let subject = BehaviorSubject(5);
subject.next(subject.getData() + 1);
let value = subject.getData(); // 6

subject.subscribe(function (value) { console.log('Value: ' + value); });

BehaviorSubject Functions

subscribe

void subscribe ( listenerFunction )

Listener function will be automatically called whenever the subject's value changes. Returns the behavior subject.

clearSubscription

object clearSubscription( functionToClear )

Remove functionToClear from the list of subscribed functions. Returns the behavior subject.

clearSubscriptions

object clearSubscriptions( )

Remove all subscribed functions. Returns the behavior subject.

next

object next( value )

Set the next value of the subject. All subscribers are automatically called. Returns the behavior subject.

getData

primitive|object getData( )

Get the underlying value.