Status: IMPLEMENTED

Champion: Unknown

RFC created: 1970/01/01

Last updated: 2019/11/05

Services

Status

Services are implemented in LWC but are not exposed to platform users, the were part of the GA under the experimental tag. They are about to be removed after the Wire Reform lands.

Summary

Just like the virtual DOM implementation (currently snabbdom) provides hooks for controlling the diffing process of vnodes, we need similar capabilities for component instances. This API is not intended for component authors; it is a high-privilege low-level API for application developers.

This is the first draft of what we call Lightning Web Components Services.

Use Cases

Proposal

Registration

Services must be registered to participate in the lifecycle of components. Services must register during application boot so they may be involved in all stages of all components. Because services are an application concern it is expected the application defines which services are available within it.

Services may not be unregistered. A service may instead choose not to exhibit behavior at any time.

The following code demonstrates the registration of a new service:

Engine.register({
    declared: (Ctor, def) => {}, // removed from first implementation
    constructed: (cmp, data, def, context) => {}, // removed from first implementation
    connected: (cmp, data, def, context) => {},
    disconnected: (cmp, data, def, context) => {},
    rehydrated: (cmp, data, def, context) => {}
});

Note: the order of registration is important: it dictates the order in which the different services' hooks will be invoked.

Hooks

Each of the hooks provided via the registration process will be invoked for each component at the relevant lifecycle stage. All hooks are provided the following parameters (except the declared hook):

The available hooks are:

Note: the hooks are stateless, they return nothing, and they store nothing since they are spliced into functions that will be called directly without context.

Examples

1. Focusable Decorator

Engine.service({
    declared: (Ctor, def) => {
        if (!Ctor.x) {
            return; // exit fast if there is nothing to do
        }
        // Act if the constructor is marked to be decorated with `x`
        // Assert: Ctor must extends Engine.LightningElement.
        const selector = Ctor.x.selector;
        Ctor.prototype.focus = transferableFocusFactory(selector);
        Ctor.prototype.blur = transferableBlurFactory(selector);
        // expose the two new public methods
        def.publicMethods.push('focus', 'blur');
    },
    constructed: (cmp, data, def, context) => {
        // make sure that the custom element has the right tabindex attribute otherwise add it manually (TBD)
    },
});

2. Locker Service

Engine.service({
    declared: (Ctor, def) => {
        if (!CheckIfLockerShouldBeAdded(Ctor)) {
            return;
        }
        // store metadata in def.locker for locker hooks
        // change Ctor.prototype to accomodate locker membrane
    },
    constructed: (Ctor, data, def, context) => {
        if (!def.locker) {
            return;
        }
        ...
    },
    connected: (Ctor, data, def, context) => {
        if (!def.locker) {
            return;
        }
        ...
    },
    disconnected: (Ctor, data, def, context) => {
        if (!def.locker) {
            return;
        }
        ...
    },
});

3. Metrics Service

TBD

undefined