Один из вопросов, который задают в Stencil Slack channel, касается возможности создания общего состояния / сервиса в Stencil. Хотя вы можете создать такое поведение, используя только одноэлементный класс ES2015, который имеет один экземпляр и импортирован в ваши компоненты, существует внутренний и еще не задокументированный способ сделать это в Stencil.

Я хочу поблагодарить @insanicae, который первым опубликовал это поведение на канале Stencil Slack.

В этом посте я попытаюсь объяснить, как реализовать общее состояние с помощью свойства Stencil connect.

Примечание. Все данные в этом сообщении могут измениться в будущем, поскольку это недокументированная функция Stencil.

Опора () Connect

В качестве части параметров, которые вы можете передать декоратору Prop (), вы также можете передать объект со свойством connect. Свойство connect получает имя компонента, которое позже будет введено компилятором Stencil.
Давайте посмотрим на пример:

@Prop({connect: 'data-service-injector'}) injector: IDataServiceInjector;

Вы можете заметить, что вместо использования сложного типа для свойства я использую интерфейс под названием IDataServiceInjector. Позже я вернусь к интерфейсу и другим частям схемы подключения.

Давайте сначала попробуем понять, что происходит в Stencil, когда вы создаете свойство connect.

Prop () Connect во время выполнения трафарета

После компиляции компонента Stencil создает небольшую часть библиотеки, которая развертывается со всем компонентом. Вы можете найти эту реализацию как часть созданной сборки компонента в папке www / build / your_component_name. Этот код будет запускать некоторые дополнительные функции Stencil, такие как виртуальные различия DOM и многое другое. Одной из вещей, за которые будет отвечать этот код, будет также обработка функциональных возможностей свойства connect. Когда среда выполнения Stencil видит свойство connect, оно определяет свойство в вашем компоненте, используя следующий код:

defineProperty(instance, memberName, plt.propConnect(memberMeta.ctrlId));

ctrlId - это имя, которое вы указали свойству connect. Функция propConnect будет отвечать за создание proxyController. Вот фрагмент proxyController:

function proxyController(domApi, controllerComponents, ctrlTag) {
    return {
        'create': proxyProp(domApi, controllerComponents, ctrlTag, 'create'),
        'componentOnReady': proxyProp(domApi, controllerComponents, ctrlTag, 'componentOnReady')
    };
}

Как видите, proxyController отвечает за определение двух функций (create и componentOnReady). Позже мы будем использовать эти знания для создания нашего инжектора общего состояния.

Теперь мы знаем, что при использовании свойства connect Stencil создаст прокси-объект, который позволит нам создать (или использовать) объект контроллера. Как мы можем использовать эти знания в наших интересах?

Создание общего состояния в трафарете

В первом фрагменте кода в начале сообщения я показал свойство connect с интерфейсом под названием IDataServiceInjector, так что теперь вы можете понять, как этот интерфейс может выглядеть:

export interface IDataServiceInjector {
    create(): Promise<DataService>
}

В этом интерфейсе будет функция create (и, при необходимости, функция componentOnReady). Функция create вернет тип, который мы ожидаем в качестве общего состояния. В моем примере это объект DataService:

export interface  IDataService {
    getData();
}

export class DataService implements IDataService{
    getData() {
        return ['data1', 'data2'];
    }
}

Для простоты кода я создал простой пример класса DataService. Конечно, есть недостающая часть, которая сделает все волшебство для соединения всех точек, и это реализация DataServiceInjector:

import {Component, Method} from '@stencil/core';
import {DataService} from "./dataService";
import {IDataServiceInjector} from "../common";

const dataService = new DataService();

@Component({
    tag: 'data-service-injector'
})
export class DataServiceInjector implements IDataServiceInjector {
    @Method()
    create(): Promise<DataService> {
        return new Promise(resolve => {
            resolve(dataService);
        });
    }
}

Давайте разберемся, что здесь происходит. Сначала я создал компонент с именем data-service-injector, которое является именем, которое proxyController будет искать и, следовательно, вводить в качестве экземпляра компонента. Затем я реализовал функцию create, которая просто возвращает единственный экземпляр объекта DataService. Это все, что касается инжектора, и теперь мы можем делиться состоянием / функциональностью между нашими компонентами.

Теперь, когда у нас есть реализация data-service-injector, мы можем добавить несколько вещей к компоненту, который использует свойство connect:

@Component({
   ...
})
export class InjectedComponent {  
    @Prop({connect: 'data-service-injector'}) injector: IDataServiceInjector;
    private dataService: DataService;

    componentWillLoad() {
        this.injector.create().then(dataService => {
            this.dataService = dataService;
            console.log(this.dataService.getData());
        });
    }
    ...
}

То, что вы видите в компоненте, - это сначала использование свойства connect. Вы, вероятно, захотите иметь член-компонент (в примере dataService), который позже будет содержать разрешенное состояние / службу. В функции жизненного цикла componentWillLoad вы будете использовать инжектор для создания компонента для инъекции, а затем для передачи его члену на внедренном компоненте. После того, как вы все это сделаете, состояние / служба вставки будут готовы к использованию внутри вашего компонента.

Последнее, что вам следует помнить, - это зарегистрировать компонент data-service-injector в вашем stencil.config.js как часть вашего пакета, иначе ничего не будет работать.

Резюме

В этом посте я объяснил внутренний механизм Stencil, который может помочь вам добавить свойства в ваши компоненты Stencil. Эта функция может измениться в будущем, поэтому используйте ее на свой страх и риск.

Когда свойство connect будет задокументировано в документации по трафарету, я сделаю ссылку на него из сообщения.