На этот раз для Vue.js

Все мы знаем, что фреймворки и библиотеки помогают разрабатывать масштабируемые веб-приложения. С React паттерн Flux стал широко использоваться для предсказуемого и удобного поддержания состояния.

Мы видели тонны реализации Flux, самая известная из них - Redux. Его можно использовать с Vue, но состояние не является реактивным по дизайну, поэтому мы потеряли огромную часть преимуществ Vue. Собственная библиотека управления состоянием - Vuex, разработанная создателем Vue. Это отличный инструмент, но, честно говоря, мне не нравится его реализация.

Поэтому я подумал, что, может быть, в качестве побочного проекта я мог бы попробовать создать новый. Я собрал всю свою фантазию и назвал ее: Flue (Flux + Vue).

Одна из основных целей заключалась в том, чтобы предоставить приятную объектно-ориентированную реализацию, которая может поддерживать асинхронные действия из коробки. Кроме того, мы увидим это позже, Actions и Middlewares для Redux полностью совместимы.

Обзор

Прежде всего, вы можете найти это здесь:

Https://github.com/FrancescoSaverioZuppichini/Flue

Как и во всех реализациях Flux, основным компонентом является Store. У него есть состояние, и он может отправлять действия и переключать поведение в зависимости от их типа. Одно из основных отличий от других библиотек заключается в том, что с помощью Flue каждое хранилище можно добавить в глобальное хранилище, чтобы поделиться своим состоянием с системой. Это конкретное хранилище, в котором хранится глобальное состояние, называется SuperStore.

Магазин

Я люблю кодить. Итак, сразу же приведем пример. Это магазин HelloWorld

import { Action, Store } from 'flue-vue'

class HelloWorld extends Store {
  constructor() {
    super()
    this.state.text = ""
  }
  reduce(action) {
    this.reduceMap(action, {
      'HELLO_WORLD': ({text}) => this.state.text = text
    })
  }
  actions(ctx) {
    return {
      helloWorld() {
        ctx.dispatchAction('HELLO_WORLD', {text: 'Hello World'})
      }
    }
  }
}

const helloWorldStore = new HelloWorld()
export default helloWorldStore

В конструкторе мы явно инициализируем состояние. Таким образом, Flue может позже получить его и сделать реактивным с помощью Vue vm. В противном случае необходимо вызвать Vue.set (), чтобы гарантировать его реактивность при добавлении нового свойства.

Функция уменьшения - классический редюсер от Redux. Он принимает действие как параметр и делает что-то в зависимости от его типа. Вы можете заметить, что мы не используем классический переключатель, который вы могли видеть. Класс Store предоставляет функцию под названием reduceMap, которая позволяет разработчикам более кратко описывать старый вариант переключения. Он использует карту, где ключи - это тип действия, а значения - функция. Код можно переписать так:

reduce(action) {
    switch (action.type) {
      case 'HELLO_WORLD':
        this.state.text = action.payload.text
        break;
      default:
}

Например, можно использовать классическую функцию редуктора.

const testReducer = (action) => {
  console.log(action)
}

Действителен и будет регистрировать действия.

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

actions() {
   const dispatch = this.dispatch // we are in the store scope
   return {
      helloWorld() {
        dispatch(new Action('HELLO_WORLD', {text: 'Hello World'}))
      }
    }
  }

Вы всегда можете поместить свои действия в другие файлы и импортировать их.

import {ACTION} from './actions.js`
{  //inside a vue component
   this.$store.dispatch(ACTION)
}

Поставщик действий может быть создан и добавлен позже в SuperStore

const apiActionProvider = (ctx) => {
  return {
    getMeFromAPI() {
      ctx.dispatch(new Action("GET_ME_FROM_API"))
    }
  }
}
SuperStore.addActions(apiActionProvider)

Как и в случае с Redux (на самом деле это тот же код!), Вы можете подписаться на магазин, чтобы следить за обновлениями.

const unsubscribe = SuperStore.subscribe((store)=> {
    console.log(store.state)
})

Для Vue это не требуется, так как состояние является реактивным, и каждое изменение вызывает повторный рендеринг.

SuperStore

Чтобы экосистема работала, каждый магазин или редуктор должны быть добавлены в SuperStore.

import HelloWorldStore from './HelloWorldStore.js
SuperStore.addStore(HelloWorldStore) // add a Store instance
SuperStore.addStore(testReducer) // a function reducer
// or
SuperStore.addStores([HelloWorldStore,testReducer]) // add multiple at ones

SuperStore - это контейнер всех состояний и действий хранилищ. Действия можно вызывать непосредственно из экземпляра Superstore, а также из состояния.

SuperStore.actions.helloWorld()
SuperStore.state

Когда Store добавляется в SuperStore, его состояние передается Vue vm, чтобы получить его и сделать реактивным. Состояние не является неизменным! Поскольку мы полагаемся на реактивность.

В каждом магазине для удобства хранится указатель по имени конструктора.

SuperStore.HelloWorldStore //point to the create HelloWorldStore

Доступ к SuperStore можно получить из любого компонента Vue по ссылке $ store.

Действия

Действия должны содержать поле типа и поле полезной нагрузки. Вы можете использовать класс Action или классический объект

import {Action} from 'flue-vue'
const fooAction = new Action('FOO',{foo:'foo'})
const fooAction = {type:'FOO',payload:{foo:'foo'})

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

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

this.reduceMap(action,{//map},'data') // action.data will be passed to all the functions

Для единообразия я предлагаю создать подкласс класса Action

import {Action} from 'flue-vue'
class MyAction extends Action { ... }

ПО промежуточного слоя

Поддерживается промежуточное ПО Redux. Redux-thunk не нужен, поскольку мы используем диспетчер на основе обещаний, а асинхронные действия поддерживаются изначально.

Добавим классический redux-logger

import logger from 'redux-logger'
import {SuperStore} from 'flue-vue'
SuperStore.applyGlobalMiddleware([logger]) //applied to each store
SuperStore.applyMiddleware(HelloWorld,[logger]) //applied only to a specific store

Мы пробовали также redux-api-middleware, и это прекрасно работает !.

Это возможно из-за функции getState в классе Store, которая возвращает глубокую копию текущего состояния, чтобы предоставить промежуточному программному обеспечению тот же API, что и Redux.

Кредиты

Я черпал вдохновение из Redux (https://github.com/reactjs/redux) и Vuex (https://github.com/vuejs/vuex).

Вывод

Flue не готов к производству, но я без проблем использовал его для своего бакалаврского проекта и других побочных проектов, таких как https://beautifulquotations.herokuapp.com/, и он работает без проблем. Жду ваших отзывов. Спасибо за чтение

P.S

Это мой первый пост, поэтому приветствую любые советы, ребята.

Франческо Саверио