» » Vibe.js — попытка сделать state management без боли

 

Vibe.js — попытка сделать state management без боли

Автор: admin от 30-10-2017, 01:30, посмотрело: 158

Всем йо, хабражители.



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

TL; DR;

Нет ничего привычнее на свете,

Чем писать колесо на велосипеда



Что-то хочется кешировать, что-то обновлять, причем обновлять везде, а не только в локальном компоненте, не хочется перерисовывать весь компонент если поменялся весь Store (shout out to Vuex), а хочется подписываться на то, что используешь (shout out to MobX).

json api спецификации, включая сущности и отношения этих сущностей. Мне спецификация очень понравилась, хоть сначала я не вдуплил. И сразу пример проблемы: у нас список диалогов, мы зашли в диалог — там пользователь онлайн. Вернулись в список диалогов — пользователь оффлайн. Думаю, знакомо юзерам ВК.



В Vuex мне в принципе, все нравится, но там не решена проблема разрозненности сущностей и есть нюансы.



В чем идея Vibe.js



Когда я писал proof of concept, я отталкивался от следующих идей:



1) Я хочу, чтобы сущность была в одном месте. Как в базе данных: 1 id = 1 сущность.

2) Я хочу, чтобы я мог подписываться только на нужные сущности

3) В то же время я хочу комбинировать различные сущности и атрибуты, чтобы не воротить каждый раз кучу подписок на нужные сущности.

4) Я хочу иметь возможность напрямую реактивно обновлять состояние — entity.name = "Vasiliy", но в то же время иметь возможность делать мутации с payload и как-то дебажить мутации, как минимум, например, добавляя к ним текстовый message.



Что получилось



Сейчас в Vibe.js есть следующие концепты:



Model, EntitySubject



Класс, который позволяет определить модель сущности.

Пример использования:



const User = new Model('User', {
    structure: {
        name: types.Attribute,
        bestFriend: types.Reference('User'),
        additionalInfo: {
            age: types.Attribute
        }
    },
    computed: {
        bestFriendsName(){
            return this.bestFriend && this.bestFriend.name || "No best friend :C"
        }
    },
    mutations: {
        setName(newName){
            this.mutate({
                name: newName
            }, "User.setName")
        }
    }
});


Конструктор позволяет описать структуру сущности, вычисляемые значения, а так же мутации.

Структура может быть описана с помощью Атрибутов, Ссылок или вложенных объектов.



Замечу, что имя пользователя можно изменить и напрямую: someUser.name = "New name",

но мутации — более стандартизированный подход.



Сам экземпляр модели практически ничего не может — он только хранит структуры из конструктора.



Если мы хотим добавить сущность:



User.insertEntity(1, {
    name: "Yura",
    bestFriend: 1, // sad when the best friend of yourself is you
    additionalInfo: {
        age: 17
    }
});


Если какие-то значения не будут указаны, будет использоваться дефолтный null. Чтобы теперь пользоваться этой сущностью, вызовем метод observe.



const entity = User.observe(1);
const user = entity.interface;
console.log(user.name) //  "Yura"


Есть нюанс, да? Слишком много чего нужно написать, чтобы работать с сущностью. По строчкам.



1) entity = Экземпляр EntitySubject. Он подписывается на изменения сущности и обновляет interface. На него так же можно подписаться.

2) interface = Реактивный интерфейс для работы с сущностью. У него доступны значения состояния сущности, computed значения и мутации. Нужно заметить, что если сущность еще не существует в EntityStore, то entity.interface будет `null.



EntityStore



Это, как понятно из названия, хранилище сущностей. В нем хранятся все состояния, все observable, модели и содержит методы, которыми пользуются Model или Subject.



const User = new Model('User', {
    structure: {
        name: types.Attribute,
        bestFriend: types.Reference('User'),
        additionalInfo: {
            age: types.Attribute
        }
    },
    computed: {
        bestFriendsName(){
            return this.bestFriend && this.bestFriend.name || "No best friend :C"
        }
    },
    mutations: {
        setName(newName){
            this.mutate({
                name: newName
            }, "User.setName")
        }
    }
});
const Store = new EntityStore([User]);


Мы инициализируем наше хранилище сущностей массивом моделей, чтобы EntityStore мог потом линковать все отношения, ссылки, подписки...



Directory, DirectorySubject



Директории похожи на сущности — синглтоны. У них не индентификаторов, они статичны. Также их не нужно указывать при инициализации EntityStore, потому что сущности не могут на них подписаться. По сути директории — это "каталоги", которые имеют какое-то локальное состояние в виде атрибутов и ссылки на сущности.



К примеру, если мы смотрим книги в интернет магазине, то такая директория могла быть использована:



const Store = new EntityStore([Book]);

const BooksList = new Directory('BooksList', {
    structure: {
        page: types.Attribute,
        searchWord: types.Attribute,
        fetchedBooks: types.Array(types.Reference(Book.name))
    }
}, Store);


Директории так же поддерживают computed значения и мутации, и на них так же можно подписаться.



А что насчет подписок?



Vibe.js организован так, что сущность или директория будет подписана только на те сущности, которые присутствуют на текущий момент в их состоянии. То есть, если мы в директории имеем массив отображаемых элементов и заменяем его, то после изменения состояния она отпишется от сущностей из предыдущего массива и будет подписана только на новые.



Ну и все это протестировано



Или не все. Я люблю писать тесты и хочу написать их побольше, потому что мне кажется, что их недостаточно. В том плане, они не охватывают все, что может пойти не так.



Ссылки



Github репозиторий библиотеки



NPM модуль



Репозиторий с примером Todo list на react



Github pages с генерированной документацией ESDOC



Источник: Хабрахабр

Категория: Программирование

Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Добавление комментария

Имя:*
E-Mail:
Комментарий:
Полужирный Наклонный текст Подчеркнутый текст Зачеркнутый текст | Выравнивание по левому краю По центру Выравнивание по правому краю | Вставка смайликов Выбор цвета | Скрытый текст Вставка цитаты Преобразовать выбранный текст из транслитерации в кириллицу Вставка спойлера
Введите два слова, показанных на изображении: *